Вебхуки
События на ваш адрес: список, заголовки, проверка подписи HMAC, повторы и защита от петли.
На этой странице
События #
| Событие | Когда | В data |
|---|---|---|
| conversation.started | посетитель завёл обращение | conversation |
| message.created | новое сообщение в обе стороны | conversation, message |
| conversation.closed | обращение закрыто | conversation, reason |
| visitor.identified | сайт передал сведения о посетителе | conversation |
| webhook.test | проверка из кабинета, подписаться нельзя | — |
Имена окончательные. Новое событие добавляется рядом, старое не переименовывается никогда: по именам написаны обработчики на вашей стороне.
Как выглядит запрос #
Объекты conversation и message — те же, что отдаёт API: один разбор годится и там и там.
POST /ваш-обработчик HTTP/1.1
Content-Type: application/json
X-WidgetChat-Event: message.created
X-WidgetChat-Delivery: message.created:8f1c…
X-WidgetChat-Attempt: 1
X-WidgetChat-Timestamp: 1755878400
X-WidgetChat-Signature: sha256=6b2f…
{"id": "message.created:8f1c…",
"event": "message.created",
"createdAt": "2026-08-22T12:00:00Z",
"workspaceKey": "example-ru",
"data": {"conversation": { … }, "message": { … }}}Проверка подписи #
Подписывается строка timestamp + "." + body, а не одно тело: подпись голого тела можно переслать повторно хоть через сутки, и обработчик не отличил бы повтор от свежего события.
Проверяйте три вещи: подпись совпала при сравнении за постоянное время; метка времени отличается от ваших часов не больше чем на пять минут; номер из X-WidgetChat-Delivery ещё не встречался.
// Go
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(r.Header.Get("X-WidgetChat-Timestamp") + "." + string(body)))
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
ok := hmac.Equal([]byte(expected), []byte(r.Header.Get("X-WidgetChat-Signature")))# PHP
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, $secret);
$ok = hash_equals($expected, $signature);Ответы и повторы #
Ждём ответ 20 секунд. Отвечайте сразу, а работу делайте после — долгий обработчик считается неудачей. Все попытки видны в журнале доставок в кабинете: событие, номер попытки, код ответа, текст ошибки.
| Ваш ответ | Что делаем |
|---|---|
| 2xx | считаем принятым, больше не шлём |
| 4xx | повтора не будет: получатель не изменит мнения |
| 5xx, таймаут, обрыв | 3 попытки, пауза 5 секунд и дальше вдвое |
Защита от петли #
На ответ, отправленный вашей же программой, тоже придёт message.created. Смотрите поле origin внутри message: api — это вы сами, telegram — оператор из группы, bitrix24, amocrm, retailcrm — менеджер из CRM, widget — посетитель.
Требования к адресу #
Только https и только внешний адрес: событие несёт переписку клиента. Адрес во внутренней сети принят не будет.