PD Документация ← На главную

Вебхуки

PushData может отправлять POST на ваш сервер при каждом событии с уведомлением или прогоном сценария: провайдер принял push, доставка не удалась, получатель открыл или кликнул, прогон завершился. Вебхуки настраиваются на приложение в Приложения → Вебхуки (или мутациями createHook / updateHook / deleteHook).


События

СобытиеКогдаpayload
NOTIFICATION_SENTПровайдер принял доставку одному получателюnotificationId, deviceId или channelId, messageId
NOTIFICATION_FAILEDДоставка одному получателю окончательно не удалась (после повторов транзиентных ошибок)notificationId, deviceId или channelId, error
NOTIFICATION_DELIVEREDКлиент подтвердил доставку (POST /api/v1/track, service worker, мобильные SDK); только pushnotificationId, deliveryLogId, deliveredAt
NOTIFICATION_OPENEDЗагружен пиксель открытия письма или клиент сообщил об открытииnotificationId, deliveryLogId, openedAt
NOTIFICATION_CLICKEDПереход по отслеживаемой ссылке или клиент сообщил о кликеnotificationId, deliveryLogId, clickedAt, destination
WORKFLOW_COMPLETEDПрогон сценария выполнил последний шагexecutionId, workflowId, status
WORKFLOW_FILTEREDШаг FILTER остановил прогонexecutionId, workflowId, status, stepOrder
WORKFLOW_FAILEDШаг упал окончательно (ошибка провайдера, квота, выключенное приложение)executionId, workflowId, error
WORKFLOW_CANCELLEDПрогон отменён (оператором, архивированием или удалением сценария)executionId, workflowId, reason

Вебхук подписывается на список событий; пустой список означает все события. Неизвестные имена событий отклоняются при создании (400). Одно событие доставляется каждому активному вебхуку приложения, подписанному на него: рассылка на 10 000 устройств с двумя вебхуками на NOTIFICATION_SENT — это 20 000 POST-запросов.


Запрос

http
POST https://your-server.example/pushdata
Content-Type: application/json
X-PushData-Signature: t=1757664000, v1=5f1c…e3a9
json
{
  "id": "019c9a99-8aad-77b4-b2b2-719f3f8e0610",
  "event": "NOTIFICATION_SENT",
  "appId": "019c9359-fe1d-73bc-90fd-594d284d24f3",
  "timestamp": "2026-09-12T08:00:00.000Z",
  "payload": { "notificationId": "…", "deviceId": "…", "messageId": "…" }
}
  • id идентифицирует событие и не меняется между повторами — используйте его для дедупликации.
  • timestamp — момент события, а не отправки этой попытки.
  • Тело ответа не читается; учитывается только статус.

Отвечайте 2xx в течение 10 секунд. Всё остальное (4xx, 5xx, таймаут, отказ соединения, редирект) — неудачная попытка.


Подпись

Если у вебхука задан секрет, каждый запрос подписывается по схеме Stripe: X-PushData-Signature: t=<unix-секунды>, v1=<hex HMAC-SHA256>, где ключ HMAC — секрет, а подписываемый текст — <t>.<сырое тело запроса>. Метка времени свежая на каждой попытке: сверяйте её со своими часами (принято окно в 5 минут) и сравнивайте подпись за постоянное время:

ts
import { Buffer } from 'node:buffer'
import { createHmac, timingSafeEqual } from 'node:crypto'

export function verifyPushDataWebhook(rawBody: string, header: string, secret: string, toleranceSeconds = 300): boolean {
  const parts = Object.fromEntries(header.split(',').map(p => p.trim().split('=')))
  const t = Number(parts.t)
  if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceSeconds)
    return false
  const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
  const given = String(parts.v1 ?? '')
  return given.length === expected.length && timingSafeEqual(Buffer.from(given), Buffer.from(expected))
}

Проверяйте по сырым байтам тела, до разбора JSON и любой пересериализации. Заголовок один — X-PushData-Signature; получатель, написанный под имя до ребрендинга (X-Nitroping-Signature), должен читать новое имя.


Повторы и автоматический выключатель

  • Неудачная попытка повторяется с экспоненциальной задержкой: 6 попыток, начиная с 30 секунд, всего около 15 минут. Каждая попытка несёт свежую метку времени в подписи.
  • Вебхук, у которого 50 доставок подряд не удались, выключается; дашборд показывает причину рядом со статусом, в API её несёт Hook.disabledReason. Успешная доставка сбрасывает счётчик. Почините endpoint и нажмите Включить (или updateHook(isActive: true)) — это же сбрасывает выключатель.
  • Доставки, исчерпавшие повторы, записываются в Dead letters для разбора (переиграть оттуда можно только отправки уведомлений; вебхук просто уйдёт со следующим событием).
  • У приложения может быть не более 20 вебхуков.

Ограничения

  • URL должен быть публичным адресом http(s): loopback, приватные и link-local диапазоны отклоняются при создании и ещё раз при доставке (адрес резолвится и закрепляется, редиректы не выполняются).
  • Имя до 120 символов, секрет до 256; секрет хранится зашифрованным и после создания больше не показывается.
  • Порядок между событиями не гарантируется (каждая доставка — отдельная джоба); опирайтесь на timestamp и собственное состояние.