Estados de mensaje
Saber qué pasó con cada mensaje que mandaste —en cola, enviado, entregado, leído o falló—, por el aviso message.status_changed o consultando el mensaje.
Sabé qué pasó con cada mensaje que mandaste: los de tu clave y también los del chat, las automatizaciones y los asistentes de IA. Hay dos caminos, que dan lo mismo: el aviso message.status_changed, que te llega solo, y Ver un mensaje, que consultás cuando querés.
Los estados
status | Qué significa |
|---|---|
queued | Registrado y en cola: está por salir. En cola no es enviado. Solo aparece al consultar el mensaje; no genera aviso. |
sent | El proveedor aceptó el mensaje. Aceptado no es entregado. |
delivered | Llegó al teléfono del contacto. |
read | El contacto lo abrió (si tiene activadas las confirmaciones de lectura). |
failed | No salió o no se pudo entregar. Viene con error (abajo). |
null | El resultado no se conoce: el proveedor no confirmó ni rechazó el envío, o el envío se interrumpió. Puede haber llegado y no va a cambiar de estado. |
Lo normal es queued → sent → delivered → read. Cada estado se informa una sola vez por mensaje y nunca uno que retroceda: si llegan varios avisos juntos se informa el más avanzado, y un aviso atrasado que no cambia el estado no genera evento.
Cuando falla
error trae code, title y reason, solo cuando status es failed:
- Lo rechazó el proveedor:
codees su código numérico ytitlesu título (cualquiera de los dos puede sernull);reasonesnull. - Lo frenó Funnelchat antes de que saliera (solo en mensajes enviados con clave):
reasontrae el motivo,codeesnullytitlees un texto llano en el idioma de la cuenta. No salió: reintentar es un intento nuevo y no lo duplica.
error.reason | Qué hacer |
|---|---|
device_unavailable | Reconectá el número en Funnelchat y mandá de nuevo. |
insufficient_balance | Recargá tu billetera de WhatsApp. |
window_closed | Mandá una plantilla aprobada. |
recipient_unreachable | Revisá el teléfono del contacto. |
template_not_found, template_unresolved_placeholder | Corregí la plantilla o sus variables. |
not_queued, not_sent | Volvé a intentarlo. |
Más de una hora en queued: un mensaje cuyo envío no empezó dentro de la hora ya no sale. Pasada la hora sin resultado, su status es null, o failed con reason: "not_sent" si su envío llega tarde (y entonces sí te llega su aviso).
El aviso: message.status_changed
{
"id": "evt_000000000000000000000014",
"type": "message.status_changed",
"version": 1,
"occurred_at": "2026-10-01T12:00:00Z",
"data": {
"message_id": 1001,
"contact_id": 2001,
"device_id": 4521,
"status": "failed",
"previous_status": "sent",
"error": { "code": 131047, "title": "Re-engagement message", "reason": null },
"occurred_at": "2026-10-01T12:00:00Z",
"client_reference": "pedido-1001"
}
}| Campo | Qué es |
|---|---|
message_id | El id del mensaje: el mismo id que devuelve el envío y el historial del contacto. |
contact_id, device_id | El contacto y el número por el que salió (el mismo device_id con el que mandás). |
status | sent, delivered, read o failed. |
previous_status | El estado anterior. Es null si es el primer aviso del mensaje, si repite el estado que ya tenía o si no se pudo leer. |
error | Solo cuando status es failed: code, title y reason (arriba). |
occurred_at | Cuándo registró Funnelchat el estado, en UTC. |
client_reference | La referencia que mandaste al enviar, o null. Sirve para relacionar el aviso con tu envío aunque la llamada de envío no haya respondido. |
- Qué mensajes lo emiten: los que se mandan a un contacto desde el chat, una automatización o un asistente de IA, incluidos los de tu clave. No se emite para mensajes de difusiones ni para mensajes recibidos.
- Un mensaje que no llegó a salir no tiene avisos, salvo uno enviado con clave: ese recibe un único aviso
failed, conprevious_statusennull, suerror.reasony suclient_reference. Un mensaje con resultado desconocido (statusnull) no recibe aviso. - En cuentas con mucho tráfico se descartan los eventos que superan el límite de entrega por minuto de la cuenta. Si necesitás el estado de un mensaje concreto, consultalo.
- El
iddel evento es el mismo en todos los reintentos de una entrega: si te llega dos veces, descartá el repetido porid.
Cómo suscribirte
Igual que a cualquier evento, con una suscripción que tenga tu URL y message.status_changed en events (Webhooks). Pedilo de forma explícita: es el único evento de mensajes que hay que nombrar siempre. Para elegirlo, la clave necesita webhooks:write y además conversations:read, el permiso de lectura de su área. El paso a paso está en Armá tu propio flujo.
El estado en el historial
El historial de un contacto trae el estado de cada mensaje en status, pero la especificación lo publica como un número, sin el nombre de cada valor. Para el estado con nombre, consultá Ver un mensaje con el id, o usá el aviso.
Probalo acá
El historial de un contacto, con tu clave. Es una lectura: no manda nada.
Permiso requerido: conversations:read
httpAuthorizationBearer <clave>contactId*integercursor?stringupdated_since?stringperPage?integer1 <= value <= 10025OK
application/json- response
data*array<>links*meta*curl -X GET "https://example.com/contacts/0/messages"{ "data": [ { "chat_id": "string", "contact_id": 0, "content": [], "device_id": 0, "error_code": "string", "file": "string", "forwarded": 0, "group_id": 0, "id": 0, "origin": 0, "origin_id": 0, "own": 0, "reference_message": "string", "reference_message_uid": "string", "is_quotable": true, "status": 0, "temporal_uuid": "string", "text": "string", "time": "string", "type": 0, "uid": "string", "info": [], "user_id": 0, "deleted_at": "string", "deleted_scope": "string", "poll_results": "string", "event_rsvps": "string", "form_answers": "string", "reactions": [ null ], "file_original_name": "string", "client_reference": "string" } ], "links": { "first": "string", "last": "string", "prev": "string", "next": "string" }, "meta": { "path": "string", "per_page": 0, "next_cursor": "string", "prev_cursor": "string" }}