Огляд

Вебхук — це 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.

Далі

Чи була стаття корисною?