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

# Ciclo de vida de entrega: de queued a delivered y los estados intermedios

> Cómo un mensaje saliente pasa de queued a sent y luego a delivered (o a un resultado de fallo), quién avanza cada transición, cómo los acuses de entrega (DLR) la impulsan y las particularidades por canal que necesitas antes de gestionar tu primer webhook.

# Ciclo de vida de entrega

Todo mensaje saliente que envías a través de Orbit lleva un campo `status` que avanza a medida que el mensaje va desde tu llamada a la API hacia el dispositivo del destinatario — o hacia un resultado de fallo. Esta página explica ese autómata de estados a nivel conceptual: qué significan los estados, qué impulsa cada transición y dónde están las particularidades por operador. Léela antes de suscribirte a tu primer webhook o de ramificar tu integración en resultados de mensajes.

La semántica por estado, la tabla completa de transiciones y el mapa de eventos de webhook están en la [referencia del ciclo de vida de estados de mensajes](/api-reference/messages-status-lifecycle); el esquema de respuesta para leer el estado actual de un mensaje está en la [referencia de la API de Messaging](/api-reference/endpoints/messaging). Esta página une ambas a un nivel superior.

## El camino feliz

Un mensaje que tiene éxito de punta a punta pasa por:

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

Cada transición la avanza un actor distinto — ninguna parte individual ve todo el recorrido:

| Transición         | Significado                                                        | Qué la avanza                                                                                                                                                                                                |
| ------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `pending → queued` | El registro existe y se coloca en la cola de envío.                | La ruta de envío, en la aceptación de `POST /messages/<channel>`. `pending` es un estado transitorio previo a la cola que rara vez observarás.                                                               |
| `queued → sending` | Un worker ha tomado el mensaje y lo está despachando al proveedor. | La canalización de envío al sacarlo de la cola.                                                                                                                                                              |
| `sending → sent`   | El proveedor acusó recibo del envío en el cable.                   | La ruta de envío, en el momento en que el proveedor acepta (un ACK de envío SMPP, un id de mensaje del proveedor, un id de entrega de Meta — cualquiera que sea la aceptación al nivel de cable del canal). |
| `sent → delivered` | El operador confirmó la entrega al destinatario.                   | Un acuse de entrega (DLR) que vuelve del operador, gestionado por la canalización de DLR.                                                                                                                    |
| `delivered → read` | El destinatario lo abrió.                                          | Un DLR de acuse de lectura, en canales que los emiten (WhatsApp, RCS, correo electrónico). Implica que el registro se entregó primero.                                                                       |

Dos actores están al margen de este camino y pueden sacar un registro de él:

* **El planificador de no-DLR.** Cuando un operador nunca devuelve un acuse de entrega, un planificador promueve `sent` a `submitted_no_receipt` tras una ventana de gracia por canal — véase [Confirmado por operador vs. centinela intermedio de cable](#confirmado-por-operador-vs-centinela-intermedio-de-cable).
* **Tú (el operador).** Cancelar un mensaje programado aún no enviado lo mueve a `cancelled`, un estado terminal que ninguna llamada al proveedor y ningún DLR tocan jamás. Los envíos de sandbox se resuelven en `test_sent`, un estado terminal alcanzado antes de cualquier despacho al proveedor. Borrar un registro mueve cualquier estado terminal a `deleted`, tras lo cual nada puede tocarlo.

Un mensaje programado para el futuro espera en `scheduled` hasta su hora de disparo y luego se une a la cola. Solo puede salir de `scheduled` de tres maneras: promovido a `queued` en la hora de disparo, `cancelled` por ti, o `expired` si su ventana de validez vence antes del envío.

## Confirmado por operador vs. centinela intermedio de cable

La distinción que más importa para informes y conciliación es si un estado es un **resultado confirmado por operador** o un **centinela intermedio de cable**:

* `delivered` y `read` están confirmados por operador. Llegó un DLR real; el propio operador afirmó el resultado.
* `submitted_no_receipt` es intermedio de cable. Significa «el envío fue aceptado y no volvió ningún acuse dentro de la ventana de gracia». Se etiqueta como intermedio (`state_class: "intermediate"`, `is_terminal: false`) en todo webhook, porque un DLR de `delivered`, `read` o fallo genuino puede llegar después y sobrescribirlo.

Trata `submitted_no_receipt` como *resultado desconocido*, no como entrega. Que «desconocido» se acerque a positivo o sea realmente ambiguo depende del canal — véase [Particularidades por canal](#particularidades-por-canal).

`expired` es la contrapartida en el otro lado: llegó un DLR, pero tan tarde que la ventana de acuse ya se había cerrado. El resultado es indeterminable y el registro queda cerrado; `expired` se distribuye a los suscriptores como un evento `message.failed`.

## Particularidades por canal

* **Canales DM de Meta (Instagram, Messenger).** La API de envío de Meta nunca emite acuses de entrega. El envío sale de Orbit como `sent`, se marca `no_dlr_channel` en los metadatos del mensaje y pasa a `submitted_no_receipt` a los 5 minutos. Para un destinatario con opt-in, Meta garantiza la entrega en la aceptación — así que en estos canales `submitted_no_receipt` se comporta como una señal funcional de entrega, y los metadatos del mensaje llevan `no_dlr_channel: true` para que puedas distinguir ese caso.
* **Canales con respaldo SMPP (SMS, MMS, voz, fax, RCS).** La ventana de gracia es de 30 minutos. Aquí `submitted_no_receipt` es realmente ambiguo: el dispositivo puede haber recibido el mensaje sin que se informe acuse, el operador puede no enviar nunca acuses en esa ruta, o el acuse puede haberse perdido en tránsito. Vigila esta tasa por separado de tu tasa de entregados — una proporción creciente de `submitted_no_receipt` en un destino apunta a una ruta no cooperativa o a una ruta de acuses rota, y merece investigación en cualquier caso.
* **Correo electrónico.** Añade un resultado de fallo que otros canales no tienen: `bounced`, cuando el servidor de correo receptor rechaza el mensaje. Bounced cuenta contra tu tasa de fallos terminales igual que `failed`, pero es un estado distinto para que puedas separar rechazos del lado del destinatario de los del lado del proveedor.
* **Cancelación del operador.** `cancelled` solo es alcanzable por ti — mediante `POST /messages/:id/cancel` en un mensaje no enviado. Ningún operador lo escribe jamás y, en consecuencia, no dispara **ningún evento de webhook**; nada notifica a un suscriptor de una cancelación. Consulta `GET /messages/:id` si expones la cancelación en tu propia interfaz y necesitas observarla.
* **Sandbox / modo de prueba.** Los envíos de prueba se resuelven en `test_sent` antes de cualquier despacho al proveedor. Disparan `message.sent` con `status: "test_sent"` y `metadata.test_mode: true`, así que un suscriptor debe ramificar en `metadata.test_mode` para mantener el tráfico de sandbox fuera del tratamiento de producción.

## En qué ramificar

Las integraciones deben conmutar solo en campos legibles por máquina, nunca en etiquetas de presentación:

* **`status`** — el estado actual del mensaje. Este es el punto de ramificación principal. Gestiona el conjunto completo: además de los estados del camino feliz y de fallo comunes, no olvides `cancelled`, `test_sent`, `submitted_no_receipt` y `bounced`.
* **`metadata.classified_error_code`** — presente en fallos terminales; la categoría de fallo normalizada y legible por máquina. Combínala con el `error_code` / `error_message` en crudo en webhooks `message.failed` cuando quieras la redacción del propio operador.
* **`metadata.no_dlr_channel`** — presente en registros de DM de Meta; te dice que un `submitted_no_receipt` es el caso Meta con entrega garantizada, no el caso SMPP ambiguo.
* **`state_class` / `is_terminal`** — en webhooks de ciclo de vida. `is_terminal: true` (equivalentemente `state_class: "terminal"`) significa que el resultado es final; `submitted_no_receipt` informa `is_terminal: false` precisamente para que no cierres el expediente sobre él.

## Errores comunes

1. **Tratar `message.created` como aceptación.** `message.created` dispara en el momento en que el registro se pone en cola, antes de cualquier llamada al proveedor. Un rechazo síncrono (un 4xx en el envío, o un `rejected`/`failed` inmediato) deja ese evento entregado igualmente. Combínalo con `message.sent` antes de concluir que el mensaje salió.
2. **Suponer que `delivered` es inmutable.** Operadores en algunas rutas emiten un acuse de entrega y luego una corrección minutos después — algunos operadores indios y brasileños hacen esto. Orbit lo respeta: el registro puede moverse `delivered → undelivered` o `delivered → failed`. Si copias estados en tu propio almacén de datos, aplica las actualizaciones de forma idempotente por id de mensaje en vez de ignorar transiciones de un mensaje que ya marcaste como entregado.
3. **Esperar un webhook en `cancelled`.** Nunca llegará — la cancelación se origina en tu propia llamada a la API, no en un callback del operador, así que la plataforma no emite evento para ella. El registro simplemente queda en `cancelled` hasta que lo borres.
4. **Meter `submitted_no_receipt` en el saco de fallos.** Llega en el tipo de evento `message.failed` por razones de transporte (no hay evento dedicado), pero el `status` de su carga es `submitted_no_receipt` con `is_terminal: false`. Ramifica en `data.status`, no en el tipo de evento, y mantenlo fuera de tus métricas de fallos duros.
5. **Esperar un evento terminal por mensaje.** Un mensaje puede emitir `message.failed` con `status: "submitted_no_receipt"` y luego `message.delivered`, cuando un acuse de operador lento finalmente llega dentro de la ventana de llegada tardía. Desduplica y concilia por `message_id`, dejando que el evento posterior prevalezca.

Una vez claro el autómata de estados, las cargas de webhook por estado están en la [referencia de eventos de webhook](/reference/webhook-events), y la semántica de estados en el nivel de endpoint está en la [referencia del ciclo de vida de estados de mensajes](/api-reference/messages-status-lifecycle).
