> ## 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 three success statuses: delivered vs submitted_no_receipt vs sent

> How to read the three outbound statuses that can all mean 'the send succeeded so far': terminal delivered (carrier/read confirmed), the wire-intermediate submitted_no_receipt sentinel (promoted by the no-DLR scheduler past the per-channel grace window), and sent (wire-acknowledged, in flight). Includes the promotion rules, the no_dlr_channel marker, and the failure modes you must not confuse.

# The three success statuses: `delivered` vs `submitted_no_receipt` vs `sent`

Three outbound statuses can all describe a message that, in the reader's
mind, "went fine" — `sent`, `submitted_no_receipt`, and `delivered` — yet
they encode three very different grades of success:

* `delivered` — terminal, read-confirmed by the receiver (carrier DLR or
  read receipt).
* `submitted_no_receipt` — an intermediate sentinel promoted by the
  no-DLR scheduler once the per-channel grace window closes with no
  receipt.
* `sent` — the earliest wire acknowledgment; the message is in flight.

The [delivery lifecycle](/concepts/delivery-lifecycle) page names all
three, the [status map](/concepts/message-status-map) ranks them on the
transition DAG, and the
[DLR two-plane model](/concepts/dlr-model-two-planes) page splits the
carrier plane from the send-side plane. None of them collected the
distinction per group: this page does. Read it before you wire a webhook
handler, a KPI, or a reconciliation table that treats "success" as a
single boolean.

## The three buckets — terminal vs wire-intermediate on a promotion boundary

Every status you can read off an outbound row sits on one side of a
promotion boundary: a **terminal** status the receiver (carrier or
recipient) asserted, or a **wire-intermediate** status that names how
far the submission travelled but not the outcome.

| Status                 | Class                               | What it proves                                                                                                      | Why it is not the same thing as the other two                                                                                                                                                                                                                                     |
| ---------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `delivered`            | terminal                            | A DLR arrived — carrier `DELIVRD` / a read receipt — stamped `delivered_at` (or `read_at` when `read` advanced it). | Genuine terminal success. Later corrections may apply on some routes (`delivered → undelivered` / `failed`), but no in-flight state remains.                                                                                                                                      |
| `submitted_no_receipt` | wire-intermediate sentinel          | The no-DLR scheduler promoted `sent` after the grace window closed with **no receipt**; `is_terminal: false`.       | A sentinel meaning "submission accepted; outcome unknown." A late genuine receipt (`delivered` / `read` / a failure status) still supersedes it. On Meta DM it is a *functional delivery signal* (Meta guarantees delivery on accept) — on other channels it is honest ambiguity. |
| `sent`                 | wire-intermediate (earliest origin) | The provider acknowledged the submission on the wire (SMPP ACK, provider id, Meta hand-off).                        | In flight. The wait for a receipt has just begun; nothing terminal has been asserted — a genuine DLR, a failure callback, or the no-DLR promotion can still land.                                                                                                                 |

The row is stamped each transition by the single writer
`messages.messages` row, and reporting distills the three into two
canonical ladders: numeric *delivery rate* and event *state-class* —
see [Failure modes](#failure-modes) for why reducing them blindly poisons
both.

Recasting the three buckets with the reader's mental model:

* Terminal success says "a receiver asserted the outcome."
* `submitted_no_receipt` says "the scheduler bounded the wait; the
  outcome is still an open variable."
* `sent` says "the wire ACK came back; the receiver has not spoken yet."

## The promotion rules — the no-DLR safety net, the per-channel grace windows, and the partial index

A background scheduler (the no-DLR transition net in the webhook worker)
promotes `sent` rows through a two-step sweep per tenant every 5-minute
tick: a bounded `SELECT ... WHERE status = 'sent' AND <channel gate> AND
sent_at < NOW() - <grace>` (capped, `FOR UPDATE SKIP LOCKED`) followed
by a second `UPDATE ... WHERE id IN (...) RETURNING ...` that flips the
row. The relevant gates:

| Channels                            | Grace window         | No-receipt resolution                   | Why the window differs                                                                                                                                                                                          |
| ----------------------------------- | -------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `instagram`, `messenger`            | 5 minutes            | `submitted_no_receipt`                  | Meta's Send API never emits a wire DLR; the row is `no_dlr_channel:true` from insert on.                                                                                                                        |
| `sms`, `mms`, `voice`, `fax`, `rcs` | 30 minutes           | `undelivered` (honest terminal failure) | Real-DLR channels: a carrier `DELIVRD` normally lands within 1–3 minutes; 30 min absorbs store-and-forward without fabricating a delivered-equivalent.                                                          |
| `email`                             | 60 minutes           | `submitted_no_receipt`                  | Resend's Svix envelope retries for 24h; the reconciliation scheduler heals the true outcome later. Rows carrying `metadata.soft_bounced_at` are excluded — a soft bounce is a negative receipt, not no receipt. |
| `viber`, `whatsapp`                 | 24 hours             | `undelivered`                           | Report-capable channels that did not answer within a day; recovery window deliberately generous.                                                                                                                |
| every other channel                 | 23 hours (catch-all) | `undelivered`                           | The catch-all ages conservatively *below* the 24h probe floor so the net converges before the data-integrity probe flags it.                                                                                    |

The two-select promotion sweep is bounded by a partial index,
`idx_messages_no_dlr_channel(nodlr_channel) WHERE no_dlr_channel = TRUE`,
which keys only the TRUE rows (currently Meta DM traffic) so the sweep
cost stays flat regardless of overall message volume. Every row entering
the `messages` table carries the flag: the sole canonical message writer
(`MessagesRepository.create`) derives `no_dlr_channel = (channel IN
('instagram','messenger'))` at the insert seam and binds it explicitly,
so a row either joins the index cleanly or provably stays out of it.

A row never runs *from nothing*: the `sent → <grace>` predicate reads
`sent_at` (not `created_at`), so a retry that lands late restarts its
window fairly. Per promotion, the random receiver of dashboard parity
hooks (SSE, webhooks, cache-bust, fallback escalation) fans out exactly
like a real DLR flip would. See
[the Scheduler section of the cross-check](#the-promotion-rules) note
below — the schedule is part of the longevity contract, not an
implementation detail you timing-tune against.

## Why `submitted_no_receipt` is a sibling-but-skipped status on every consumer path

Five waypoints read `messages.status` without advancing it, and each of
them deliberately treats the sentinel differently:

1. **The canonical writer.** The single writer that creates or merges a
   `messages` row at send time derives and binds `no_dlr_channel`, but
   never writes `submitted_no_receipt`; promotion is the scheduler's job.
2. **The no-DLR scheduler.** The five promotion UPDATEs transition
   `sent → <sentinel or undelivered>` per channel grace, and the
   reconciliation UPDATE heals a sentinel row back to a genuine receipt.
3. **The DLR receiver path.** A genuine late receipt flips the sentinel
   back to `delivered`/`read` (weight 4/5 beats 2.5) — a forward-only
   correction, never a regression.
4. **The data-integrity probe.** `status='sent' AND no_dlr_channel=false
   AND sent_at < NOW()-25h` rows flag the gap class; Meta-DM-flagged rows
   are excluded so a healthy safety net never ring-fences a false
   positive.
5. **The unified-inbox/receipt planes.** The DLR chokepoint and the
   scheduler's parity fan-out emit `message.receipt`/`message.status_changed`
   frames so the inbox thread and dashboard lists stop freezing at
   "Sent" or "Submitted (no receipt) once the row converges.

Across those waypoints, four anchors must never filter the sentinel:
**webhook events** (the sentinel arrives on `message.failed` because no
dedicated event exists — branch on `data.status`), **route trace** (`GET
/api/v1/messages/:id/trace` renders the sentinel as its own event with
`stage_state` intact), **budgets** (the wallet debit stays where the
send path charged it except the scheduler's explicit undelivered refund;
the sentinel itself is not a refund trigger), and **fallback decisions**
(the cross-channel cascade trigger relies on the scheduler's aged-to-
`undelivered` flip, not on the sent/ACK origin, which must not be
skipped).

Metadata gate — `no_dlr_channel`: `true` (Meta DM) means the sentinel is
a *functional delivery* (Meta guarantees accept-delivery on opted-in
recipients); `false` means ordinary honest ambiguity.

## Failure modes a reader must not confuse

Two failure modes share the postcard-status `sent` surface and only one
of them is a bug:

* **Low-DLR** — a report-capable channel whose carrier *should* return
  receipts but does not (a non-cooperating route, a dropped Jasmin
  `deliver_sm` PDU, an endpoint drift on email). The scheduler ages the
  row to `undelivered` and, where a pre-send charge is recorded, frankly
  reverses it. Track it in your failure KPIs; it is a new-shipment
  ambiguity class.
* **No-DLR** — a channel (currently Instagram / Messenger, both flagged
  `no_dlr_channel=true`) where *by contract no receipt ever arrives*.
  The scheduler promotes the row to `submitted_no_receipt`; reporting
  counts it on the delivered numerator for exactly those two channels
  (channel-aware
  `status IN (MESSAGE_DELIVERED_STATUSES_UNCONDITIONAL) OR (status =
  'submitted_no_receipt' AND channel IN (META_DM_CHANNELS))`). Do not
  confuse the second class with the first: a no-DLR channel is not
  broken, and a low-DLR channel will not fix itself by being promoted
  to the sentinel.

Guard for both: annotate any "no DLR" tax with `channel`, and only then
interpret the sentinel. The data-integrity probe's `no_dlr_channel=false`
predicate and the unified `META_DM_CHANNELS` set exist to keep the two
classes apart on the same field.

## Related pages

* [Delivery lifecycle](/concepts/delivery-lifecycle) — every status
  meaning and who advances it
* [Message status map](/concepts/message-status-map) — the four-owner
  index over the status pages and the transition DAG
* [Message status DAG](/concepts/message-status-dag) — allowed
  transitions, weights, and floors
* [The DLR model: two planes](/concepts/dlr-model-two-planes) — which
  plane each receipt crossed
* [Message route-trace](/concepts/message-route-trace) — per-message
  event timeline including the `submitted_no_receipt` event
* [Troubleshooting: message sent but no delivery receipt](/troubleshooting/submitted-no-receipt) —
  when a row sits at `sent` or the sentinel with no terminal event
