Client API Webhooks
Webhook infrastructure is designed for public-beta readiness but is currently gated while the Client API remains in internal beta. This page documents the contract and operational guidance so you can build and test your receiver now. For production runbooks, see:/sources/client-api-webhooks-ops.
Summary: what you must do
- Verify signatures using the raw request body (when
Omni-Signatureis present). - Enforce a replay window using the included timestamp.
- Dedupe using
event.id(safe even if retries are added later). - Return
2xxquickly and process asynchronously. - If
Omni-Signatureis absent, treat the delivery as unsigned and follow the guidance below.
Event families
usage.threshold_reachedinvoice.finalizedinvoice.payment_failedapi_key.revoked
Delivery model (operational contract)
Current (internal beta)
- At-least-once intent with bounded retry for transient delivery failures.
- Retry classes: network error (
status=0),408,429,5xx. - Default retry profile:
3attempts, exponential backoff from500mswith jitter. - Retry settings are exposed in internal beta diagnostics as
retryPolicy.maxAttemptsandretryPolicy.baseDelayMs.
Target (public-beta-ready)
- At-least-once delivery.
- Exponential retry with bounded maximum attempts.
- Idempotent consumer requirement on the receiver.
Delivery diagnostics (internal beta)
Webhook delivery diagnostics include retry metadata:attempts: number of delivery attempts made for an endpointretryExhausted:truewhen a retryable failure exhausted max attempts
Event shape
Webhook POSTs carry a JSON payload with this high-level shape:Signature verification
Webhook requests include:Omni-TimestampX-Request-Id
Omni-Signature
If Omni-Signature is absent
In the current internal beta, OMNI may deliver webhook events without a signature if signing is not enabled for your environment.
Recommended receiver behavior:
- Do not attempt signature verification if
Omni-Signatureis missing. - Keep your webhook URL unguessable (random path) and treat the endpoint as a secret.
- Log and alert on unsigned deliveries so you can tighten posture when signing is enabled.
Signing payload
Compute the signature over:Omni-Timestampis the exact header value (string, unix seconds).raw_request_bodyis the exact raw bytes received over HTTP (before JSON parsing).
Algorithm
- HMAC-SHA256
- Secret: your webhook signing secret
- Output format: lowercase hex digest
Verification steps
- Reject events older than your replay window.
- Recompute HMAC from timestamp + raw request body.
- Constant-time compare against
Omni-Signature. - Persist event ID for deduplication.
TypeScript (Node.js) example
Python example
Go example
Replay protection
- Store processed event IDs for at least 7 days.
- Reject duplicate event IDs.
- Keep handler logic idempotent.
Failure handling playbook
- Return
2xxonce you have durably queued the work. - Treat
event.idas the dedupe key. - If you return
4xx, OMNI will treat that as a signal your receiver rejected the payload. - If you return
5xx, OMNI will treat that as a transient receiver failure.
Recommended receiver behavior
- Acknowledge fast: respond
2xxquickly and queue work to background processing. - Dedupe: use
event.idas your primary dedupe key. - Handle out-of-order: do not assume event ordering.
- Use request IDs: log
X-Request-Idandevent.requestIdfor correlation.