Огляд
Вебхук — це push поверх API v1: щойно в організації щось сталося, Smengo сам надсилає POST на вашу адресу. Опитувати /api/v1/* за розкладом не потрібно.
Вебхуки проти опитування API
| Опитування API | Вебхуки | |
|---|---|---|
| Хто ініціює | ви, за розкладом | Smengo, за фактом події |
| Затримка | дорівнює періоду опитування | одиночна зміна — у межах хвилини |
| Вартість простою | запити йдуть навіть коли нічого не змінювалося | запитів немає, поки немає подій |
| Що надходить | поточний стан ресурсу | факт зміни: тип, ідентифікатори, дата |
Тіло події тонке: ідентифікатори й дати, без деталей. Деталі клієнт дочитує своїм API-ключем — у більшості подій для цього є готове посилання related.url. Так вебхук не віддає більше, ніж віддав би API тому самому ключу, а дані в тілі не застарівають під час повтору.
Звична схема інтеграції: вебхук як тригер, API як джерело правди.
Як підключити
- Відкрийте «Налаштування» → «Інтеграції» → блок «Вебхуки». Розділ доступний учаснику з правом «Налаштування організації» (
manage_org). - Натисніть «Додати вебхук», вкажіть адресу приймача, позначте події та (за бажанням) опис.
- Скопіюйте секрет
whsec_…— він показується рівно один раз. Ним підписано кожен запит, див. Перевірку підпису. - Натисніть «Надіслати тестову» — надійде службова подія
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.
Далі
- Події — каталог, склад
data, права. - Перевірка підпису — готовий код на Node і Python.
- Повтори та збої — розклад спроб, авто-вимкнення, журнал.