Skip to main content

Catalogue sync

The event table must mirror the generated catalogue before subscribers can pick events. Two mechanisms keep it that way: Verify with:
missing_internals lists events present in code but not yet in the database; stale_internals lists database rows no route produces anymore (they stay subscribable until an operator deactivates them).

Test delivery

POST /api/v3/webhook/webhook/{id}/test enqueues a synthetic webhook.test delivery to that subscriber and returns the dispatch_id. Follow it on GET /api/v3/webhook/dispatch/{dispatch_id} or on the detail screen in Horizon Enterprise. The webhook does not need to be subscribed to webhook.test for the action to work; subscribing simply lets you filter those deliveries later.

Development sink

POST /api/v3/webhook/sink is a built-in subscriber endpoint mounted only when APP_ENV is development or certification and WEBHOOK_SINK_SECRET is set. It verifies the signature with that secret, logs Webhook sink delivery accepted with the envelope headers, and answers 200, so a dispatch row flips to success without any external system.
1

Create a webhook pointing at the sink

url = https://<api host>/api/v3/webhook/sink, is_global = true. Copy the returned whsec_… secret.
2

Share the secret with the API

Set WEBHOOK_SINK_SECRET to that value in the tier’s environment and redeploy (or restart locally).
3

Send a test

POST /api/v3/webhook/webhook/{id}/test, then check the dispatch row and the API logs.
Production never mounts the sink: the route answers 404 there regardless of the variable.

Dashboard and health

Environment variables

The signature tolerance (300 s), the backoff schedule, the attempt cap (5) and the auto-disable threshold (20) are code constants in lib/webhook/outbound/webhook-out.constants.ts and webhook-signature.ts, not environment variables — they are part of the public contract and change only with a release.

Permissions

All webhook permissions are granted to the administrator role only in this release. Tokens for automation must carry the specific internals:

Known limits

  • No fixed egress IP. Deliveries originate from the API’s serverless runtime; allow-list by signature, not by address.
  • Thin body. The payload never includes the resource; a second API call is required to read it.
  • At-least-once. Retries and replays may repeat a delivery; deduplicate on id.
  • No ordering guarantee across events, even for the same resource.
  • Secret rotation has no overlap window.
  • Purge events carry data.account = null and only reach global subscribers.
  • Internal runbook for the first rollout (per-tier steps, rollback, day-2 signals): docs/initiatives/webhook/outbound/11-rollout-and-operations.md in the monorepo.
  • API reference: Webhook, Webhook Event, Webhook Dispatch, Event.