# Notifications and webhooks

> Send deploys, site outages and server alerts to Slack, Discord, Telegram or your own signed webhook.

Under **Settings → Notifications**, add channels that receive events from your organization.

## Channels

| Channel | How to set it up |
| --- | --- |
| Slack | In Slack: Apps → Incoming Webhooks → Add to a channel, then paste the webhook URL. |
| Discord | In Discord: channel settings → Integrations → Webhooks → New webhook → Copy webhook URL. |
| Telegram | Create a bot with @BotFather, add it to your group or channel, and enter the bot token and the chat id. |
| Webhook (signed) | Zefily POSTs signed JSON to your HTTPS endpoint, for your own systems. |

Webhook URLs and bot tokens are kept secret (encrypted).

## Events

Choose which events each channel receives. Alerts reach a chat channel only when its events include them; owners and admins also get them by e-mail and in the notification bell.

| Event | When |
| --- | --- |
| Deploy finished | A deploy or rollback finished, succeeded or failed |
| Site down | An uptime check found a site down |
| Site up | A site that was down answers again |
| Server alert opened | A server went offline, a disk passed your threshold, services failed, memory, CPU or load stayed high, or the agent certificate has a problem |
| Server alert resolved | That alert cleared |

**Send test** sends a test message. **Recent deliveries** shows each delivery's state (**Delivered**, **Retrying**, **Failed**) and the number of attempts; failed deliveries are retried. A channel that keeps failing shows **Failing since …**. **Disable** pauses a channel.

## Verifying signed webhooks

Each request carries a `Zefily-Signature` header: `t=<unix time>,v1=<signature>`, where the signature is the HMAC-SHA256 of `t.body` (the timestamp, a dot, the raw body) with your signing secret. The secret is shown **once** when you create the webhook; **New secret** rotates it.

```php
[$t, $v1] = sscanf($header, 't=%d,v1=%s');
$expected = hash_hmac('sha256', $t.'.'.$body, $secret);
$valid = hash_equals($expected, $v1) && abs(time() - $t) < 300;
```

Compare in constant time and reject old timestamps.

---
Source: https://zefily.com/docs/notifications
