Webhooks — Eventos de mensaje
receive_message, send_message, send_bot_message, status_message y los eventos de WhatsApp Flows.
Es la familia que cubre la conversación en sí: lo que entra, lo que sale y qué pasó con
cada envío.
| Evento | Cuándo se dispara |
|---|---|
receive_message | Entra un mensaje de un contacto |
send_message | Sale un mensaje desde la plataforma, la app o la API |
send_bot_message | Un chatbot responde. Payload anidado, ver abajo |
status_message | Cambia el estado de un mensaje que usted envió |
wa_flow_sent | Se envía un WhatsApp Flow |
wa_flow_response_received | El contacto responde un Flow |
Los seis se filtran por línea: solo recibe los de los números que marcó al configurar el
webhook.
Campos garantizados
Estos campos están siempre, con null cuando no hay valor:
| Campo | Tipo | Notas |
|---|---|---|
id | int | Id del mensaje en Wasapi |
user_id | int | Cuenta dueña |
sender_id | int | null | Agente que lo envió. null en los entrantes y en los que envía un bot, una campaña o la API |
from_id | int | La línea de WhatsApp por la que entró o salió |
message | string | null | El texto |
type | string | in (entrante), out (saliente), system (nota interna del sistema) |
message_type | string | Ver la tabla de tipos |
wa_id | string | ⚠️ La clave de conversación: teléfono o BSUID. Ver Identidad del contacto |
wam_id | string | null | Id del mensaje en Meta. Es la mejor llave para descartar duplicados |
context_wam_id | string | null | wam_id del mensaje citado, si es una respuesta |
status | string | sent, delivered, read, played, failed, n/a |
caption | string | null | Texto que acompaña a un archivo |
filename | string | null | Nombre del archivo adjunto |
data | string | ⚠️ Campo interno. No lo parsee — ver la advertencia abajo |
reactions | string | null | Reacciones al mensaje |
origin | string | null | Por dónde se originó: web, app, api, chatbot, campaign, webhook, wa_business, survey, make, n8n, system |
created_at | string | |
updated_at | string | |
deleted_at | string | null | |
bsuid | string | null | Ver Identidad del contacto |
wa_username | string | null | |
version | int | 1 |
dataes un campo interno de Wasapi, no JSON.Viene vacío en la mayoría de los mensajes y, cuando trae algo, su formato no forma parte
del contrato y puede cambiar sin aviso. No dependa de él. Para adjuntos usefilename
ycaption.
Valores de message_type
message_type| Valor | Qué es |
|---|---|
text | Texto |
image, audio, video, document, sticker | Multimedia |
template | Plantilla aprobada por Meta |
interactive | Mensaje con botones o lista |
button | Respuesta del contacto a un botón |
cta_url | Mensaje con botón de enlace |
flow_response | Respuesta de un WhatsApp Flow |
system | Nota interna (asignación de agente, cierre de conversación…) |
unsupported | Tipo que WhatsApp no soporta reenviar |
Pueden aparecer valores nuevos: Meta agrega tipos. Trate un valor desconocido como genérico
en vez de rechazar el evento.
Campos extra, según el camino
Además del piso garantizado, cada evento puede traer campos propios. No están declarados y
pueden faltar: trátelos como opcionales.
| Campo extra | Aparece en |
|---|---|
error_code, error_message | status_message cuando status es failed |
context_message, context_type | Respuestas a un mensaje citado |
Ejemplo — receive_message
receive_message{
"event": "receive_message",
"data": {
"id": 900001,
"user_id": 1234,
"sender_id": null,
"from_id": 42,
"message": "Hola, quiero información de precios",
"type": "in",
"message_type": "text",
"wa_id": "573001234567",
"wam_id": "wamid.HBgMNTczMDAxMjM0NTY3FQIAEhgU…",
"context_wam_id": null,
"status": "n/a",
"caption": null,
"filename": null,
"data": "",
"reactions": null,
"origin": "webhook",
"created_at": "2026-08-07T15:04:11.000000Z",
"updated_at": "2026-08-07T15:04:11.000000Z",
"deleted_at": null,
"bsuid": null,
"wa_username": null,
"version": 1
}
}Ejemplo — status_message con fallo
status_message con fallo{
"event": "status_message",
"data": {
"id": 900002,
"user_id": 1234,
"from_id": 42,
"wa_id": "573001234567",
"wam_id": "wamid.HBgMNTczMDAxMjM0NTY3FQIAERgS…",
"type": "out",
"message_type": "template",
"status": "failed",
"error_code": 131047,
"error_message": "Re-engagement message",
"bsuid": null,
"wa_username": null,
"version": 1
}
}status_message es el único evento que le avisa que un envío no llegó. Si le importa la
entrega, actívelo.
send_bot_message tiene el payload anidado
send_bot_message tiene el payload anidadoEs la excepción de la familia: trae dos mensajes, el del contacto y la respuesta del bot.
{
"event": "send_bot_message",
"data": {
"user": {
"id": 900003,
"type": "in",
"message": "1",
"wa_id": "573001234567",
"bsuid": null,
"wa_username": null,
"...": "el resto del piso garantizado"
},
"bot": {
"id": 900004,
"type": "out",
"message": "Perfecto, le comparto el catálogo 👇",
"wa_id": "573001234567",
"bsuid": null,
"wa_username": null,
"...": "el resto del piso garantizado"
},
"version": 1
}
}Cada uno de los dos objetos cumple el piso garantizado por su cuenta. version va en la
raíz, no dentro de user ni de bot.
// El acceso NO es data['message'], sino:
$textoDelContacto = $data['user']['message'] ?? null;
$respuestaDelBot = $data['bot']['message'] ?? null;Notas de comportamiento
receive_message no distingue de qué contacto viene por sender_id
receive_message no distingue de qué contacto viene por sender_idsender_id es siempre null en los entrantes: identifica al agente que envía, no al
contacto. Para saber de quién es el mensaje use wa_id (más bsuid / wa_username).
Los mensajes de tipo system también disparan eventos
system también disparan eventosWasapi registra notas internas —asignación de agente, cierre de conversación— como mensajes
con type: "system" y message_type: "system". Si su integración solo quiere conversación
real, fíltrelos:
if ($data['type'] === 'system') {
return; // nota interna, no es un mensaje de la conversación
}origin le dice quién originó el mensaje
origin le dice quién originó el mensajeÚtil para no reprocesar sus propios envíos: un mensaje que usted mandó por la API vuelve
como send_message con origin: "api".
Ver también
- Identidad del contacto —
wa_idno siempre es un teléfono - Introducción — reintentos e idempotencia
Updated 17 days ago
