> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Data model: envelope, identifiers, idempotency, pagination

> The conventions every Orbit endpoint follows: the envelope shape (data, meta.request_id, meta.timestamp), prefixed identifier conventions, the 24-hour idempotency replay window, cursor pagination, ISO-8601 time fields, and webhook event shapes.

# 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](/api-reference/overview); 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`.

```json theme={null}
{
  "data": {
    "id": "msg_9f8a7b6c5d4e3f2a1b0c",
    "status": "delivered"
  },
  "meta": {
    "request_id": "req_17d9a2f8e1c4b5a6",
    "timestamp": "2026-08-23T12:00:00Z"
  }
}
```

| Field             | Meaning                                                                                                                                  |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `data`            | The resource you asked for (object) or the page of resources (array). Never wrapped a second time.                                       |
| `meta.request_id` | Unique id of this API call, echoing the `X-Request-Id` response header. Quote it when contacting support — Orbit logs are indexed by it. |
| `meta.timestamp`  | ISO-8601 UTC instant the response was generated.                                                                                         |
| `meta.pagination` | Present on list endpoints only — see [Pagination](#pagination-cursor-not-offset) below.                                                  |

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](/reference/error-codes).

## 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:

| Prefix  | Resource                               |
| ------- | -------------------------------------- |
| `msg_`  | Message (SMS, WhatsApp, RCS, email, …) |
| `num_`  | Phone number you own                   |
| `conv_` | Conversation thread across channels    |
| `vrf_`  | Verification session                   |
| `kb_`   | Knowledge base                         |
| `agt_`  | AI agent                               |
| `req_`  | API request id (in `meta.request_id`)  |
| `evt_`  | Webhook event                          |
| `cur_`  | Pagination cursor                      |
| `call_` | Voice call                             |
| `cnt_`  | Contact                                |
| `cmp_`  | Campaign                               |
| `tpl_`  | Template                               |
| `wh_`   | Webhook endpoint configuration         |
| `flow_` | Flow definition                        |
| `exec_` | Flow execution                         |

Treat ids as opaque strings. Prefixes let you tell resource types apart; they carry no semantic content you should parse. The [glossary](/reference/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.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/messages/sms \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Idempotency-Key: order-conf-98421" \
  -H "Content-Type: application/json" \
  -d '{"to":"+14155552671","body":"Your order shipped"}'
```

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 body** → `409 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](#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.

```json theme={null}
{
  "data": [ ... ],
  "meta": {
    "request_id": "req_17d9a2f8e1c4b5a6",
    "timestamp": "2026-08-23T12:00:00Z",
    "pagination": {
      "cursor": "cur_9f8a7b6c5d4e3f2a",
      "has_more": true,
      "total": 1542
    }
  }
}
```

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](/guides/pagination).

The unified [Interaction Search](/guides/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:

```json theme={null}
{
  "id": "evt_3f1c8b2a9d4e5f7a8b6c",
  "type": "message.delivered",
  "created_at": "2026-08-23T12:00:00Z",
  "data": {
    "message_id": "msg_9f8a7b6c5d4e3f2a",
    "status": "delivered"
  }
}
```

| Field        | Meaning                                                                                                                                                                                                                                                      |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`         | Stable event id (`evt_` prefix). Use it for dedup; at-least-once delivery means the same event may arrive twice. The `Idempotency-Key` header echoes this value.                                                                                             |
| `type`       | Event name from the [event catalog](/webhooks/events).                                                                                                                                                                                                       |
| `created_at` | ISO-8601 UTC instant the event was emitted.                                                                                                                                                                                                                  |
| `data`       | Event-specific payload. Per-type shapes are in the [webhook events reference](/reference/webhook-events); transport-level headers and signature verification are in [webhook payloads](/webhooks/event-payloads) and [webhook security](/webhooks/security). |

## Related reading

* [API overview](/api-reference/overview) — base URL, authentication, rate limits, and per-service endpoint groups.
* [Error codes](/reference/error-codes) — the full `error.code` catalog, including the idempotency errors.
* [Glossary](/reference/glossary) — product vocabulary.
* [Tenant isolation](/concepts/tenant-isolation) — why ids and data never cross tenant boundaries.
