Настройка вебхуков в Нотифлоу: события, HMAC, ретраи
Вебхук — это HTTP-запрос, который Нотифлоу отправляет на ваш URL, когда в кабинете происходит событие. Чат начался, в виджет пришел клик, оператор закрыл диалог — каждое событие подписывается HMAC-SHA256 и при сбое повторяется до трех раз с возрастающей задержкой. Подпись защищает от подделки, идемпотентность — на стороне получателя по дедупу запроса.
Что такое вебхук и зачем он нужен?
В отличие от REST API, где вы периодически опрашиваете «есть ли что новое», вебхук работает наоборот: Нотифлоу сам пишет на ваш сервер, когда есть что сообщить. Это экономит запросы, снижает задержку доставки до секунд и позволяет выстраивать триггерные сценарии.
Типичный сценарий: посетитель открыл чат → Нотифлоу отправляет вебхук chat_new → ваш бэкенд создает сделку в CRM и пишет в Slack команды. Без вебхука пришлось бы крутить cron-опрос API.
Какие события Нотифлоу можно отправлять по webhook?
Доступные события (выбираются в кабинете в «Интеграции → Вебхуки»):
widget_show— виджет показан посетителюwidget_click— клик по виджетуwidget_submit— отправка формы из виджета (лид, NPS-ответ, оценка)chat_new— посетитель открыл чатchat_message— пришло сообщение в чатchat_closed— диалог закрыт оператором или клиентомcontact_identified— пользователь идентифицирован через JS SDKcontact.score_tier_changed— изменился тир скоринга контактаai_auto_reply— AI-ассистент отправил автоответai_summary_generated— AI-ассистент собрал саммари диалогаemail_received,email_sent— события email-каналаcrm_lead_created,crm_deal_updated— события связки с CRM
Имена событий жестко зафиксированы в WebhookService::EVENTS — кабинет не позволит сохранить вебхук с неизвестным типом.
Как создать вебхук в кабинете Нотифлоу?
Откройте cp.notiflow.ru → «Интеграции → Вебхуки» → «Создать вебхук».
Заполните три поля:
- URL —
https://api.example.com/webhooks/notiflow(только HTTPS, HTTP не принимается) - События — выберите одно или несколько из списка выше
- Секрет подписи — Нотифлоу сгенерирует автоматически или введите свой (32+ символа). Этот секрет нужен для проверки HMAC
После сохранения проверьте связь — отправьте в виджет тестовое сообщение или начните чат, событие сразу уйдет на ваш URL.
Как проверить, что вебхук доходит?
Для отладки используйте webhook.site или RequestBin — оба показывают входящие запросы в браузере без своего бэкенда.
- Откройте webhook.site, скопируйте уникальный URL
- Вставьте его в поле URL вебхука в Нотифлоу
- Триггерните событие в кабинете (например, начните тестовый чат)
- На webhook.site появится запрос с заголовками, телом и подписью
Когда убедились, что доставка работает — замените URL на свой боевой эндпоинт.
Подпись запросов и проверка HMAC
Нотифлоу подписывает каждый запрос HMAC-SHA256 от тела запроса с вашим секретом. В заголовок кладется hex-подпись:
X-Нотифлоу-Signature: a3f5b8c1d2e4... (sha256 hex)
Content-Type: application/json
Проверка на Node.js:
const crypto = require('crypto');
function verifyWebhook(rawBody, signatureHeader, secret) {
if (!signatureHeader) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(expected)
);
}
На PHP:
function verifyWebhook(string $rawBody, string $signatureHeader, string $secret): bool {
if ($signatureHeader === '') return false;
$expected = hash_hmac('sha256', $rawBody, $secret);
return hash_equals($expected, $signatureHeader);
}
Используйте timingSafeEqual / hash_equals, а не === — иначе уязвимы к timing attack. Подпись считается от raw body, поэтому читайте тело запроса до парсинга JSON.
Ретраи и идемпотентность: как не дублировать события?
Если ваш сервер ответил не 2xx или не ответил за разумное время, Нотифлоу повторит доставку. Расписание простое:
| Попытка | Когда уходит |
|---|---|
| 1 | сразу |
| 2 | через 60 секунд после неудачи |
| 3 | еще через 5 минут |
После третьей неудачи вебхук помечается как «failed». Дальше повторов нет — поднимайте сервис и при необходимости пересылайте события вручную из журнала.
Готового event_id в payload нет — идемпотентность держите на своей стороне. Используйте детерминированный ключ: event + timestamp + содержимое data (например, ID контакта или диалога). Сохраняйте хэш этого ключа в БД и проверяйте перед обработкой:
app.post('/webhooks/notiflow', async (req, res) => {
const { event, timestamp, data } = req.body;
const dedupKey = crypto
.createHash('sha256')
.update(`${event}:${timestamp}:${JSON.stringify(data)}`)
.digest('hex');
const exists = await db.query('SELECT 1 FROM webhook_events WHERE dedup_key = $1', [dedupKey]);
if (exists.rows.length > 0) {
return res.status(200).send('duplicate');
}
await db.query('INSERT INTO webhook_events(dedup_key, received_at) VALUES($1, NOW())', [dedupKey]);
await processEvent(req.body);
res.status(200).send('ok');
});
Ответ 200 даже на дубль обязателен — иначе Нотифлоу продолжит ретраить.
Сценарии: связка с amoCRM, Bitrix24, Slack, n8n
crm_lead_created → проброс события в свою аналитику. Прямая связка с amoCRM настраивается через коннектор без вебхуков, описана в интеграции с amoCRM. Вебхук нужен, если хотите кастомную логику: например, отдельную ветку для лидов с UTM source=ads.
widget_submit для NPS-формы со score < 7 → задача в Trello / Linear. Менеджер связывается с недовольным клиентом в течение часа.
chat_new → запись в n8n / Make. Обогащение лида данными из Clearbit, передача в HubSpot, уведомление в Telegram дежурного.
chat_closed → запись в Slack команды поддержки с длительностью диалога и итогом.
{
"event": "widget_submit",
"timestamp": "2026-04-30T14:22:11+00:00",
"data": {
"widget_type": "nps",
"contact": { "id": "ctc_42", "email": "ivan@example.com" },
"score": 4,
"comment": "Не нашел, как удалить аккаунт"
}
}
Что делать, если вебхук не приходит?
Сначала смотрите журнал в кабинете: «Интеграции → Вебхуки → ваш вебхук → История». Каждая попытка с кодом ответа и телом. Если код 401 — у вас ошибка проверки подписи. Если timeout — сервер не отвечает достаточно быстро, оптимизируйте обработчик: положите задачу в очередь и отвечайте 200 сразу. Если 5xx — баг в обработчике.
Если вебхук вообще не отправлен — проверьте, не отключен ли он, и подходит ли событие под выбранный список. Бывает, что в подписке выбраны только chat_new и chat_closed, а вы ждете widget_submit — в этом случае событие просто не матчится.
Часто задаваемые вопросы
Чем webhook отличается от API?
API — это «вы спрашиваете», webhook — «сервер сам сообщает». API подходит для разовых запросов и пагинации, webhook — для реактивных триггеров. На практике используют оба: webhook ловит событие, по полученному ID API забирает полные данные.
Как часто Нотифлоу повторяет неуспешные доставки?
До трех попыток: первая — сразу, вторая — через 60 секунд после неудачи, третья — еще через 5 минут. После третьей неудачи вебхук помечается как failed. Восстановить поток можно вручную из журнала.
Как проверить подпись webhook?
Возьмите заголовок X-Нотифлоу-Signature, посчитайте hash_hmac('sha256', rawBody, secret) и сравните через timingSafeEqual или hash_equals. Подпись — hex от raw тела запроса, никаких префиксов и timestamp.
Можно ли настроить несколько URL для одного события?
Да. Создайте два отдельных вебхука с одинаковым событием и разными URL — каждый получит копию запроса. Полезно, если одну систему пишет CRM, другую — аналитический пайплайн.
Какие события доступны?
widget_show, widget_click, widget_submit, chat_new, chat_message, chat_closed, contact_identified, contact.score_tier_changed, ai_auto_reply, ai_summary_generated, email_received, email_sent, crm_lead_created, crm_deal_updated. Список фиксированный — события за пределами этого набора не отправляются.
Готовы автоматизировать поток событий? Создайте аккаунт и подключите первый вебхук в «Интеграции → Вебхуки». Дальше — гайды по API-ключам и передаче атрибутов пользователя. Полный обзор API — на странице платформы.