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

# The DLR model: two planes, one vocabulary

> How to tell which DLR lane a behaviour belongs to: the carrier plane (a receipt crosses from the carrier into Orbit and drives a status row) and the send-side plane (Orbit's own canonical vocabulary, encoded back out as an SMPP deliver_sm or a webhook POST).

# The DLR model: two planes, one vocabulary

Four concept pages each cover one slice of delivery receipts — [Delivery lifecycle](/concepts/delivery-lifecycle), the [DLR and MO gateway pipeline](/concepts/dlr-and-mo-pipeline), the [Send-side DLR model](/concepts/send-side-dlr-model), and the [SMPP edge model](/concepts/smpp-edge-model). Each answers a different question, but none tells you which plane a given receipt behaviour lives on. When your integration reads a receipt, that distinction is the thing that decides what it means.

Both planes operate on one vocabulary: the six canonical delivery states every channel maps into, defined in the [Send-side DLR model](/concepts/send-side-dlr-model). What differs is where the state came from and which surface carries it.

## The two planes

**The carrier plane** is the path a carrier's own receipt takes: a delivery receipt arrives from the carrier through the SMPP gateway, the DLR pipeline verifies the callback, deduplicates replays, maps the receipt onto a message-row status update, and fans the transition out to your webhook subscribers. Every question of the form "what did the carrier say about my message" belongs on this plane. Its definition of states and transitions is [Delivery lifecycle](/concepts/delivery-lifecycle); its definition of how the receipt physically crosses into Orbit is the [DLR and MO gateway pipeline](/concepts/dlr-and-mo-pipeline).

**The send-side plane** is the other direction: for traffic Orbit terminated itself, or for every SMPP client receipt in general, Orbit holds the canonical vocabulary and encodes receipts *back out* — onto an SMPP bind as a `deliver_sm`, or to your endpoint as a webhook POST. Every question of the form "what does Orbit report to me, and in what encoded form" belongs on this plane. Its definition of the vocabulary and the encoders is the [Send-side DLR model](/concepts/send-side-dlr-model); its definition of where those encoded receipts leave Orbit — bind, webhook, or both — is the [SMPP edge model](/concepts/smpp-edge-model).

The reader's rule: a receipt that names what a *carrier* returned makes no sense unless it maps into one of the six canonical states, and a receipt that came from *Orbit's own termination path* has no carrier leg to consult at all — it crosses directly on the send side. Sort your mental model first; the individual pages then answer the specific question.

## The canonical row set

Every delivery event from any channel resolves to exactly one of six states:

| Canonical state  | Meaning                                                          |
| ---------------- | ---------------------------------------------------------------- |
| `sent`           | Acknowledged by the destination; in flight.                      |
| `delivered`      | Final success — the handset confirmed receipt.                   |
| `read`           | The recipient opened the message.                                |
| `failed{code}`   | Terminal failure, with one of the documented failure codes.      |
| `expired`        | The message's TTL or validity window lapsed.                     |
| `rejected{code}` | Pre-send refusal — Orbit declined the submission before it left. |

Carrier `stat` and `err` tokens map into this set, and the same vocabulary is re-encoded back out on the send side:

| Carrier `stat` token                                                       | Canonical state                         |
| -------------------------------------------------------------------------- | --------------------------------------- |
| `DELIVRD`                                                                  | `delivered`                             |
| `ACCEPTD`, `ENROUTE`, `SCHEDULD`                                           | `sent` (acknowledged, in flight)        |
| `EXPIRED`                                                                  | `expired`                               |
| `UNDELIV`, `REJECTD`, `DELETED`, `UNKNOWN` (and the other terminal tokens) | `failed`                                |
| Any unrecognised token                                                     | Acknowledged without moving the message |

Because the same table decodes carrier tokens in and encodes receipt tokens back out, a token a carrier sends in means the same thing as the token Orbit returns on your bind. The [Send-side DLR model](/concepts/send-side-dlr-model) page defines this mapping; the [SMPP edge model](/concepts/smpp-edge-model) page defines the `err:` token vocabulary (`81` not on destination channel, `88` re-engagement required, `89` rate limit, `00` unspecified provider error) carried on the failure arm.

## Absorbed traffic

When Orbit's termination engine absorbs a submission onto another channel — your SMPP `submit_sm` resolves to a WhatsApp message, for example — there is no carrier to return a `deliver_sm` from, and the receipt crosses the send-side plane directly. The resolved canonical state is injected onto your bind as a `deliver_sm` in the same envelope a carrier receipt would have used.

Three rules hold on absorbed traffic, and they follow from the canonical model rather than being a per-channel config:

* **Only `DELIVRD` ends a successful termination.** A later `read` (a "blue ticks" event) is recorded on the message row, but it never produces a second `deliver_sm` — `DELIVRD` already closed the receipt stream for that submission, and a second terminal receipt would be a duplicate. On absorbed traffic, `DELIVRD` is the only receipt a successful termination produces.
* **Exactly one receipt per absorbed submission.** The submission-to-receipt correlation is consumed by the first receipt. Events that arrive afterward are recorded but produce no second receipt.
* **A synchronously rejected submit never yields a returned receipt.** If preflight declines the submission, the error travels back in the `submit_sm_resp` itself, inside the submission hold; the out-of-band receipt encoders never run, and no `deliver_sm` arrives later to contradict the synchronous answer.

If the submission was terminated as ordinary SMS — that is, genuinely carrier-routed — the receipt is a carrier-generated `deliver_sm` on the carrier plane with the same canonical vocabulary. Nothing about carrier-routed SMS changed when the absorbed-traffic encoder was added; it fills a hole rather than rewriting an existing path.

## The timer contract

When a carrier never returns a receipt at all, a scheduler advances the message row after a per-channel grace window. The grace is part of the DLR contract, not an implementation detail you time against:

* **SMPP-backed channels.** The no-DLR grace is **30 minutes**. A `sent` SMS that never receives a receipt flips to `submitted_no_receipt`; because a genuine `delivered`, `read`, or failure receipt can still land afterward, the state is tagged wire-intermediate (`is_terminal: false`) rather than final. The 30-minute window is deliberately longer than the typical carrier receipt latency so a slow receipt usually wins the race.
* **Meta DM channels.** Meta's Send API never emits delivery receipts, so a Meta DM send is flagged `no_dlr_channel` and flips to `submitted_no_receipt` after **5 minutes**. On those channels `submitted_no_receipt` behaves like a functional delivery signal on an opted-in recipient, because Meta guarantees delivery on accept.

The [Delivery lifecycle](/concepts/delivery-lifecycle) page states the per-channel grace values; the [DLR and MO gateway pipeline](/concepts/dlr-and-mo-pipeline) page describes where the receipt-ingress loop those timers protect actually lives.

## Reading SMPP receipts as a REST caller

If your integration does not hold an SMPP bind, you still see the same canonical outcomes — the mode split decides which surface carries each receipt. Per credential, the `dlrMode` field (from the [SMPP edge model](/concepts/smpp-edge-model)) is:

| `dlrMode`        | Where the receipt leaves Orbit                                                           |
| ---------------- | ---------------------------------------------------------------------------------------- |
| `bind` (default) | Delivered back over your SMPP session as a `deliver_sm` PDU. Nothing is POSTed anywhere. |
| `webhook`        | POSTed as JSON to your `dlrWebhookUrl`. Nothing is returned on the bind.                 |
| `both`           | Both of the above, for the same receipt.                                                 |

A REST consumer receiving a receipt on the webhook leg reads the same canonical status, the mapped Orbit status, and (when they exist) the same `stat` and `err` tokens verbatim — the webhook leg and the bind leg describe one event identically, because both start from the same canonical state. Choose `webhook` or `both` if your SMPP client can be offline while receipts arrive, or if you want receipts without a bind at all.

## Related

* [Delivery lifecycle](/concepts/delivery-lifecycle) — what each message status means and who advances it.
* [DLR and MO gateway pipeline](/concepts/dlr-and-mo-pipeline) — how carrier receipts and inbound messages cross into Orbit.
* [Send-side DLR model](/concepts/send-side-dlr-model) — the canonical vocabulary the two planes encode into and out of.
* [SMPP edge model](/concepts/smpp-edge-model) — the DLR delivery modes, bind credentials, and by-which a receipt leaves Orbit on the SMPP side.
* [Message status transition rules](/concepts/message-status-dag) — the allowed status transitions every consumer must agree on.
* [Webhook delivery semantics](/concepts/webhook-delivery-semantics) — how status events are delivered and retried.
