Skip to main content

Data model

Every Orbit endpoint — REST request, REST response, and webhook delivery — follows the same conventions. This page is the canonical reference for those conventions: the response envelope, identifier shapes, idempotency, pagination, time formats, and webhook event envelopes. Per-endpoint field schemas live in the API reference; this page defines the shared frame those schemas sit inside.

Envelope: data and meta

Every successful response returns a data object (or array) plus a meta object. Errors return an error object plus the same meta.
On an error, error.code is the machine-readable value (stable enough to switch on), and meta.request_id is still present for support triage. The full code catalog is the error codes reference.

Identifier shapes

Every resource id carries a prefix naming its entity type, so an id is self-describing in logs, webhook payloads, and support tickets. Suffixes are lowercase hex (32 characters, 16 random bytes). Ids are unique per environment — no two tenants ever receive the same id, and ids always refer to a single tenant’s resource. Common prefixes: Treat ids as opaque strings. Prefixes let you tell resource types apart; they carry no semantic content you should parse. The glossary catalogs term-level vocabulary; this table is the id-shape reference.

Idempotency and retries

Every POST that creates a resource accepts an Idempotency-Key header. Send one on every creation call.
Replay semantics for the 24-hour window after the first request:
  • Same key + same body → Orbit returns the cached original response. No duplicate message is sent, no duplicate charge is posted.
  • Same key + different body409 IDEMPOTENCY_KEY_REUSED. Keys are scoped to one operation shape — generate a fresh key per logical send (an order id, a UUID, anything unique per attempt).
  • Key expired (over 24 hours) → the request is treated as new and may create a second resource.
Webhooks are idempotent in the other direction: every delivery carries a stable Idempotency-Key equal to the event id, so your receiver can dedup retries safely. See Webhook event shapes below.

Pagination: cursor, not offset

List endpoints are cursor-paginated. Offset pagination is not offered — offsets drift under concurrent inserts, cursors do not.
Pass ?cursor=<meta.pagination.cursor> (and optionally &limit=<n>, default 20, maximum 200) to fetch the next page. Stop when has_more is false. Pass cursors verbatim — they are opaque and URL-safe. Implementation detail is in the pagination guide. The unified Interaction Search surface returns rows ordered by last_activity_at descending with this cursor contract — the dashboard and CSV export share it.

Time formats

All timestamps are ISO-8601 in UTC — response fields, request fields you send, and expires_at-style deadlines.
  • Write: send (Send) objects with send_at fields accept an ISO-8601 instant; a future instant schedules the send and returns 202, a past instant (or omission) sends immediately.
  • Read: every timestamp you receive (created_at, meta.timestamp, message timestamp) is UTC with a Z suffix. Convert client-side for display.
  • Deadlines: fields like expires_at (verification sessions) state the last valid instant; requests that land after it return a terminal 4xx such as 410 EXPIRED_TOKEN.

Webhook event shapes

Webhook deliveries follow a fixed envelope — distinct from the REST envelope above:
  • API overview — base URL, authentication, rate limits, and per-service endpoint groups.
  • Error codes — the full error.code catalog, including the idempotency errors.
  • Glossary — product vocabulary.
  • Tenant isolation — why ids and data never cross tenant boundaries.