Components
How a delivery flows
1
Publish (inside the request)
Every route that goes through the shared
handler()/ok() wrapper calls
publishApiTransactionEvent after a 2xx. The publisher derives the
catalogue internal from the method and route, then checks the in-memory
set of internals that currently have at least one active subscriber.
If nobody listens, nothing is enqueued — the queue tables stay empty
for the thousands of mutations no one subscribed to. Failures here are
logged and never surface to the API caller.2
Route (webhook-out-route)
The consumer loads the catalogue row, resolves the owning account of the
mutated resource (see below), and selects the matching webhooks: global
ones always match; account-scoped ones match only when the resolved
account equals the webhook’s
account. One webhook_dispatch row is
inserted per match with status = pending, then one
webhook-out-deliver message is sent per row.3
Deliver (webhook-out-deliver)
The consumer builds the thin body, signs it with the subscriber secret,
POSTs it with a 10 s timeout and redirect: "error", and records the
outcome (success, error with next_retry_at, or exhausted) on the
ledger row. Retryable failures are rescheduled on the custom backoff
schedule; see Retries and failures.Owning-account resolution
Account-scoped subscriptions need to know which account a mutated row belongs to. The catalogue records one of four strategies per event:
The resolver reads the row once, on the route stage; deliveries never touch
the source table again.
Lanes and schedule
Both topics run on the dedicatedwebhook-out lane (vercel.json →
app/api/queue/consumers/webhook-out/route.ts) so a burst of deliveries
cannot starve business jobs, and business backlogs cannot delay a
subscriber’s notification. The deliver topic uses a custom retry schedule
instead of the platform default — the subscriber-facing backoff is part of
the public contract.
Feature flag
Publishing is gated byENABLE_API_EVENT_QUEUE. While it is false the API
behaves exactly as before: no catalogue lookup, no cache, no queue traffic.
Operators flip it per environment after the catalogue is synced and the first
subscriber has been tested; see Operations.