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

# Zustellungslebenszyklus: von queued bis delivered und die Zustände dazwischen

> Wie eine ausgehende Nachricht von queued über sent nach delivered (oder einem Fehlerzustand) wandert, wer jede Transition auslöst, wie Zustellbestätigungen (DLRs) sie treiben und welche kanalspezifischen Sonderfälle Sie vor dem ersten Webhook kennen müssen.

# Zustellungslebenszyklus

Jede ausgehende Nachricht, die Sie über Orbit senden, trägt ein `status`-Feld, das sich weiterbewegt, während die Nachricht von Ihrem API-Aufruf zum Empfängergerät wandert — oder in einen Fehlerzustand endet. Diese Seite erklärt diesen Zustandsautomaten auf Konzeptebene: was die Zustände bedeuten, was jede Transition auslöst und wo die carrierspezifischen Besonderheiten liegen. Lesen Sie sie, bevor Sie Ihren ersten Webhook abonnieren oder Ihre Integration auf Nachrichtenergebnisse verzweigen.

Die Semantik pro Status, die vollständige Transitionstabelle und die Webhook-Ereigniszuordnung stehen in der [Referenz zum Nachrichtenstatus-Lebenszyklus](/api-reference/messages-status-lifecycle); das Antwortschema zum Lesen des aktuellen Zustands einer Nachricht in der [Messaging-API-Referenz](/api-reference/endpoints/messaging). Diese Seite verbindet beides auf höherer Ebene.

## Der Erfolgspfad

Eine Nachricht, die durchgehend erfolgreich ist, durchläuft:

`pending → queued → sending → sent → delivered → read`

Jede Transition wird von einem anderen Akteur ausgelöst — keine einzelne Partei sieht den gesamten Ablauf:

| Transition         | Bedeutung                                                                 | Was sie auslöst                                                                                                                                                        |
| ------------------ | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pending → queued` | Der Eintrag existiert und wird in die Sendewarteschlange gestellt.        | Der Sendepfad, bei der Annahme von `POST /messages/<channel>`. `pending` ist ein kurzlebiger Vor-Warteschlangen-Zustand, den Sie selten beobachten werden.             |
| `queued → sending` | Ein Worker hat die Nachricht übernommen und übergibt sie an den Provider. | Die Sendepipeline beim Entnehmen aus der Warteschlange.                                                                                                                |
| `sending → sent`   | Der Provider hat die Einreichung auf der Leitung bestätigt.               | Der Sendepfad, in dem Moment, in dem der Provider annimmt (ein SMPP-Annahme-ACK, eine Provider-Nachrichten-ID, eine Meta-Übergabe-ID — je nach kanalseitiger Annahme). |
| `sent → delivered` | Der Carrier hat die Zustellung an den Empfänger bestätigt.                | Eine eingehende Zustellbestätigung (DLR) vom Carrier, verarbeitet von der DLR-Pipeline.                                                                                |
| `delivered → read` | Der Empfänger hat sie geöffnet.                                           | Eine Lesebestätigungs-DLR, auf Kanälen, die sie ausgeben (WhatsApp, RCS, E-Mail). Setzt voraus, dass der Eintrag zuvor zugestellt war.                                 |

Zwei Akteure stehen neben diesem Pfad und können einen Eintrag aus ihm herausnehmen:

* **Der No-DLR-Scheduler.** Wenn ein Carrier nie eine Zustellbestätigung zurückliefert, hebt ein Scheduler den Zustand `sent` nach einem kanalspezifischen Zeitfenster auf `submitted_no_receipt` — siehe [Carrier-bestätigt vs. Zwischenzustand auf der Leitung](#carrier-bestätigt-vs-zwischenzustand-auf-der-leitung).
* **Sie (der Betreiber).** Ein Abbruch einer noch nicht gesendeten geplanten Nachricht setzt sie auf `cancelled`, einen Endzustand, den kein Provider-Aufruf und keine DLR je berührt. Sandbox-Sendungen lösen in `test_sent` auf, einen Endzustand, der vor jeder Provider-Übergabe erreicht wird. Das Löschen eines Eintrags setzt jeden Endzustand auf `deleted`, ab dem nichts mehr ihn ändern kann.

Eine für die Zukunft geplante Nachricht ruht in `scheduled` bis zu ihrem Auslösezeitpunkt und tritt dann der Warteschlange bei. Sie kann `scheduled` nur auf drei Arten verlassen: Hochstufung zu `queued` zur Auslösezeit, `cancelled` durch Sie oder `expired`, wenn ihr Gültigkeitsfenster vor dem Versand abläuft.

## Carrier-bestätigt vs. Zwischenzustand auf der Leitung

Die für Reporting und Abgleich wichtigste Unterscheidung ist, ob ein Zustand ein **vom Carrier bestätigtes Ergebnis** oder ein **Zwischenzustand auf der Leitung** ist:

* `delivered` und `read` sind carrier-bestätigt. Eine echte DLR ist eingegangen; der Carrier selbst hat das Ergebnis behauptet.
* `submitted_no_receipt` ist ein Zwischenzustand auf der Leitung. Er bedeutet „die Einreichung wurde angenommen, und innerhalb des Zeitfensters kam keine Bestätigung zurück". Er wird auf jedem Webhook als Zwischenzustand markiert (`state_class: "intermediate"`, `is_terminal: false`), weil eine echte `delivered`-, `read`- oder Fehler-DLR noch nachträglich eintreffen und ihn überschreiben kann.

Behandeln Sie `submitted_no_receipt` als *Ergebnis unbekannt*, nicht als Zustellung. Ob „unbekannt" eher positiv oder wirklich zweideutig ist, hängt vom Kanal ab — siehe [Kanalabhängige Besonderheiten](#kanalabhängige-besonderheiten).

`expired` ist das Gegenstück auf der anderen Seite: Eine DLR ist eingegangen, aber so spät, dass das Bestätigungsfenster bereits geschlossen war. Das Ergebnis ist nicht bestimmbar, und der Eintrag ist abgeschlossen; `expired` wird den Abonnenten als `message.failed`-Ereignis gemeldet.

## Kanalabhängige Besonderheiten

* **Meta-DM-Kanäle (Instagram, Messenger).** Metas Send-API gibt nie Zustellbestätigungen aus. Der Versand verlässt Orbit als `sent`, wird in den Nachrichtenmetadaten als `no_dlr_channel` markiert und kippt nach 5 Minuten auf `submitted_no_receipt`. Für einen opt-imierten Empfänger garantiert Meta die Zustellung bei Annahme — auf diesen Kanälen verhält sich `submitted_no_receipt` also wie ein funktionales Zustellsignal, und die Nachrichtenmetadaten tragen `no_dlr_channel: true`, damit Sie diesen Fall erkennen.
* **SMPP-gestützte Kanäle (SMS, MMS, Sprache, Fax, RCS).** Das Zeitfenster beträgt 30 Minuten. Hier ist `submitted_no_receipt` wirklich zweideutig: Das Gerät kann die Nachricht empfangen haben, ohne dass eine Bestätigung gemeldet wurde, der Carrier sendet auf dieser Route möglicherweise nie Bestätigungen, oder die Bestätigung wurde unterwegs verworfen. Verfolgen Sie diese Rate getrennt von Ihrer Zustellrate — ein steigender `submitted_no_receipt`-Anteil auf einem Ziel deutet auf eine nicht kooperierende Route oder einen defekten Bestätigungspfad hin und ist in jedem Fall untersuchenswert.
* **E-Mail.** Fügt ein Fehlerergebnis hinzu, das anderen Kanälen fehlt: `bounced`, wenn der empfangende Mailserver die Nachricht abweist. Bounced zählt wie `failed` zu Ihrer Endfehlerrate, ist aber ein eigener Status, damit Sie empfängerseitige von providerseitigen Ablehnungen trennen können.
* **Betreiberabbruch.** `cancelled` ist nur von Ihnen erreichbar — über `POST /messages/:id/cancel` auf einer unversendeten Nachricht. Kein Carrier schreibt ihn je, und dementsprechend löst er **kein Webhook-Ereignis** aus; nichts benachrichtigt einen Abonnenten von einem Abbruch. Fragen Sie `GET /messages/:id` ab, wenn Sie den Abbruch in Ihrer eigenen Oberfläche bereitstellen und beobachten müssen.
* **Sandbox / Testmodus.** Testsendungen lösen in `test_sent` auf, bevor irgendeine Provider-Übergabe stattfindet. Sie lösen `message.sent` mit `status: "test_sent"` und `metadata.test_mode: true` aus, sodass ein Abonnent auf `metadata.test_mode` verzweigen muss, um Sandbox-Verkehr aus der Produktionsverarbeitung fernzuhalten.

## Auf was zu verzweigen ist

Integrationen sollten nur auf maschinenlesbaren Feldern umschalten, nie auf Anzeigenamen:

* **`status`** — der aktuelle Zustand der Nachricht. Dies ist der primäre Verzweigungspunkt. Behandeln Sie die vollständige Menge: neben den Erfolgs- und üblichen Fehlerzuständen vergessen Sie nicht `cancelled`, `test_sent`, `submitted_no_receipt` und `bounced`.
* **`metadata.classified_error_code`** — bei Endfehlern vorhanden; die normalisierte, maschinenlesbare Fehlerkategorie. Kombinieren Sie sie mit dem rohen `error_code` / `error_message` auf `message.failed`-Webhooks, wenn Sie den Carrier-eigenen Wortlaut haben möchten.
* **`metadata.no_dlr_channel`** — auf Meta-DM-Einträgen vorhanden; besagt, dass ein `submitted_no_receipt` der zustellgarantierte Meta-Fall ist, nicht der zweideutige SMPP-Fall.
* **`state_class` / `is_terminal`** — auf Lebenszyklus-Webhooks. `is_terminal: true` (äquivalent `state_class: "terminal"`) bedeutet, dass das Ergebnis endgültig ist; `submitted_no_receipt` meldet `is_terminal: false` nur deshalb, damit Sie den Vorgang nicht endgültig abschließen.

## Häufige Fallstricke

1. **`message.created` als Annahme behandeln.** `message.created` feuert in dem Moment, in dem der Eintrags der Warteschlange beitritt, vor jedem Provider-Aufruf. Eine synchrone Ablehnung (ein 4xx beim Versand oder ein sofortiges `rejected`/`failed`) lässt dieses Ereignis trotzdem ausgeliefert. Kombinieren Sie es mit `message.sent`, bevor Sie schließen, dass die Nachricht rausging.
2. **Annehmen, `delivered` sei unveränderbar.** Carrier auf manchen Routen geben eine Zustellbestätigung aus und dann Minuten später eine Korrektur — manche indische und brasilianische Carrier tun dies. Orbit folgt dem: Der Eintrag kann von `delivered → undelivered` oder `delivered → failed` wandern. Wenn Sie Status in Ihren eigenen Datenspeicher spiegeln, wenden Sie Aktualisierungen idempotent über die Nachrichten-ID an, statt Transitionen für eine Nachricht zu ignorieren, die Sie bereits als zugestellt markiert haben.
3. **Auf einen Webhook zu `cancelled` warten.** Er wird nie kommen — der Abbruch stammt von Ihrem eigenen API-Aufruf, nicht von einem Carrier-Rückruf, sodass die Plattform dafür kein Ereignis ausgibt. Der Eintrag ruht schlicht auf `cancelled`, bis Sie ihn löschen.
4. **`submitted_no_receipt` in den Fehler-Eimer werfen.** Es trifft zu Transportgründen auf dem Ereignistyp `message.failed` ein (es gibt kein eigenes Ereignis), aber sein `status` in der Nutzlast ist `submitted_no_receipt` mit `is_terminal: false`. Verzweigen Sie auf `data.status`, nicht auf den Ereignistyp, und halten Sie es aus Ihren Hard-Failure-Metriken heraus.
5. **Erwarten, genau ein Endereignis pro Nachricht.** Eine Nachricht kann `message.failed` mit `status: "submitted_no_receipt"` und später `message.delivered` ausgeben, wenn eine langsame Carrier-Bestätigung innerhalb des Nachfolgefensters endlich eintrifft. Deduplizieren und rekoncizieren Sie nach `message_id` und lassen Sie das spätere Ereignis gewinnen.

Sobald der Zustandsautomat klar ist, finden Sie die Nutzlasten je Status in der [Webhook-Ereignisreferenz](/reference/webhook-events) und die Statussemantik auf Endpunktsebene in der [Referenz zum Nachrichtenstatus-Lebenszyklus](/api-reference/messages-status-lifecycle).
