Вебхуки
События ваших диалогов приходят на ваш адрес в тот момент, когда происходят. Это обратное направление к серверному API: там вы зовёте нас, здесь мы зовём вас.
Настраиваются в кабинете: проект → вкладка «Вебхуки». У одного проекта их может быть несколько, каждый со своим набором событий и своим секретом. Ключ API для них не нужен — они ходят к вам, а не вы к нам.
Что приходит
POST на ваш адрес, тело — JSON в UTF-8.
{
"event": "message.created.in",
"occurred_at": "2026-08-23T10:15:30+03:00",
"sequence": 1787472930123456,
"delivery_id": "0198f3c1-...",
"project": { "public_key": "pk_...", "name": "Мой сайт" },
"data": {
"message": {
"id": "0198f3c0-...",
"direction": "in",
"body": "Сколько стоит доставка?",
"author": { "type": "contact", "via": null, "name": null },
"created_at": "2026-08-23T10:15:30+03:00"
},
"conversation": {
"id": "0198f3bf-...",
"status": "open",
"created_at": "..."
},
"contact": {
"id": "0198f3be-...",
"number": 5,
"name": null,
"email": null
}
}
}
Заголовки:
| Заголовок | Что это |
|---|---|
X-Globask-Event |
имя события, то же, что в поле event |
X-Globask-Delivery |
уникальный id доставки — ключ идемпотентности |
X-Globask-Signature |
подпись вида t=<unix>,v1=<hex> |
User-Agent |
Globask-Webhook/1 |
Состав полей message, conversation и contact тот же, что у
чтения, — описание значений direction, author.type и
author.via смотрите там.
События
| Имя | Когда |
|---|---|
message.created.in |
посетитель написал в виджет |
message.created.out |
менеджер или автоответ ответил посетителю |
conversation.reopened |
закрытый диалог ожил: посетитель написал снова |
conversation.closed |
диалог закрыт командой из топика |
widget.opened |
посетитель открыл панель чата |
inbox.failing |
доставка менеджерам сломана: бота выгнали из группы |
ping |
проверочная доставка кнопкой из кабинета |
Про widget.opened важно знать, что именно он считает. Это охват, а не
клики: событие уходит при первом открытии панели на загруженной странице.
Панель живёт в iframe, который повторно не создаётся, поэтому второе и третье
открытие до нас просто не доходят. Если вам нужно «сколько раз кликнули» —
этого события у нас нет, и делать вид, что есть, мы не станем.
Проверка подписи
Подписывается метка времени вместе с телом, а не одно тело. Так записанный запрос нельзя проиграть вам повторно через месяц: подпись без метки оставалась бы валидной вечно.
Строка, от которой считается HMAC-SHA256: <timestamp>.<сырое тело>.
$parts = [];
foreach (explode(',', $request->header('X-Globask-Signature')) as $part) {
[$key, $value] = explode('=', trim($part), 2);
$parts[$key] = $value;
}
$expected = hash_hmac('sha256', $parts['t'].'.'.$request->getContent(), $secret);
// Окно в 5 минут: часы на наших серверах и на вашем могут разойтись.
$fresh = abs(time() - (int) $parts['t']) <= 300;
if (! $fresh || ! hash_equals($expected, $parts['v1'])) {
abort(403);
}
Три вещи, на которых обычно ошибаются:
- Считайте подпись от сырого тела, до разбора JSON. Перекодированный JSON — уже другие байты, и подпись не сойдётся.
- Сравнивайте
hash_equals, а не===: обычное сравнение строк утекает время и позволяет подобрать подпись побайтно. - Проверяйте метку времени. Без неё подпись защищает от подделки, но не от повтора.
Секрет показывается один раз — сразу после создания вебхука. Повторно мы его не покажем: он хранится шифрованным, и ключ, который можно подсмотреть в интерфейсе спустя месяц, защищает хуже, чем кажется. Потеряли — заведите вебхук заново.
Идемпотентность
X-Globask-Delivery не меняется между попытками: если мы отправили событие,
не дождались ответа и повторили, у обеих отправок будет один и тот же id.
Запоминайте его и отбрасывайте повторы — иначе сетевой таймаут на нашей
стороне обернётся вторым лидом в вашей CRM.
Мы этот урок выучили на входе: у нас так же дедуплицируются сообщения виджета и апдейты Telegram.
Порядок
Порядок доставки не гарантирован. События независимы, и медленный ответ на одно не должен задерживать остальные.
Для упорядочивания есть два поля: occurred_at (человекочитаемое время
события) и sequence (то же время в микросекундах, целым числом). Сортируйте
по sequence.
Ретраи и что значит «доставка не проходит»
Отвечайте 2xx — этого достаточно, тело ответа мы не читаем. Любой другой
код и любой сетевой сбой считаются неудачей.
- Повторяем с паузами 5, 15, 60 и 300 секунд.
- Общий бюджет одной доставки — 30 минут. После него событие помечается неудачным и больше не повторяется.
- Три исчерпанных бюджета подряд переводят вебхук в состояние «доставка не проходит»: отправка останавливается совсем.
Остановка — не каприз: мёртвый адрес иначе занимал бы очередь бесконечно. Починив приём, включите вебхук в кабинете. Накопленные за простой события повторно не уйдут — если для вас это важно, вычитайте пропущенное чтением: списки листаются курсором как раз для таких случаев.
Ответьте быстро: у нас 5 секунд на ответ и 3 на соединение. Тяжёлую работу кладите в свою очередь, а нам отвечайте сразу.
Требования к адресу
- Только
https. Подпись защищает от подделки, но не от чтения, а в теле едет переписка ваших посетителей. - Адрес должен быть доступен из интернета. Внутренние диапазоны
(
127.0.0.1,10.*,192.168.*,169.254.*и прочие приватные сети) мы не вызываем — ни напрямую, ни через имя, которое в них резолвится. - Редиректы не выполняются: отвечайте по тому адресу, который вы указали.
- Логин и пароль в адресе не поддерживаются — для авторизации используйте подпись.
Что наружу не уходит никогда
Настоящие имена наших и ваших сотрудников. В поле author.name приходит
публичное имя менеджера, а если оно не задано — «Оператор». Внутренние
числовые идентификаторы тоже не уходят: все сущности адресуются uuid, проект —
своим public_key.