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

# DLR outcomes monitoring — reading the WARN classification

> How to read the platform's deliberate WARN classification for delivery-report outcomes: the plain-language WARN lines the no-receipt safety net emits, the per-lane counters behind them, and which of those WARNs are designed-benign versus actually actionable.

# DLR outcomes monitoring — reading the WARN classification

Orbit emits WARN log lines about delivery-report (DLR) outcomes on purpose, not by accident. A WARN from the no-receipt safety net means a lane handed a message to the carrier and no receipt crossed back inside that lane's grace window — the safety net aged the row to a terminal status instead of leaving it stuck at `sent`. Whether that WARN deserves a page depends on the lane it names, and this page teaches the split.

The concept pages define the data model — [Delivery lifecycle](/concepts/delivery-lifecycle) for the states, [The DLR model: two planes](/concepts/dlr-model-two-planes) for the planes, [DLR and MO gateway pipeline](/concepts/dlr-and-mo-pipeline) for the ingress chain. What none of them documents is the classification layer the application wires to its logs: this page is the operator-facing version of that behavior.

## 1. Where DLR WARNs surface

DLR WARNs come from the no-receipt safety net, a scheduled worker that protects the `sent` state from stalling. It emits its WARN on a per-lane basis at the end of each pass over your workspace, carrying the lane's counter name in the `metric` field. In the same pass it also performs the terminal-status update, so a lookup in the [Delivery Log](/guides/delivery-log) nearly always resolves the WARN's subject before you investigate further.

The webhook events you receive are downstream of the same transitions. The safety net's status updates fire the same customer webhook events a carrier receipt would:

* `message.delivered` — bookkeeping promotion to delivered (e.g. a read receipt arriving for a `sent` row, or a soft-bounce correction).
* `message.failed` — terminal no-receipt aging (`submitted_no_receipt` or `undelivered`).
* `message.read` — a read receipt promotion, where the channel supports it.

Subscribe to `message.delivered`, `message.failed`, `message.read` (and `message.sent` for the send-side ack) as you already do; when a WARN names one of the no-receipt counters, expect the matching `message.failed` (or `message.delivered`) event to describe the same row. Correlate on the `id` of the payload — the envelope created by the WARN path carries the same message-row id the API names. Event semantics are at-least-once, and a late genuine DLR within the buffer window re-corrects the row and fires the correcting event.

On Meta DM channels (Messenger, Instagram) the safety net flips to `submitted_no_receipt` after a 5-minute window — a deliberately short window because Meta guarantees delivery on accept. On SMPP-backed channels the window is 30 minutes. Viber and WhatsApp age to `undelivered` after a 24-hour DLR window. SMPP-bind submits stuck at `queued` age after the queue grace window, and an SMPP row prematurely aged can be re-pended back to a fresh window when DLRs arrive late.

## 2. The per-lane counters

Each WARN line names one of these counters in its structured `metric` field. Scrape them individually rather than reading the aggregate tick counters — the aggregate (`no_dlr_transition.rows_promoted`) lumps every lane together.

| Counter                                             | Lane                                      | What the WARN says                                                                                                                                                                                                |
| --------------------------------------------------- | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `no_dlr_transition.sms_no_receipt_promoted`         | SMS on the 30-minute window               | `No-DLR transition: scheduled/immediate SMS reached the grace window with no carrier DLR — aged to the terminal undelivered status (delivery could not be confirmed; a later genuine DLR still corrects the row)` |
| `no_dlr_transition.viber_whatsapp_aged_undelivered` | Viber/WhatsApp 24-hour DLR window         | `No-DLR transition: viber/whatsapp reached the 24-hour DLR window with no delivery status — aged to the terminal undelivered status (a later genuine delivered/read status still corrects the row)`               |
| `no_dlr_transition.smpp_queued_aged_undelivered`    | SMPP bind rows stuck at `queued`          | `No-DLR transition: SMPP bind submit reached the queued grace window with no carrier DLR — aged to the terminal undelivered status (a later genuine delivered/read status still corrects the row)`                |
| `no_dlr_transition.smpp_repend`                     | SMPP rows re-pended after premature aging | `No-DLR transition: SMPP-bind 'sent' rows re-pended to a fresh window instead of premature undelivered+refund (delaying the refund until the re-pend budget is exhausted; a genuine DLR still resolves them)`     |

Four auxiliary counters complete the picture, emitted even at zero so the metric's absence is itself alertable:

* `no_dlr_transition.rows_promoted` — total rows the pass promoted (all lanes).
* `no_dlr_transition.tenant_failures` — per-pass failure count (a pass-fail means a tenant's promotion batch rejected).
* `no_dlr_transition.tenant_skipped` — per-pass benign-skip count (transient infrastructure blips the pass deliberately demoted out of the WARN stream).
* `no_dlr_transition.refund_attempted` and `no_dlr_transition.backfill_residual` — refund companion counters on aged rows.

The healthy shape of every WARN counter is a flat zero. The unhealthy shape is a climb — and the climb is lane-specific, which is why the split matters for alerting.

## 3. Designed-benign vs actionable

Two of the four WARN classes are designed-benign at low rates; two are actionable. Classify by lane:

**Designed-benign at low rates — no-DLR-supported lanes.** Meta DM channels (Messenger, Instagram) are flagged `no_dlr_channel` in the data model because Meta's Send API never emits delivery receipts; the corner of the WARN stream where *viber/whatsapp* aging sits is the same family — a channel where the receipt stream is structurally thin, slow, or absent, and aging under its own window is the correct terminal answer. Viber and WhatsApp sends that sit past the 24-hour DLR window age to `undelivered` on the safety net's own window — the distinction from the benefited `no_dlr_channel` path is in which counter the WARN names, not in the WARN text. Route these to a dashboard-level signal or a long-window trend (a slow drift over days), not a page.

**Actionable — SMPP and queued lanes.** On SMPP-backed SMS a healthy carrier returns receipts well within 30 minutes, so a `no_dlr_transition.sms_no_receipt_promoted` WARN means the carrier leg is genuinely not returning receipts for those rows — page-appropriate at sustained rates, because the cause is on the route (the carrier, the SMPP bind, or both). The `smpp_queued_aged_undelivered` WARN names the invalid-MSISDN failure mode: the carrier never accepted the submit at all, so no receipt ever arrives. And `smpp_repend` marks rows the safety net prematurely aged before late receipts arrived — non-actionable in itself, but a sustained re-pend rate tells you your carrier's receipts are arriving after the 30-minute window the safety net protects.

The quadrant (cable versus no-DLR) the WARN names is the whole decision:

```text theme={null}
WARN names no_dlr_transition.sms_no_receipt_promoted     -> actionable
WARN names no_dlr_transition.smpp_queued_aged_undelivered -> actionable (list hygiene)
WARN names no_dlr_transition.viber_whatsapp_aged_undelivered -> benign (designed)
WARN names no_dlr_transition.smpp_repend                  -> signal (a route whose receipts arrive late)
```

## 4. Runbook: classifying a WARN

As tech ops, work one WARN like this:

1. **Read the `metric` field first.** It names the lane. Ignore the aggregate tick line (`No-DLR transition tick complete` is info-level and carries the per-tick totals — useful context, not the WARN).
2. **Confirm the transition happened.** Open the [Delivery Log](/guides/delivery-log) with the message id from the WARN's context (for example a `message.failed` event you received, or a row you got from the API). The row should already sit at `undelivered` (or `submitted_no_receipt` for the Meta DM path) — on the safety net's promotion the terminal webhook fired, so a subscriber you control already heard the outcome.
3. **Decide by lane.** Viber/whatsapp aging and the SMPP re-pend are treated as benign-by-design; SMS no-receipt and SMPP queued aging are actionable. Sustained non-zero on an actionable lane means escalate to the provider — for SMPP, the route or bind; for the queued lane, the audience list.
4. **Let the late-DLR buffer close it.** A late genuine receipt still corrects the row: the re-correction flips the message to `delivered` (or `read` where supported) and fires the correcting webhook. Nothing needs a human ack to unblock the recipient.

### Worked examples

**A) Viber aging (designed-benign).**

```json theme={null}
{
  "level": "warn",
  "metric": "no_dlr_transition.viber_whatsapp_aged_undelivered",
  "viberWhatsappAgedUndelivered": 3,
  "tenants": 12,
  "msg": "No-DLR transition: viber/whatsapp reached the 24-hour DLR window with no delivery status — aged to the terminal undelivered status (a later genuine delivered/read status still corrects the row)"
}
```

Three Viber sends aged out of a 12-tenant pass. The lane is no-receipt-supported by design; the aging is the correct terminal answer. Route: dashboard/trend, no page.

**B) SMS no-receipt (actionable).**

```json theme={null}
{
  "level": "warn",
  "metric": "no_dlr_transition.sms_no_receipt_promoted",
  "smsNoReceiptPromoted": 41,
  "tenants": 12,
  "msg": "No-DLR transition: scheduled/immediate SMS reached the grace window with no carrier DLR — aged to the terminal undelivered status (delivery could not be confirmed; a later genuine DLR still corrects the row)"
}
```

Forty-one SMS rows aged past the 30-minute window. On a healthy route the carrier returns receipts well inside 30 minutes; escalate the carrier leg. Route: page.

**C) SMPP re-pend (signal).**

```json theme={null}
{
  "level": "warn",
  "metric": "no_dlr_transition.smpp_repend",
  "smppRepended": 12,
  "tenants": 12,
  "msg": "No-DLR transition: SMPP-bind 'sent' rows re-pended to a fresh window instead of premature undelivered+refund (delaying the refund until the re-pend budget is exhausted; a genuine DLR still resolves them)"
}
```

Twelve SMPP bind submissions narrowly missed the aging window and were re-pended instead of aged — receipts arrived late but the refund stayed on hold. Route: watch the trend; a sustained re-pend rate means the carrier's receipts arrive after 30 minutes and you should treat that route's `undelivered` as "late verdict, not final failure" until the re-pend budget runs out.

## Compliance

Everything on this page is a tenant-owned control: which webhook events you subscribe to, which WARN metrics your log/metric sensors page on, and which lane you route where. The platform's safety net owns the transition itself; the alert-shape is yours. Outbound delivery stays within the Devotel softswitch contract described in [DLR and MO gateway pipeline](/concepts/dlr-and-mo-pipeline) — nothing on this page sends traffic anywhere new.

## Related

* [Delivery lifecycle](/concepts/delivery-lifecycle) — what each of the six canonical states means.
* [The DLR model: two planes](/concepts/dlr-model-two-planes) — which plane each receipt behaviour lives on.
* [DLR and MO gateway pipeline](/concepts/dlr-and-mo-pipeline) — how carrier receipts cross into Orbit.
* [Webhook delivery semantics](/concepts/webhook-delivery-semantics) — retry and ordering for the events this WARN path fires.
* [Delivery Log guide](/guides/delivery-log) — the dashboard surface you confirm the transition on.
* [Webhook events catalogue](/webhooks/events) — event names, including `message.delivered` / `message.failed` / `message.read`.
