Доставка и эскалация
Push-уведомление доходит мгновенно — но только если телефон под рукой. Доставка (вкладка Доставка в настройках канала) решает вторую задачу: что делать, если сообщение никто не прочитал.
Для каждого канала можно включить дополнительные способы доставки. Работают они как эскалация: сообщение уходит по такому каналу, только если владелец не прочитал его за заданное время.
POST /message ──▶ push всем устройствам (мгновенно) │ └─ раз в минуту: воркер доставки ├─ сообщение прочитано? → ничего не делаем └─ прошло >= таймаута метода? → email / SMS / звонок / Slack / Telegram / webhook / Discord / Teams / Mattermost / PagerDuty / OpsgenieСпособы доставки
Заголовок раздела «Способы доставки»| Метод | Что настраивается | Приходит |
|---|---|---|
| адрес получателя, таймаут | письмо со ссылкой на сообщение и кнопками действий | |
| СМС | номер в формате +79001234567, таймаут | короткий текст с названием канала и заголовком |
| Звонок | номер, таймаут | голосовой звонок с уведомлением |
| Slack | incoming webhook URL, таймаут, мин. приоритет | сообщение в канал Slack |
| Telegram | bot token, chat ID, таймаут, мин. приоритет | сообщение от бота (с inline-кнопками) |
| Webhook | URL, необязательный секрет, таймаут, мин. приоритет | POST с JSON-телом |
| Discord | webhook URL, таймаут, мин. приоритет | карточка (embed) со ссылками действий |
| Teams | URL workflow (Workflows), таймаут, мин. приоритет | Adaptive Card с кнопками действий |
| Mattermost | incoming webhook URL, таймаут, мин. приоритет | сообщение с цветной полосой и ссылками |
| PagerDuty | Integration Key, регион, реакция на прочтение | инцидент (Events API v2) |
| Opsgenie | API key, регион, реакция на прочтение | алерт (Alerts API) |
Таймаут: когда именно сработает
Заголовок раздела «Таймаут: когда именно сработает»У каждого метода свой таймаут доставки (readTimeoutSec):
0— досылка на ближайшем прогоне воркера (то есть примерно в течение минуты);300— если сообщение не прочитано 5 минут;- максимум — 23 часа (
82800).
Отсчёт идёт от момента создания сообщения, а не от момента доставки push.
Сообщение считается прочитанным, когда вы открыли его в админке или мобильном
приложении, либо когда сработал один из endpoint-ов
POST /message/read / POST /application/{id}/message/read,
либо когда вы нажали кнопку ✓ Прочитано в письме, в Telegram или в другой
интеграции. В Android-приложении то же делает кнопка
«Прочитано» прямо в уведомлении и кнопка подтверждения на экране режима
будильника.
Отсечка «до подключения»
Заголовок раздела «Отсечка «до подключения»»В момент включения метода сервер запоминает отметку времени. Сообщения, созданные раньше этого момента, по новому методу не досылаются — иначе включение email-а вечером обернулось бы письмом обо всём, что накопилось за сутки.
Минимальный приоритет
Заголовок раздела «Минимальный приоритет»У всех интеграций (Slack, Telegram, webhook, Discord, Teams, Mattermost, PagerDuty, Opsgenie) есть поле мин. приоритет: сообщения с приоритетом ниже указанного через этот адаптер не уходят. Удобно, чтобы в рабочий чат попадали только важные события, а вся мелочь оставалась в приложении.
У email, SMS и звонка фильтра по приоритету нет — они управляются только переключателем и таймаутом.
Группировка и дайджест
Заголовок раздела «Группировка и дайджест»Внутри одного прогона сообщения группируются по паре «владелец + адрес»:
- email — одно сообщение приходит обычным письмом, несколько — одним письмом-дайджестом со списком;
- СМС — одно СМС на номер: заголовок и краткий список каналов и тем;
- звонок — один звонок на номер, независимо от числа сообщений.
Интеграции (Slack, Telegram, webhook, Discord, Teams, Mattermost, PagerDuty, Opsgenie) не группируются: одно сообщение — одна отправка.
Гарантии доставки
Заголовок раздела «Гарантии доставки»| Метод | Поведение при сбое |
|---|---|
| email и интеграции | Идемпотентны: отметка о доставке ставится после отправки, при сбое следующий прогон повторит |
| СМС, звонок | Отметка ставится до отправки (дубль недопустим), при ошибке снимается и попытка повторяется |
Каждое сообщение уходит по каждому методу ровно один раз: факт доставки фиксируется в журнале и повторно не срабатывает.
Кнопки действий в письме и Telegram
Заголовок раздела «Кнопки действий в письме и Telegram»В письмо и в сообщение Telegram-бота добавляются подписанные ссылки:
- ✓ Прочитано — отмечает сообщение прочитанным и останавливает досылку по остальным методам;
- варианты ответа — если сообщение содержит закрытый вопрос (Ask), кнопки соответствуют вариантам ответа.
Те же действия получают и остальные интеграции: в Slack и Teams — кнопками,
в Discord и Mattermost — ссылками под текстом, в PagerDuty — ссылками инцидента
(links), в Opsgenie — строками описания.
Ссылки подписаны HMAC и действуют ограниченное время (для вопросов — до истечения срока ответа), поэтому авторизация в интерфейсе для них не нужна.
Discord, Microsoft Teams и Mattermost
Заголовок раздела «Discord, Microsoft Teams и Mattermost»Чат-интеграции получают сообщение карточкой с цветом по приоритету (красный —
9–10, оранжевый — 7–8, жёлтый — 4–6, синий — 1–3), заголовком, текстом и
подписью Notifly · канал · P<приоритет>.
| Сервис | Куда слать | Как выглядят действия |
|---|---|---|
| Discord | webhook канала: https://discord.com/api/webhooks/<id>/<token> | markdown-ссылки под текстом |
| Teams | URL workflow «Post to a channel when a webhook request is received» (Workflows / Power Automate) | кнопки Action.OpenUrl Adaptive Card |
| Mattermost | incoming webhook: https://<сервер>/hooks/<key> | markdown-ссылки под текстом |
- Discord. Настройки канала Discord → Интеграции → Вебхуки → Новый
вебхук → Копировать URL. Принимаются только хосты Discord
(
discord.com,discordapp.com). Упоминания@everyone/@hereиз текста сообщения не срабатывают (allowed_mentionsпуст). - Microsoft Teams. В канале Teams: … → Workflows → шаблон Post to a
channel when a webhook request is received → выберите команду и канал →
скопируйте URL. Сообщение приходит Adaptive Card (версия 1.4). Старые
Office 365 connectors (
*.webhook.office.com) Microsoft выводит из эксплуатации — такие URL отклоняются при сохранении с подсказкой перейти на Workflows. - Mattermost. Integrations → Incoming Webhooks → Add Incoming Webhook.
Формат Slack-совместимый (attachment). Массовые упоминания
@channel,@all,@hereиз текста нейтрализуются.
PagerDuty и Opsgenie
Заголовок раздела «PagerDuty и Opsgenie»Incident-интеграции создают инцидент/алерт, а не просто пишут в чат.
| Приоритет Notifly | PagerDuty severity | Opsgenie priority |
|---|---|---|
| 9–10 | critical | P1 |
| 7–8 | error | P2 |
| 4–6 | warning | P3 |
| 1–3 | info | P4 |
| 0 | info | P5 |
- PagerDuty — Events API v2. В сервисе PagerDuty: Integrations → Add
integration → Events API V2, скопируйте Integration Key (32 символа).
Для аккаунтов в EU-регионе выберите регион
eu(events.eu.pagerduty.com).dedup_key=notifly-<id сообщения>: повторная отправка того же сообщения не создаст второй инцидент. Кнопки действий приходят вlinksинцидента, текст — вcustom_details.message. - Opsgenie — Alerts API (
POST /v2/alerts, заголовокAuthorization: GenieKey …). В команде Opsgenie: Integrations → Add integration → API, скопируйте API key; регионus(api.opsgenie.com) илиeu(api.eu.opsgenie.com).alias=notifly-<id сообщения>— Opsgenie дедуплицирует открытые алерты по нему. Ссылки действий — в описании и вdetails.
Реакция на прочтение
Заголовок раздела «Реакция на прочтение»Поле «Когда сообщение прочитано» (onRead) замыкает цикл: когда сообщение
прочитано в Notifly — в приложении, админке или по ссылке ✓ Прочитано прямо
из инцидента, — воркер доставки в течение минуты отправляет в PagerDuty
acknowledge или resolve, в Opsgenie — acknowledge или close (по тому же
dedup_key/alias). По умолчанию ("") инцидент не трогается.
Реакция выполняется не более одного раза на сообщение: неудачная попытка (после встроенных повторов) не повторяется бесконечно. Обратного направления нет: acknowledge в PagerDuty не отмечает сообщение прочитанным в Notifly.
Формат webhook
Заголовок раздела «Формат webhook»Generic-webhook получает POST с JSON:
{ "title": "CPU high", "message": "load average 12.4", "priority": 8, "channel": "Prod", "appId": 12345, "ts": 1782561600000}Если задан секрет, тело подписывается HMAC-SHA256 и подпись передаётся в заголовке:
X-Notifly-Signature: sha256=<hex>Проверка на стороне получателя — в SDK она уже написана.
Python:
import notifly
raw = request.get_data()if not notifly.verify_signature(SECRET, raw, request.headers.get("X-Notifly-Signature")): abort(401)Go — подпакет webhook даёт готовый обработчик, который сам проверяет подпись,
разбирает тело и отвечает нужными кодами:
import "github.com/truboter/notifly.ru/sdk/go/webhook"
http.Handle("/notifly/alerts", webhook.DeliveryHandler(secret, func(ctx context.Context, p *webhook.DeliveryPayload) error { log.Printf("не прочитано в %s: %s", p.ChannelName, p.Title) return nil // вернуть ошибку = попросить Notifly повторить }, &webhook.Options{Dedup: webhook.NewDedup(time.Hour)}))Без SDK это семь строк на любом языке: hmac_sha256(secret, raw_body) в hex,
с префиксом sha256=, сравнить в постоянном времени.
Настройка
Заголовок раздела «Настройка»Через админку
Заголовок раздела «Через админку»Настройки канала → вкладка Доставка. Включите нужный метод переключателем, укажите адрес и таймаут. Кнопка Проверить рядом с методом сохраняет настройки и сразу шлёт туда тестовое уведомление.
Подсказка по Telegram: создайте бота через @BotFather (/newbot) — он выдаст
bot token; узнайте Chat ID (для личных сообщений — у @userinfobot, для группы
это ID вида -100…); обязательно нажмите Start у своего бота или добавьте
его в группу, иначе Telegram запретит отправку.
Через REST API
Заголовок раздела «Через REST API»Настройки доставки — часть объекта канала, поле deliveryConfig в
POST /application и PUT /application/{id}:
curl -X PUT "$NOTIFLY_URL/application/12345" \ -H "Content-Type: application/json" \ -H "X-Notifly-Key: <client-token>" \ -d '{ "name": "Prod", "deliveryConfig": { "email": { "enabled": true, "to": "oncall@example.com", "readTimeoutSec": 300 }, "telegram": { "enabled": true, "botToken": "123:ABC", "chatId": "-1001234567890", "readTimeoutSec": 900, "minPriority": 7 }, "webhook": { "enabled": true, "url": "https://example.com/hook", "secret": "s3cret", "readTimeoutSec": 0, "minPriority": 5 }, "pagerduty": { "enabled": true, "routingKey": "R0123456789abcdef0123456789abcde", "region": "eu", "onRead": "resolve", "readTimeoutSec": 600, "minPriority": 8 } } }'Поля адаптеров:
| Поле | Кто использует | Значение |
|---|---|---|
enabled | все | включён ли метод |
to | email, sms, call | адрес или номер получателя (+79001234567) |
url | slack, webhook, discord, teams, mattermost | URL вебхука / произвольный URL |
botToken, chatId | telegram | параметры бота |
secret | webhook | ключ подписи HMAC-SHA256 (опционально) |
readTimeoutSec | все | таймаут досылки, 0…82800 |
routingKey | pagerduty | Integration Key (Events API v2) |
apiKey | opsgenie | ключ API-интеграции |
region | pagerduty, opsgenie | us (по умолчанию) или eu |
onRead | pagerduty, opsgenie | "", acknowledge или resolve |
minPriority | все интеграции | не слать сообщения с приоритетом ниже |
Тест доставки
Заголовок раздела «Тест доставки»curl -X POST "$NOTIFLY_URL/application/12345/delivery/test" \ -H "Content-Type: application/json" \ -H "X-Notifly-Key: <client-token>" \ -d '{"adapter": "telegram"}'Без тела проверяются все включённые методы канала. Ответ — результат по каждому:
{"results": {"telegram": {"ok": false, "error": "telegram: 403 bot was blocked by the user"}}}Если ни один метод не включён (или указанный адаптер выключен), вернётся 400
no enabled adapter to test. Проверка идёт в обход эскалации: ждать таймаута
не нужно, уведомление уходит сразу.
Досылка не расходует дневную квоту: событие уже списано при создании
сообщения (message). Сколько бы методов доставки ни было включено, счётчик
событий от этого не растёт — см. Квоты и тарифы.
Безопасность
Заголовок раздела «Безопасность»- URL-адреса Slack, webhook, Discord, Teams и Mattermost проверяются защитой от SSRF: приватные, loopback и link-local адреса на момент отправки блокируются.
- Секреты — URL вебхуков Slack, Discord, Teams и Mattermost (в них зашит
токен),
botToken,secret,routingKey,apiKey— в ответах API заменяются маской********; маска при сохранении означает «не менять». Они хранятся в настройках канала и доступны только владельцу канала: получателю делегированного доступа вкладка «Доставка» недоступна. - Управление доставкой требует API-токена владельца канала; app-токен канала сюда не подходит.
Тихие часы и окна обслуживания
Заголовок раздела «Тихие часы и окна обслуживания»Эскалацию отменяют — насовсем, без утреннего «догона» — два механизма:
- Тихие часы владельца канала: пока окно тишины активно, методы для сообщений ниже порога срочности не отправляются; вместо них приходит одна сводка.
- Окна обслуживания: сообщения в каналах охвата не эскалируются вовсе, независимо от приоритета.
Подавленный метод записывается в журнал доставки как «подавлен» и больше не повторяется.
Если досылка не сработала
Заголовок раздела «Если досылка не сработала»Проверьте по порядку:
- Сообщение не прочитано? Прочтение — в админке, в приложении или кнопкой «Прочитано» в уведомлении — отменяет эскалацию.
- Прошёл ли таймаут метода (см. Таймаут). Сообщения, созданные до включения метода, не досылаются.
- Приоритет не ниже минимального (
minPriority) — у интеграций, где он задан (см. Минимальный приоритет). - Не действуют ли тихие часы или окно обслуживания — они подавляют эскалацию насовсем.
- Работает ли сам метод — запустите тест доставки: он отправит уведомление сразу и вернёт ошибку адаптера, если что-то не так. SMS и звонок срабатывают, только если интеграция включена на стороне сервера.
Если не приходит само push-уведомление на устройство, см. «Не приходят уведомления».
См. также
Заголовок раздела «См. также»- Отправка сообщений — приоритеты, заголовки и extras.
- Вопросы в каналах (Ask) — ответы прямо из письма и Telegram.
- Не приходят уведомления — маршрут диагностики push на устройства.
- WebSocket-протокол — мгновенный приём вместо эскалации.
- Публичные статус-страницы — уведомления не для команды, а для ваших пользователей.