Skip to main content

Documentation Index

Fetch the complete documentation index at: https://orbit-docs.devotel.io/llms.txt

Use this file to discover all available pages before exploring further.

Webhook Event Payloads

This page documents the wire-level shape of an Orbit webhook delivery — the request envelope, the headers Orbit sends, and the metadata Orbit captures about every delivery attempt (including the request headers sent and the response headers received from your endpoint). For the full catalog of event types (message.delivered, call.completed, …) see Webhook Events. For HMAC signature verification see Webhook Security.

Request envelope

Every delivery is a single HTTP POST with a JSON body that follows the canonical envelope:
{
  "id": "evt_3f1c8b2a9d4e5f7a8b6c1d2e3f4a5b6c",
  "type": "message.delivered",
  "created_at": "2026-05-24T12:00:00Z",
  "data": {
    "message_id": "msg_xyz789",
    "channel": "sms",
    "to": "+1415555****",
    "status": "delivered",
    "delivered_at": "2026-05-24T12:00:00Z"
  }
}
FieldTypeNotes
idstringStable event id. Use this for idempotency / dedup — at-least-once delivery means the same event may arrive twice (e.g. when your endpoint timed out the first send).
typestringEvent-type name from the event catalog.
created_atstringISO-8601 timestamp when the event was emitted on Orbit’s side.
dataobjectEvent-specific payload. The exact shape per event type is documented in the webhook events reference.

Request headers

Orbit sends every delivery with the following headers:
HeaderValueNotes
Content-Typeapplication/json; charset=utf-8
User-AgentDevotel-Webhooks/1.0Static. Use this for ACL allowlisting if your receiver gates by UA.
X-Devotel-Event-Id<event id>Mirrors body.id. Some receivers (e.g. Cloudflare Workers) prefer to deduplicate from a header.
X-Devotel-Event-Type<event type>Mirrors body.type.
X-Devotel-Signaturet=<unix>,v1=<hex>[,v1=<hex>]HMAC-SHA256. See Webhook Security.
X-Devotel-Delivery-Id<delivery uuid>Unique per delivery attempt — the same event_id retried twice carries two distinct delivery_ids. Use this for debugging across the deliveries API.

Inspecting delivery attempts

Orbit persists metadata about every delivery attempt (request headers sent, response status, response body, response headers received). Operators can inspect these via the dashboard or the API:
GET /api/v1/webhooks/{endpoint_id}/deliveries/{delivery_id}
Authentication: Clerk session or API key with webhooks:read scope. 200 OK
{
  "data": {
    "id": "wdl_3f1c8b2a9d4e5f7a8b6c",
    "endpoint_id": "wh_abc123",
    "event_type": "message.delivered",
    "event_id": "evt_xyz789",
    "status": "delivered",
    "http_code": 200,
    "latency_ms": 142,
    "attempts": 1,
    "next_retry_at": null,
    "last_response_status": 200,
    "last_response_body": "{\"ok\":true}",
    "request_headers": {
      "Content-Type": "application/json; charset=utf-8",
      "User-Agent": "Devotel-Webhooks/1.0",
      "X-Devotel-Event-Id": "evt_xyz789",
      "X-Devotel-Event-Type": "message.delivered",
      "X-Devotel-Signature": "t=1715357600,v1=4f9c2e6b...",
      "X-Devotel-Delivery-Id": "wdl_3f1c8b2a9d4e5f7a8b6c",
      "Authorization": "<redacted>"
    },
    "response_headers": {
      "Content-Type": "application/json",
      "X-Request-Id": "your-trace-id-xyz",
      "Cache-Control": "no-store"
    },
    "payload": {
      "id": "evt_xyz789",
      "type": "message.delivered",
      "created_at": "2026-05-24T12:00:00Z",
      "data": { "message_id": "msg_xyz789", "channel": "sms", "status": "delivered" }
    },
    "created_at": "2026-05-24T12:00:00Z",
    "updated_at": "2026-05-24T12:00:00Z",
    "completed_at": "2026-05-24T12:00:00Z"
  },
  "meta": { "request_id": "req_abc123", "timestamp": "2026-05-24T12:00:00Z" }
}

request_headers (captured since 2026-04, migration 217)

The exact header set Orbit sent on this delivery attempt. Useful for self-serve HMAC debugging — operators can inspect the X-Devotel-Signature value Orbit signed against and compare it to what their receiver computed. Redaction. Orbit redacts sensitive header values before persisting:
  • Authorization<redacted>
  • Any header whose name starts with X- and ends with -Secret<redacted>
The redaction is one-way — once persisted, the original values are unrecoverable from the delivery row. This is intentional: the deliveries API is read-only and operator-facing; raw secrets must not surface in dashboard render paths even via inspect.

response_headers (captured since 2026-05-24, migration 306)

The header set returned by your endpoint on this delivery attempt. Useful for:
  • Debugging your receiver’s CDN / WAF chain (e.g. confirming Cloudflare didn’t strip your body).
  • Correlating with your own logging — many receivers echo a request id (X-Request-Id / Traceparent) that the deliveries view can surface.
  • Validating that your receiver returned the expected Cache-Control: no-store for webhook responses.
Backwards compatibility. Deliveries recorded before migration 306 (or before your tenant’s just-in-time runner caught up) carry response_headers: null. The dashboard renders a “headers not captured for this delivery” empty-state in that case — it does NOT block render of the delivery row itself.

last_response_body

Truncated to the first 4096 bytes of the response body. Use for debugging your receiver’s error messages. Already-truncated bodies are appended with […truncated].

Listing recent deliveries

GET /api/v1/webhooks/{endpoint_id}/deliveries
Cursor-paginated. The same request_headers / response_headers fields are returned per row so the dashboard can render the headers panels inline without an extra round-trip per row. Query parameters
NameTypeNotes
limitinteger (1–100)Default 20.
cursorstringOpaque cursor from the previous response.
statusenumFilter — pending, delivered, failed, retrying.
event_typestringFilter by event type, e.g. message.delivered.
from_datestringISO-8601 lower bound on created_at.
to_datestringISO-8601 upper bound on created_at (exclusive).

Replay + retry

If your endpoint was down or returned a non-2xx, Orbit retries on the delivery schedule. To force a replay of an already-completed delivery (e.g. after a downstream bug fix):
POST /api/v1/webhooks/{endpoint_id}/deliveries/{delivery_id}/replay
Creates a new delivery row with the same event_id and payload and queues it for immediate dispatch. The original row is preserved as audit trail. To re-queue a failed delivery (without changing the payload):
POST /api/v1/webhooks/{endpoint_id}/deliveries/{delivery_id}/retry
Resets the delivery’s status to pending and sets next_retry_at = now() so the worker picks it up on the next tick.

See also