Catalogue sync
Theevent 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.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 theadministrator 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 = nulland only reach global subscribers.
Related
- Internal runbook for the first rollout (per-tier steps, rollback, day-2
signals):
docs/initiatives/webhook/outbound/11-rollout-and-operations.mdin the monorepo. - API reference: Webhook, Webhook Event, Webhook Dispatch, Event.