Skip to main content
Replay keeps the delivery attempt counter
Operator replay of a webhook_dispatch row no longer resets attempts. The next POST increments the same lifetime counter so Entregas and the subscriber attempt field keep a full history. Automatic retries still stop at five POSTs; replaying an exhausted row performs one additional POST. See Retries and failures.
Outbound webhooks — first release

Outbound webhooks (first release)

horizon-api now notifies external systems when a resource changes.What shipped
  • Event catalogue generated from the v3 route tree (<resource>.<action>), synced to the event table on every deploy and checked in CI. See the catalogue.
  • Subscriptions — global or per-account webhooks with a one-shot whsec_ secret, PUT /webhook/webhook/{id}/events to manage the event set, secret rotation and test deliveries. See Subscriptions.
  • Signed deliveries — x-bbrands-signature: t=<ts>,v1=<hmac> over <ts>.<raw body>, 300 s tolerance, thin body with api_version 2026-09-11. See Delivery and signature.
  • Retries — 1 m → 5 m → 30 m → 2 h, then exhausted; automatic disable after 20 consecutive failed deliveries; operator replay with confirm_duplicate. See Retries and failures.
  • Operations — GET /webhook/dashboard/summary, GET /webhook/catalogue/status, health component webhook-out, metric webhook_out_deliveries_total{status}, development/certification sink. See Operations.
  • API reference — every webhook endpoint documented from a real OpenAPI spec: Webhook, Webhook Event, Webhook Dispatch, Event.
Breaking / migration notes
  • Webhooks created before this release are disabled with disabled_reason = "legacy-secret-rotation-required". Rotate the secret, deploy it on the subscriber, then activate.
  • Callback URLs must be HTTPS on a public host. Existing rows that no longer pass validation are rejected on the next update.
  • Publishing is behind ENABLE_API_EVENT_QUEUE, enabled per environment by the platform team after the catalogue sync.
  • Redirects (3xx) are never followed and count as terminal failures.
Schema — migration 0236 splits the webhook-in / webhook-out queue lanes; 0237 adds webhook.is_global, description, consecutive_failures, disabled_at, disabled_reason, makes webhook.account nullable, renames webhook_dispatch.retry to attempts and adds event.account_scope, method, route. See the Webhook schema.