Troubleshooting: webhook lifecycle error codes
When a webhook flow breaks, the error code tells you where in the endpoint lifecycle it broke — before the endpoint exists, while a delivery is being attempted, or as the receiver processes (or rejects) the event. This page maps each webhook code to its lifecycle state and gives you the detection query and the fix for each one. For the deep-dive runbook behind any single code, follow the cross-links at the bottom.1. The webhook endpoint lifecycle
An endpoint moves through four states, and each code fires in exactly one of them:- created —
POST /api/v1/webhooksaccepts the URL, validates it, and persists the endpoint. - delivery-attempt — Orbit POSTs the event to your URL and waits for a
2xxack. - delivery-failed — the attempt got a non-2xx response, a timeout, or a receiver-side exception; the retry cadence starts.
- retry / DLQ — up to 9 retries over ~4.3 hours, then the row moves to the dead-letter queue until you requeue or replay it.
2. The codes
WEBHOOK_DELIVERY_FAILED — receiver answered non-2xx (HTTP 502)
Orbit POSTed the event and got back a 404, 5xx, or a timeout instead of a
2xx. Anything outside the 2xx range counts as a failed attempt, so the
retry cadence starts and the row heads for the DLQ if it keeps failing.
WEBHOOK_PROCESSING_FAILED — receiver threw (HTTP 500)
Your handler accepted the request but raised an exception — an unhandled
throw, a crashed downstream call — while processing it. From Orbit’s side
this is a 5xx ack, identical in shape to WEBHOOK_DELIVERY_FAILED; the
registry records it separately because the fix is in your handler’s code, not
in routing or uptime.
WEBHOOK_SIGNATURE_INVALID — HMAC mismatch (HTTP 401)
Your verifier rejected the delivery: the computed HMAC matched none of the
v1 candidates in the signature header. This is the same failure class as
INVALID_SIGNATURE, reported from the delivery-log side. Until verification
passes, every delivery fails in the delivery-attempt state.
Creation-time siblings
The three created-state rejections are expanded in Troubleshooting: webhook endpoint creation errors:WEBHOOK_ENDPOINT_CAP_REACHED (tenant already at 10 endpoints),
WEBHOOK_DNS_INVALID (hostname does not resolve), and
INVALID_WEBHOOK_URL (non-HTTPS scheme, or a private/loopback/internal
address blocked by safety validation). All three answer 422 before any
endpoint is persisted.
The dedup sibling
Retry and DLQ states re-deliver the same event id — that is at-least-once semantics, not a new error code. If your consumer treats the re-delivery as a new event, the failure is a consumer-side dedup gap; see Troubleshooting: duplicate webhook events and consumer-side dedup for the event-id pattern that makes retries and replays safe.3. Detecting each code
List the deliveries for the endpoint and filter to the failures:GET /api/v1/webhooks/{id}/deliveries/{deliveryId}) and
read the HTTP status your endpoint returned:
- 401 → signature path. Your verifier rejected the HMAC; work the
WEBHOOK_SIGNATURE_INVALIDfix below. - 404 / 5xx / timeout → delivery or processing path. A
404means the route moved; a500from your handler means it threw; a timeout means it ran past your endpoint’stimeout_seconds.
401:
false, not throw — a throwing verifier turns
every HMAC mismatch into a WEBHOOK_PROCESSING_FAILED (500) instead of the
WEBHOOK_SIGNATURE_INVALID (401) you can actually diagnose.
4. Fix per code
Non-2xx path — WEBHOOK_DELIVERY_FAILED / WEBHOOK_PROCESSING_FAILED
- Ack within the delivery window. Your endpoint must return a
2xxwithin the window documented on Webhook security (30 seconds by default, or your endpoint’stimeout_secondsoverride). Move heavy work to a queue and ack first — a slow handler reads as a timeout. - Return 2xx on duplicates. A re-delivery of a known event id still needs
a
2xx: a fast duplicate ack stops the retry cadence. - Handle receiver exceptions. A
500from your handler is the same retry trigger as a routing failure. Catch and log the exception, run the side effect on your worker queue, and still ack2xxonce the event is persisted — the durable consumer pattern covers the shape.
Signature path — WEBHOOK_SIGNATURE_INVALID
- Wrong secret. Test and live secrets do not cross-verify; a masked
preview (
whsec_********...<last4>) is for identification, not for pasting into your verifier. If the cleartext is lost, rotate withPOST /api/v1/webhooks/{id}/rotate-secretand store the new value — it is returned once. - Raw-body mismatch. The signed string is
<t>.<raw_body>. If a body parser (Expressexpress.json(), a global Fastify parser, Next.jsbodyParser) consumed the request first, the re-serialized bytes never match — verify against the raw bytes (express.raw(),request.text()) before any parsing. - Rotation window. During the grace window, check every
v1candidate inX-Orbit-Signature(fall back toX-Devotel-Signature) and alsoX-Orbit-Signature-Nextagainst the previous secret.
See also
- Troubleshooting: signature verification failures — every HMAC failure class, with copy-paste verifiers.
- Troubleshooting: failed webhook deliveries, retries, DLQ, and replay — the retry/DLQ/replay recovery loop.
- Troubleshooting: webhook endpoint creation errors — the three created-state 422s.
- Troubleshooting: duplicate webhook events and consumer-side dedup — at-least-once semantics and the event-id anchor.
- Error code reference — the raw registry table this page routes.