Skip to content

Webhooks

OPORA принимает incoming webhook’и через единый endpoint https://app.opora.example/webhook/<uuid> — uuid выдаётся для каждой trigger-ноды при publish’е workflow’а. Зависит от типа trigger-ноды, как именно OPORA валидирует запрос.

Trigger-нодаСхема валидации
webhook.trigger (generic)Опциональный HMAC по preset’у (см. ниже)
telegram.webhook.triggerX-Telegram-Bot-Api-Secret-Token header echo
bitrix24.webhook.triggerapplication_token body-field echo
amocrm.webhook.triggerquery-param ?token=<...> match
yookassa.webhook.triggerquery-param ?<token> match + IP-whitelist (on your side)
tbank.webhook.triggerToken-based SHA-256 over sorted fields + password
zadarma.webhook.triggerMD5-signature в query
vk.webhook.triggerbody secret field echo + Callback API confirmation-handshake
stripe.webhook.triggerHMAC-SHA256 over raw body + Stripe-Signature header
github.webhook.triggerHMAC-SHA256 + X-Hub-Signature-256
shopify.webhook.triggerHMAC-SHA256 + X-Shopify-Hmac-Sha256
aws.sns.webhook.triggerMessage signature verification (RSA + X.509 cert)

В каждом случае OPORA отклоняет запросы с неверной подписью до того, как они дойдут до workflow’а — 401 Unauthorized уходит обратно провайдеру.

Для провайдеров без dedicated-ноды (Tilda, custom webhooks, кастомные HTTP-клиенты). Настройки ноды:

  • validation: none / hmac / token
  • Если hmac:
    • algorithm: sha256 / sha1
    • secretRef: ссылка на secret с ключом подписи
    • headerName: как вендор называет signature header (X-Hub- Signature-256 у GitHub, X-Hmac-Signature у custom)
  • Если token:
    • paramName: token (default)
    • secretRef: ожидаемое значение

HMAC-валидация требует байт-эксактно тех байтов, что прислал вендор — после JSON.parse() + JSON.stringify() у вас уже не те байты (другой whitespace, ordering ключей). OPORA запускается с rawBody: true, Nest/Express сохраняют req.rawBody для HMAC- check’а, parsing body для workflow’а идёт параллельно.

Это прозрачно — вам ничего конфигурировать не нужно.

Повторный webhook от провайдера (provider не получил 200 OK и ретраит) приводит к одному run’у, не двум:

  • Ключ дедупа зависит от trigger-ноды (см. Runs & Traces — Idempotency)
  • Window кэша — 5 минут в Redis
  • Повторный запрос получает 200 OK, но X-Opora-Idempotent: true header в response’е, run не создаётся

Некоторые провайдеры (ЮKassa, T-Kassa) публикуют список своих IP’ов, с которых шлют webhook’и. Для hardening’а вы можете:

  • Настроить firewall / Caddy на VM’е пропускать /webhook/* только с этих IP’ов
  • Или на уровне network-security-group VK Cloud’а (менее гибко, но проще)

OPORA в коде IP-whitelisting не делает — считаем это infrastructure-level concern’ом.

Public endpoint /webhook/* защищён rate-limit’ом 60 запросов / минуту / IP. При превышении — 429 Too Many Requests с Retry- After header’ом.

Этот лимит достаточен для legit-провайдеров (Telegram шлёт raz в 1-2 секунды на бота, CRM’ы реже). Атаки / runaway loops блокируются.

Если у вас редкий случай legit-traffic’а > 60/min (massive-rollout marketing-кампания через webhook-URL), пишите в поддержку — поднимем лимит для этого space’а.

OPORA возвращает:

  • 200 OK — webhook принят, run создан (или dedupe’нут)
  • 400 Bad Request — payload не JSON / не parseable
  • 401 Unauthorized — signature не сошлась
  • 404 Not Found — uuid webhook’а не существует (trigger-нода удалена или не publish’нута)
  • 429 Too Many Requests — rate-limit
  • 5xx — internal error OPORA, провайдер должен retry’ить. В Sentry уходит alert (если настроен).

OPORA сама шлёт webhook’и наружу через ноду external.http с POST

  • custom headers. Для HMAC-signing (вам надо подписать payload для receiving-сервиса) используйте auth.preset = "hmac" на ноде + secret с ключом.