Перейти до вмісту
На сайт

Як надійно отримувати вебхуки 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 години, а те, що так і не вдалося доставити, потрапляє в «Недоставлені пересилання».
  • Будь-що збережене можна відтворити, коли ви полагодите маршрут.

Знадобляться проєкт Railhook і API-ключ. Ті самі кроки є в панелі керування, у розділі «Вхідні».

Terminal window
export RAILHOOK_URL=https://railhook.io # or your own instance
export RAILHOOK_API_KEY=...
export PROJECT_ID=...
  1. Створіть джерело 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 з відповіді.

  2. Спрямуйте вебхуки застосунку на неї. У застосунку, зібраному через projects, вебхуки задаються конфігурацією в коді, а не формою: додайте каталог webhooks під src/app/ з файлом *-hsmeta.json, де settings.targetUrl дорівнює ingressUrl, і перелічіть потрібне в subscriptions. Адреси без HTTPS відхиляються. (HubSpot: Configure webhooks)

  3. Знайдіть client secret. HubSpot підписує саме ним, а не окремим ключем для вебхука. У вашому акаунті розробника він лежить у Development → Projects → ваш проєкт, на вкладці Auth застосунку, у секції Client credentials. (HubSpot: Manage apps in HubSpot)

  4. Передайте секрет 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"'"}'
  5. Додайте призначення, адресу вашого сервісу:

    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}'
  6. Зробіть те, на що підписалися (створіть контакт, змініть властивість) у порталі, де встановлено застосунок. Подія з’явиться в розділі «Вхідні → Отримані», з одним пересиланням на кожне призначення.

Як перевіряється підпис

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, але ідемпотентного обробника вони не заміняють.

Тіло точно таким, яким його надіслав HubSpot, байт у байт: масив, а не одна подія на запит. Заголовки з підписом не пересилаються: ваш сервіс автентифікує Railhook обліковими даними призначення, а підпис v3 покриває ingress-адресу, тож із вашою він не зійшовся б. Див. Призначення.

  1. Встановіть CLI й увійдіть, як описано в розділі CLI, а потім відкрийте тунель до порту застосунку:

    Terminal window
    railhook listen 3000
  2. Додайте джерелу друге призначення з надрукованою публічною адресою й вашим маршрутом у кінці, наприклад https://<host>/tunnel/<slug>/webhooks/hubspot. Вона веде на http://localhost:3000/webhooks/hubspot.

  3. Змініть контакт у тестовому порталі, і пакет прийде у ваш локальний застосунок. Вимкніть це призначення, коли закриєте тунель.

Відтворення після збою

Section titled “Відтворення після збою”

Кожен відтворений пакет іде на всі призначення джерела як нове пересилання:

Terminal window
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, тож пакет досі можна відтворити. Див. Відтворення.