Як надійно отримувати вебхуки HubSpot
HubSpot надсилає на джерело Railhook пакети подій CRM. Джерело перевіряє підпис версії 3, зберігає кожен пакет таким, яким той надійшов, і пересилає його у ваш сервіс із повторними спробами.
Чому вебхукам HubSpot потрібен буфер
Section titled “Чому вебхукам HubSpot потрібен буфер”HubSpot повторює невдалу доставку до 10 разів протягом доби, з експоненційною затримкою й випадковим розкидом, щоб після масового збою всі повтори не пішли одночасно. Він повторює після збою з’єднання, тайм-ауту й більшості помилкових статусів, але не після 400, 403 і 404, які вважає остаточними. (HubSpot: Updated webhook retry logic)
Маршрут, якого на мить немає під час деплою, відповідає 404, і HubSpot списує подію з першої ж спроби. Коли попереду стоїть Railhook:
- HubSpot завжди звертається до адреси, яка існує й відповідає одразу, тож
404від вашого застосунку ніколи не стає відповіддю для HubSpot. - Ваш сервіс отримує до 5 спроб на кожне пересилання з інтервалами від 1 хвилини до 1 години, а те, що так і не вдалося доставити, потрапляє в «Недоставлені пересилання».
- Будь-що збережене можна відтворити, коли ви полагодите маршрут.
Підключення HubSpot
Section titled “Підключення HubSpot”Знадобляться проєкт Railhook і API-ключ. Ті самі кроки є в панелі керування, у розділі «Вхідні».
export RAILHOOK_URL=https://railhook.io # or your own instanceexport RAILHOOK_API_KEY=...export PROJECT_ID=...-
Створіть джерело Railhook, щоб отримати адресу, на яку HubSpot має надсилати запити:
Terminal window curl -X POST "$RAILHOOK_URL/api/v1/projects/$PROJECT_ID/incoming-sources" \-H "X-API-Key: $RAILHOOK_API_KEY" \-H "Content-Type: application/json" \-d '{"name":"HubSpot","providerType":"HUBSPOT","verificationMode":"NONE"}'Збережіть
idтаingressUrlз відповіді. -
Спрямуйте вебхуки застосунку на неї. У застосунку, зібраному через projects, вебхуки задаються конфігурацією в коді, а не формою: додайте каталог
webhooksпідsrc/app/з файлом*-hsmeta.json, деsettings.targetUrlдорівнюєingressUrl, і перелічіть потрібне вsubscriptions. Адреси без HTTPS відхиляються. (HubSpot: Configure webhooks) -
Знайдіть client secret. HubSpot підписує саме ним, а не окремим ключем для вебхука. У вашому акаунті розробника він лежить у Development → Projects → ваш проєкт, на вкладці Auth застосунку, у секції Client credentials. (HubSpot: Manage apps in HubSpot)
-
Передайте секрет Railhook і ввімкніть перевірку:
Terminal window curl -X PUT "$RAILHOOK_URL/api/v1/projects/$PROJECT_ID/incoming-sources/$SOURCE_ID" \-H "X-API-Key: $RAILHOOK_API_KEY" \-H "Content-Type: application/json" \-d '{"name":"HubSpot","verificationMode":"PROVIDER","hmacSecret":"'"$HUBSPOT_CLIENT_SECRET"'"}' -
Додайте призначення, адресу вашого сервісу:
Terminal window curl -X POST "$RAILHOOK_URL/api/v1/projects/$PROJECT_ID/incoming-sources/$SOURCE_ID/destinations" \-H "X-API-Key: $RAILHOOK_API_KEY" \-H "Content-Type: application/json" \-d '{"url":"https://api.example.com/webhooks/hubspot","authType":"BEARER","authConfig":"{\"token\":\"...\"}","enabled":true}' -
Зробіть те, на що підписалися (створіть контакт, змініть властивість) у порталі, де встановлено застосунок. Подія з’явиться в розділі «Вхідні → Отримані», з одним пересиланням на кожне призначення.
Як перевіряється підпис
Section titled “Як перевіряється підпис”HubSpot v3 надсилає X-HubSpot-Signature-v3: HMAC-SHA256 у Base64 від методу запиту, повної адреси, необробленого тіла й мітки часу, з’єднаних саме в такому порядку й без роздільників, з ключем client secret. Заголовок X-HubSpot-Request-Timestamp несе цю мітку в мілісекундах, і HubSpot радить відхиляти все, старше за п’ять хвилин. Railhook так і робить. (HubSpot: Validate requests)
Запит, який не пройшов перевірку або прийшов із запізненням понад п’ять хвилин, отримує 401 і не зберігається.
Дедуплікуйте за подією, а не за запитом
Section titled “Дедуплікуйте за подією, а не за запитом”Запит HubSpot є JSON-масивом подій, і кожна має власний eventId та attemptNumber, який зростає з кожною повторною спробою. Ідентифікатора самого пакета немає, тож для джерела HubSpot Railhook не записує providerEventId і запити не дедуплікує: ключ за першою подією пакета втратив би решту, якби HubSpot під час повторної спроби перезібрав пакет інакше.
Дедуплікуйте у власному обробнику за eventId разом із portalId. Підписане п’ятихвилинне вікно й кеш повторів зупиняють перехоплений запит на порозі Railhook, але ідемпотентного обробника вони не заміняють.
Що отримує ваш сервіс
Section titled “Що отримує ваш сервіс”Тіло точно таким, яким його надіслав HubSpot, байт у байт: масив, а не одна подія на запит. Заголовки з підписом не пересилаються: ваш сервіс автентифікує Railhook обліковими даними призначення, а підпис v3 покриває ingress-адресу, тож із вашою він не зійшовся б. Див. Призначення.
Локальна розробка з CLI
Section titled “Локальна розробка з CLI”-
Встановіть CLI й увійдіть, як описано в розділі CLI, а потім відкрийте тунель до порту застосунку:
Terminal window railhook listen 3000 -
Додайте джерелу друге призначення з надрукованою публічною адресою й вашим маршрутом у кінці, наприклад
https://<host>/tunnel/<slug>/webhooks/hubspot. Вона веде наhttp://localhost:3000/webhooks/hubspot. -
Змініть контакт у тестовому порталі, і пакет прийде у ваш локальний застосунок. Вимкніть це призначення, коли закриєте тунель.
Відтворення після збою
Section titled “Відтворення після збою”Кожен відтворений пакет іде на всі призначення джерела як нове пересилання:
curl -X POST "$RAILHOOK_URL/api/v1/projects/$PROJECT_ID/incoming-events/bulk-replay" \ -H "X-API-Key: $RAILHOOK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"sourceId":"'"$SOURCE_ID"'","from":"2026-09-18T10:00:00Z","to":"2026-09-18T14:00:00Z","verified":true}'Відтворити можна все, що ще зберігається: 7 днів у Railhook Cloud і скільки налаштуєте на власному сервері. Подія, на яку ваш застосунок відповів 404, не дійшла до логіки повторів HubSpot, бо відповів Railhook, тож пакет досі можна відтворити. Див. Відтворення.