Обзор

Вебхук — это push поверх API v1: как только в организации что-то произошло, Smengo сам отправляет POST на ваш адрес. Опрашивать /api/v1/* по расписанию не нужно.

Вебхуки против опроса API

Опрос API Вебхуки
Кто инициирует вы, по расписанию Smengo, по факту события
Задержка равна периоду опроса одиночное изменение — в пределах минуты
Стоимость простоя запросы уходят даже когда ничего не менялось запросов нет, пока нет событий
Что приходит текущее состояние ресурса факт изменения: тип, идентификаторы, дата

Тело события тонкое: идентификаторы и даты, без деталей. Детали клиент дочитывает своим API-ключом — у большинства событий для этого есть готовая ссылка related.url. Так вебхук не отдаёт больше, чем отдал бы API тому же ключу, а данные в теле не устаревают при ретрае.

Обычная схема интеграции: вебхук как триггер, API как источник правды.

Как подключить

  1. Откройте «Настройки» → «Интеграции» → блок «Вебхуки». Раздел доступен участнику с правом «Настройки организации» (manage_org).
  2. Нажмите «Добавить вебхук», укажите адрес приёмника, отметьте события и (по желанию) описание.
  3. Скопируйте секрет whsec_… — он показывается ровно один раз. Им подписан каждый запрос, см. Проверку подписи.
  4. Нажмите «Отправить тестовое» — придёт служебное событие ping, код ответа вашего сервера покажется сразу в карточке.

Подписаться можно только на те события, данные которых вы сами видите в Smengo, — таблица «событие → право» в Событиях. Недоступные чекбоксы задизейблены с пояснением.

Пошаговая инструкция для нетехнического пользователя — в справке.

Формат доставки

  • Метод — POST, тело — компактный JSON (без отступов и переносов).
  • Content-Type: application/json; charset=utf-8, User-Agent: Smengo-Webhooks/1.0.
  • Только https:// и порт 443. Схема и порт проверяются при сохранении адреса и заново перед каждой доставкой.
  • Редиректы не переходим. Ответ 3xx — это сбой доставки, а не переход по Location.
  • Таймаут — 15 секунд на всю доставку (соединение, TLS, ответ).
  • Успех — строго 2xx. Тело ответа на решение о доставке не влияет: из него мы читаем не больше 512 байт — они сохраняются в журнал для отладки, а остальное не вычитывается вовсе (соединение обрывается на этом месте).
  • Одно событие — один запрос. Батчинга нет.

Заголовки запроса

Заголовок Значение
Smengo-Event-Id UUID события. Один на событие: если на одно событие подписаны два эндпоинта, обоим уйдёт один и тот же Smengo-Event-Id. При ретрае не меняется — это ключ дедупликации.
Smengo-Delivery-Id <uuid доставки>:<номер попытки>, например …:3. Строка доставки одна на эндпоинт, ретрай переиспользует её и меняет только номер.
Smengo-Event-Sequence Целое число, монотонно растёт. Сортируйте события по нему, а не по времени получения.
Smengo-Event-Type schedule.published, schedule_entry.changed, checkin.recorded, swap.decided или служебный ping — маршрутизация без парсинга тела.
Smengo-Timestamp Unix-секунды момента подписи. То же значение, что в t= подписи.
Smengo-Signature t=<unix>,v1=<hex>. В окне ротации секрета — две подписи через пробел: t=…,v1=<hex> v1=<hex>. См. Проверку подписи.

Дополнительно проставляются Content-Length и Accept-Encoding: identity (сжатый ответ нам не нужен — мы читаем из него только первые 512 байт в журнал).

Пример запроса

POST /hooks/smengo HTTP/1.1
Host: example.com
Content-Type: application/json; charset=utf-8
User-Agent: Smengo-Webhooks/1.0
Smengo-Event-Id: 3f1c8b52-9a4d-4f7e-b2c1-8e5d0a6b4c39
Smengo-Delivery-Id: 7b2e4d16-0c58-4a93-9f41-2d6b8c3e5a70:1
Smengo-Event-Sequence: 10427
Smengo-Event-Type: schedule_entry.changed
Smengo-Timestamp: 1785921323
Smengo-Signature: t=1785921323,v1=5a0f…c81b

{"id":"3f1c8b52-9a4d-4f7e-b2c1-8e5d0a6b4c39","type":"schedule_entry.changed","created":"2026-08-01T09:15:23.481Z","sequence":10427,"org_id":"9c1a2e34-5678-4abc-9def-0123456789ab","api_version":"v1","data":{"action":"updated","employee_id":"5e6d9f2a-3b7c-4e2f-9a1d-7c8e2f6b4a10","entry_date":"2026-08-14","entry_id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},"related":{"url":"https://smengo.com/api/v1/schedule-entries?from=2026-08-14&to=2026-08-14"}}

Конверт события

{
  "id": "3f1c8b52-9a4d-4f7e-b2c1-8e5d0a6b4c39",
  "type": "schedule_entry.changed",
  "created": "2026-08-01T09:15:23.481Z",
  "sequence": 10427,
  "org_id": "9c1a2e34-5678-4abc-9def-0123456789ab",
  "api_version": "v1",
  "data": { "action": "updated", "employee_id": "5e6d9f2a-3b7c-4e2f-9a1d-7c8e2f6b4a10", "entry_date": "2026-08-14", "entry_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" },
  "related": { "url": "https://smengo.com/api/v1/schedule-entries?from=2026-08-14&to=2026-08-14" }
}
Поле Тип Описание
id uuid То же, что Smengo-Event-Id.
type string Тип события.
created string Момент рождения события, ISO 8601 UTC с суффиксом Z и миллисекундами.
sequence number То же, что Smengo-Event-Sequence.
org_id uuid Организация-источник. Полезно, если один приёмник обслуживает несколько организаций.
api_version string Версия API, к которой относятся идентификаторы и ссылка — сейчас всегда v1.
data object Состав зависит от типа события — см. События. Ключи внутри data отсортированы по алфавиту.
related.url string Готовый GET-запрос к API v1 за деталями. Поле опционально: у swap.decided, schedule.published и у удаления смены его нет — см. События.

Порядок ключей конверта фиксирован (id, type, created, sequence, org_id, api_version, data, related) и одинаков во всех попытках доставки: подпись считается по тем же байтам, что уходят в тело.

Инстанты внутри data приходят так, как их отдаёт база, — ISO 8601 со смещением (2026-08-01T09:15:23.481+00:00), тогда как created в конверте всегда нормализован до Z. Разбирайте оба любым ISO-8601-парсером, а не сравнением строк.

At-least-once и дедупликация

Доставка гарантирована не менее одного раза: событие может прийти повторно после таймаута, обрыва соединения или восстановления зависшей доставки. Это нормальный режим работы, а не сбой.

Дедуплицируйте по Smengo-Event-Id. Практическая схема: уникальный индекс по этому идентификатору у себя; повторная вставка — не ошибка, а сигнал «уже обработано, отвечаем 200».

Порядок не гарантируется

События могут прийти в любом порядке: одна транзакция порождает пачку событий, они уходят параллельно, а ретрай отодвигает конкретное событие на часы.

Для восстановления порядка используйте Smengo-Event-Sequence — целое, монотонно растущее в пределах организации. Поле created для этого не годится: у всех событий одной транзакции оно совпадает с точностью до микросекунд.

Требования к приёмнику

  • Публичный https-адрес на порту 443. Адреса в приватных диапазонах (10/8, 127/8, 169.254/16, 192.168/16, ULA IPv6 и т. д.) отклоняются — доставка на них не уйдёт даже при валидном DNS-имени.
  • Не фильтруйте по IP. Фиксированного списка адресов, с которых уходят доставки, мы не публикуем и не обещаем: подлинность запроса подтверждает подпись, а не адрес источника.
  • Без user:pass@ в URL и без фрагмента #…. Длина адреса — до 2000 символов.
  • Отвечайте 2xx быстро, обрабатывайте асинхронно. Проверьте подпись, положите событие в очередь и сразу ответьте 200. Если бизнес-логика упирается в 15-секундный таймаут, доставка считается провалившейся и придёт снова.
  • Будьте идемпотентны — см. дедупликацию выше.
  • Не полагайтесь на редиректы. Адрес должен обслуживаться конечным сервером, 301/302 на новый путь мы не пройдём.
  • Отвечайте 410 Gone, только если адрес мёртв окончательно: это единственный код, который сразу отключает вебхук.

Тестовое событие ping

Кнопка «Отправить тестовое» шлёт служебное событие:

{
  "id": "…",
  "type": "ping",
  "created": "2026-08-01T09:15:23.481Z",
  "sequence": 10428,
  "org_id": "9c1a2e34-5678-4abc-9def-0123456789ab",
  "api_version": "v1",
  "data": { "test": true }
}

Оно подписано теми же секретами и несёт те же заголовки, что боевая доставка, — проверять его нужно тем же кодом. На ping нельзя подписаться: он шлётся адресно на конкретный эндпоинт, в том числе поставленный на паузу, и не ретраится (ровно одна попытка).

Ограничения v1

  • Управление эндпоинтами — только через интерфейс: REST-эндпоинтов /api/v1/webhook-* нет, API v1 остаётся read-only.
  • Подписка действует на всю организацию: фильтра по отделу нет.
  • Ручного повтора доставки из интерфейса нет — пропущенное дочитывается через API.

Дальше

Была ли статья полезна?