Webhook delivery
The Gateway delivers webhook notifications to your server via HTTP POST. Verified domain facts describe observed state changes; command acknowledgements separately report commands accepted before their outcome is known.
Event Catalog
Webhook events are grouped by category. Each category has its own reference page documenting every event name, its payload fields, and when it fires:
All 20 command.*.accepted values use one shared
CommandAcceptedEvent contract.
Every event, regardless of category, carries a timestamp field (an RFC 3339 / ISO 8601 UTC string, e.g. 2026-06-28T14:30:00.000Z). It records the event time. Preserve same-session delivery order as described below. Delivery requests use W3C traceparent and tracestate headers for distributed tracing.
HTTP POST Delivery Format
Each webhook is delivered as an HTTP POST request with a JSON body. The request includes several headers for identification, deduplication, and security.
Request Headers
Signature Verification
Every delivery is signed with the cluster Ed25519 key so you can verify the request originated from the Gateway and was not tampered with. No per-app secret is needed — verification uses the cluster public key, fetchable from GET /.well-known/webhook-public-keys (unauthenticated). More than one key may be published during rotation (current + previous), so match on akid.
The X-Webhook-Signature header carries three required fields: t (Unix epoch seconds), akid (the cluster public-key id), and v1a (the standard-base64 Ed25519 signature). Require exactly one of each, reject duplicate known fields, and ignore unknown extension fields for forward compatibility. Require X-Webhook-App-UUID and X-Idempotency-Key, compare the application UUID with your configured value, and verify v1a over the exact LF-delimited bytes v1a\n<unix_seconds>\n<app_uuid>\n<idempotency_key>\n<raw_body>. Verify before parsing JSON, acting on the event, or consulting your deduplication store. Reject deliveries whose t is stale (for example, more than 5 minutes of skew) to limit replay.
Full field reference, replay protection, public-key fetching, key rotation, and ready-to-use Node.js / Python / Go verification code are in the dedicated Webhook Signatures guide.
Webhook Delivery & Retries
Delivery is at-least-once: the Gateway retries transient failures automatically, so the same logical event may reach your endpoint more than once. A retryable failure can result in up to 5 HTTP POST attempts across 3 delivery cycles:
- The first cycle starts as soon as the event is produced. After a retryable failure, the Gateway waits approximately
500msand retries once within that cycle. - If both HTTP attempts fail, an asynchronous retry worker starts the second cycle. A retryable failure on that cycle’s first attempt triggers one inline retry after approximately
500ms. - The third and final cycle makes one HTTP attempt. If that attempt also fails with a retryable error, the event reaches retry exhaustion and is moved to the dead-letter queue.
Each individual attempt is allowed up to 10 seconds to complete before it is treated as a timeout.
Which Responses Are Retried
Build idempotent handlers. Because failed deliveries can cause an event to be re-sent, the same logical event may be delivered to your endpoint more than once. First verify the signature-covered raw body, application UUID, and idempotency key; only then consult or update your deduplication store. Process events for one session_uuid in the same-session delivery order guaranteed below; use timestamp as event-time metadata, not as a total-order key. Return 2xx after durably recording the event.
Undeliverable Events
Redirects are not followed: a redirect response is rejected without contacting its target and is treated as a retryable redirect-policy failure under the normal delivery budget. A final 5xx, 408, 429, or a connection, network, timeout, or redirect-policy failure can make up to 5 attempts across the same 3 cycles (2 + 2 + 1) because the first two cycles include one inline retry. A valid Retry-After on a 408, 429, or 5xx response can lengthen that inline delay, but never beyond 30 seconds. Every 425 instead records a persistent not-before deadline and defers without spending that delivery budget; its delay defaults to 30 seconds when Retry-After is missing or invalid. Other final 4xx responses are dead-lettered immediately with reason: consumer_rejected and the response status, without retry. Resolve the endpoint failure so subsequent events are accepted. When asking support to investigate a dead-lettered delivery, provide the event type, timestamp, X-Idempotency-Key, and any session_uuid or room_id in the verified event.
Per-Session Ordering Guarantees
Events for the same session_uuid are processed sequentially in the order they were produced. If an event has a retryable delivery failure, subsequent events for that session are held until the failed event succeeds or reaches retry exhaustion. A permanent consumer 4xx failure is dead-lettered immediately, and an exhausted retryable failure is moved to the dead-letter queue; in either case, delivery then continues with the next event. Among events your endpoint receives for one session, an earlier event is delivered before a later one — for example, call.answered before call.ended when both are delivered.
Events for different sessions may be delivered concurrently and can arrive interleaved or out of order relative to one another; ordering is guaranteed only within a single session_uuid (see above).
Room lifecycle events (room.created, command.room.delete.accepted, room.deleted) and room playback events are not associated with a session_uuid and have no ordering guarantee relative to session events. command.room.delete.accepted is emitted immediately when Delete Room is accepted (202); room.deleted follows later on confirmation, or with reason:"timeout" if confirmation never arrives.