Estructura de Payloads
Cada evento tiene una estructura de payload específica. Esta página documenta los más importantes.
message.received
El payload de message.received usa una estructura híbrida : el campo message contiene el mensaje exactamente como lo envía Meta , más IDs propios de Whaapy y extras de valor agregado.
Campos Base
Estos campos siempre están presentes:
Campo Tipo Descripción whaapy_message_idstring ID interno del mensaje en Whaapy conversation_idstring ID de la conversación en Whaapy contact_idstring ID del contacto en Whaapy messageobject Mensaje original de Meta (estructura idéntica a la API de Meta)contact_namestring Nombre del contacto (si está disponible)
Estos campos aparecen según el tipo de mensaje:
Campo Tipo Cuándo aparece Descripción media_urlstring image, video, audio, document, sticker URL con token (24h) para descargar media transcriptionstring audio Texto transcrito de la nota de voz
¿Por qué estructura híbrida? El campo message usa exactamente el formato de Meta, permitiéndote reutilizar código existente. Los extras de Whaapy (media_url, transcription) te ahorran llamadas adicionales a la API.
Ejemplos por Tipo de Mensaje
Texto
Audio
Imagen
Video
Documento
Ubicación
Contacto
Sticker
Flow Reply
{
"event" : "message.received" ,
"timestamp" : "2025-01-15T10:30:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"whaapy_message_id" : "msg-uuid" ,
"conversation_id" : "conv-uuid" ,
"contact_id" : "contact-uuid" ,
"message" : {
"from" : "5215512345678" ,
"id" : "wamid.HBgNNTIxNTUxMjM0NTY3OA==" ,
"timestamp" : "1705312200" ,
"type" : "text" ,
"text" : {
"body" : "Hola, necesito ayuda"
}
},
"contact_name" : "Juan Pérez"
}
}
{
"event" : "message.received" ,
"timestamp" : "2025-01-15T10:30:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"whaapy_message_id" : "msg-uuid" ,
"conversation_id" : "conv-uuid" ,
"contact_id" : "contact-uuid" ,
"message" : {
"from" : "5215512345678" ,
"id" : "wamid.HBgNNTIxNTUxMjM0NTY3OA==" ,
"timestamp" : "1705312200" ,
"type" : "audio" ,
"audio" : {
"mime_type" : "audio/ogg; codecs=opus" ,
"sha256" : "abc123def456..." ,
"id" : "media_id_de_meta" ,
"voice" : true
}
},
"contact_name" : "Juan Pérez" ,
"media_url" : "https://api.whaapy.com/messages/msg-uuid/media?token=..." ,
"transcription" : "Hola, quería preguntar sobre el precio del producto"
}
}
transcription solo aparece si la transcripción fue exitosa. El media_url tiene un token válido por 24 horas.
{
"event" : "message.received" ,
"timestamp" : "2025-01-15T10:30:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"whaapy_message_id" : "msg-uuid" ,
"conversation_id" : "conv-uuid" ,
"contact_id" : "contact-uuid" ,
"message" : {
"from" : "5215512345678" ,
"id" : "wamid.HBgNNTIxNTUxMjM0NTY3OA==" ,
"timestamp" : "1705312200" ,
"type" : "image" ,
"image" : {
"caption" : "Mira esta foto del producto" ,
"mime_type" : "image/jpeg" ,
"sha256" : "abc123def456..." ,
"id" : "media_id_de_meta"
}
},
"contact_name" : "Juan Pérez" ,
"media_url" : "https://api.whaapy.com/messages/msg-uuid/media?token=..."
}
}
{
"event" : "message.received" ,
"timestamp" : "2025-01-15T10:30:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"whaapy_message_id" : "msg-uuid" ,
"conversation_id" : "conv-uuid" ,
"contact_id" : "contact-uuid" ,
"message" : {
"from" : "5215512345678" ,
"id" : "wamid.HBgNNTIxNTUxMjM0NTY3OA==" ,
"timestamp" : "1705312200" ,
"type" : "video" ,
"video" : {
"caption" : "Video del problema que tengo" ,
"mime_type" : "video/mp4" ,
"sha256" : "abc123def456..." ,
"id" : "media_id_de_meta"
}
},
"contact_name" : "Juan Pérez" ,
"media_url" : "https://api.whaapy.com/messages/msg-uuid/media?token=..."
}
}
{
"event" : "message.received" ,
"timestamp" : "2025-01-15T10:30:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"whaapy_message_id" : "msg-uuid" ,
"conversation_id" : "conv-uuid" ,
"contact_id" : "contact-uuid" ,
"message" : {
"from" : "5215512345678" ,
"id" : "wamid.HBgNNTIxNTUxMjM0NTY3OA==" ,
"timestamp" : "1705312200" ,
"type" : "document" ,
"document" : {
"filename" : "factura-enero-2025.pdf" ,
"mime_type" : "application/pdf" ,
"sha256" : "abc123def456..." ,
"id" : "media_id_de_meta"
}
},
"contact_name" : "Juan Pérez" ,
"media_url" : "https://api.whaapy.com/messages/msg-uuid/media?token=..."
}
}
{
"event" : "message.received" ,
"timestamp" : "2025-01-15T10:30:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"whaapy_message_id" : "msg-uuid" ,
"conversation_id" : "conv-uuid" ,
"contact_id" : "contact-uuid" ,
"message" : {
"from" : "5215512345678" ,
"id" : "wamid.HBgNNTIxNTUxMjM0NTY3OA==" ,
"timestamp" : "1705312200" ,
"type" : "location" ,
"location" : {
"latitude" : 19.4326 ,
"longitude" : -99.1332 ,
"name" : "Mi Casa" ,
"address" : "Av. Reforma 123, CDMX"
}
},
"contact_name" : "Juan Pérez"
}
}
{
"event" : "message.received" ,
"timestamp" : "2025-01-15T10:30:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"whaapy_message_id" : "msg-uuid" ,
"conversation_id" : "conv-uuid" ,
"contact_id" : "contact-uuid" ,
"message" : {
"from" : "5215512345678" ,
"id" : "wamid.HBgNNTIxNTUxMjM0NTY3OA==" ,
"timestamp" : "1705312200" ,
"type" : "contacts" ,
"contacts" : [
{
"name" : {
"formatted_name" : "María García" ,
"first_name" : "María" ,
"last_name" : "García"
},
"phones" : [
{ "phone" : "+5215598765432" , "type" : "CELL" , "wa_id" : "5215598765432" }
],
"emails" : [
{ "email" : "maria@example.com" , "type" : "WORK" }
]
}
]
},
"contact_name" : "Juan Pérez"
}
}
{
"event" : "message.received" ,
"timestamp" : "2025-01-15T10:30:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"whaapy_message_id" : "msg-uuid" ,
"conversation_id" : "conv-uuid" ,
"contact_id" : "contact-uuid" ,
"message" : {
"from" : "5215512345678" ,
"id" : "wamid.HBgNNTIxNTUxMjM0NTY3OA==" ,
"timestamp" : "1705312200" ,
"type" : "sticker" ,
"sticker" : {
"mime_type" : "image/webp" ,
"sha256" : "abc123def456..." ,
"id" : "media_id_de_meta" ,
"animated" : false
}
},
"contact_name" : "Juan Pérez" ,
"media_url" : "https://api.whaapy.com/messages/msg-uuid/media?token=..."
}
}
{
"event" : "message.received" ,
"timestamp" : "2026-03-09T18:30:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"whaapy_message_id" : "msg-uuid" ,
"conversation_id" : "conv-uuid" ,
"contact_id" : "contact-uuid" ,
"message" : {
"from" : "5215512345678" ,
"id" : "wamid.HBgNNTIxNTUxMjM0NTY3OA==" ,
"timestamp" : "1741545000" ,
"type" : "interactive" ,
"interactive" : {
"type" : "nfm_reply" ,
"nfm_reply" : {
"name" : "booking_flow" ,
"body" : "Sucursal Centro · 2026-03-10 16:00" ,
"response_json" : "{ \" screen \" : \" BOOK_APPOINTMENT \" , \" values \" :{ \" branch \" : \" Sucursal Centro \" , \" slot \" : \" 2026-03-10 16:00 \" , \" service \" : \" demo \" }}"
}
},
"context" : {
"id" : "wamid.HBgNflowOriginal"
}
},
"contact_name" : "Juan Pérez"
}
}
Cuando Whaapy recibe un interactive.type = "nfm_reply", lo persiste como mensaje interactive en el inbox y mantiene el payload original de Meta dentro de message.interactive.nfm_reply.
message.image.id (Meta) ID de media de Meta. Requiere llamada adicional a su API para obtener la URL de descarga.
media_url (Whaapy) URL lista para descargar con token incluido. Sin llamadas adicionales.
Limitaciones:
Token válido : 24 horas
Expiración de media : WhatsApp elimina media después de 30 días
message.sent
Cuando tu negocio envía un mensaje (manual o por IA).
{
"event" : "message.sent" ,
"timestamp" : "2025-01-15T10:31:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"message_id" : "msg-uuid" ,
"conversation_id" : "conv-uuid" ,
"to" : "+5215512345678" ,
"contact_name" : "Juan Pérez" ,
"type" : "text" ,
"content" : "¡Hola! ¿En qué puedo ayudarte?" ,
"sent_by_ai" : true ,
"timestamp" : "2025-01-15T10:31:00.000Z" ,
"provider" : "meta"
}
}
Campo Tipo Descripción message_idstring ID del mensaje en Whaapy conversation_idstring ID de la conversación tostring Número de destino typestring Tipo de mensaje (text, image, template, etc.) contentstring Contenido del mensaje sent_by_aiboolean true si fue enviado por el agente de IA
conversation.created
Cuando se inicia una nueva conversación.
{
"event" : "conversation.created" ,
"timestamp" : "2025-01-15T10:30:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"conversation_id" : "conv-uuid" ,
"contact_id" : "contact-uuid" ,
"phone_number" : "+5215512345678" ,
"contact_name" : "Juan Pérez" ,
"timestamp" : "2025-01-15T10:30:00.000Z"
}
}
conversation.assigned
Cuando una conversación se asigna a un agente humano.
{
"event" : "conversation.assigned" ,
"timestamp" : "2025-01-15T10:32:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"conversation_id" : "conv-uuid" ,
"phone_number" : "+5215512345678" ,
"contact_name" : "Juan Pérez" ,
"assigned_to" : "agent-uuid" ,
"assigned_by" : "admin-uuid" ,
"method" : "manual" ,
"timestamp" : "2025-01-15T10:32:00.000Z"
}
}
Campo Tipo Descripción assigned_tostring UUID del agente asignado assigned_bystring UUID de quien asignó methodstring manual o auto
broadcast.completed
Cuando un envío masivo termina.
{
"event" : "broadcast.completed" ,
"timestamp" : "2025-01-15T11:00:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"broadcast_id" : "bc-uuid" ,
"name" : "Promoción Enero" ,
"total_recipients" : 500 ,
"sent_count" : 495 ,
"failed_count" : 5 ,
"success_rate" : 99 ,
"completed_at" : "2025-01-15T11:00:00.000Z"
}
}
message.delivered
Cuando WhatsApp confirma que el mensaje fue entregado al dispositivo del usuario.
{
"event" : "message.delivered" ,
"timestamp" : "2026-01-15T10:31:05.000Z" ,
"businessId" : "uuid" ,
"data" : {
"message_id" : "msg-uuid" ,
"wamid" : "wamid.HBgNNTIxNTUxMjM0NTY3OA==" ,
"conversation_id" : "conv-uuid" ,
"to" : "+5215512345678" ,
"contact_name" : "Juan Pérez" ,
"status" : "delivered" ,
"timestamp" : "2026-01-15T10:31:05.000Z"
}
}
Campo Tipo Descripción message_idstring ID del mensaje en Whaapy wamidstring ID del mensaje en WhatsApp statusstring Siempre delivered
message.read
Cuando el usuario abre y lee el mensaje (doble check azul).
{
"event" : "message.read" ,
"timestamp" : "2026-01-15T10:35:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"message_id" : "msg-uuid" ,
"wamid" : "wamid.HBgNNTIxNTUxMjM0NTY3OA==" ,
"conversation_id" : "conv-uuid" ,
"to" : "+5215512345678" ,
"contact_name" : "Juan Pérez" ,
"status" : "read" ,
"timestamp" : "2026-01-15T10:35:00.000Z"
}
}
El evento message.read solo se recibe si el usuario tiene habilitada la confirmación de lectura en WhatsApp.
message.failed
Cuando un mensaje no se pudo enviar.
{
"event" : "message.failed" ,
"timestamp" : "2026-01-15T10:31:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"message_id" : "msg-uuid" ,
"conversation_id" : "conv-uuid" ,
"to" : "+5215512345678" ,
"contact_name" : "Juan Pérez" ,
"status" : "failed" ,
"error" : {
"code" : "131047" ,
"title" : "Re-engagement message" ,
"message" : "La ventana de conversación de 24 horas ha expirado" ,
"details" : "More than 24 hours have passed since the customer last replied"
},
"timestamp" : "2026-01-15T10:31:00.000Z"
}
}
Campo Tipo Descripción error.codestring Código de error de WhatsApp error.titlestring Título corto del error error.messagestring Descripción del error en español error.detailsstring Detalles técnicos (en inglés)
Los códigos de error más comunes son 131047 (ventana cerrada), 131051 (número no válido), y 131026 (usuario bloqueó el número).
conversation.unassigned
Cuando una conversación es desasignada de un agente.
{
"event" : "conversation.unassigned" ,
"timestamp" : "2026-01-15T11:00:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"conversation_id" : "conv-uuid" ,
"phone_number" : "+5215512345678" ,
"contact_name" : "Juan Pérez" ,
"previous_agent" : "agent-uuid" ,
"unassigned_by" : "admin-uuid" ,
"reason" : "manual" ,
"timestamp" : "2026-01-15T11:00:00.000Z"
}
}
Campo Tipo Descripción previous_agentstring UUID del agente que tenía la conversación unassigned_bystring UUID de quien desasignó (null si fue automático) reasonstring manual, timeout, o reassigned
conversation.closed
Cuando una conversación es marcada como cerrada.
{
"event" : "conversation.closed" ,
"timestamp" : "2026-01-15T12:00:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"conversation_id" : "conv-uuid" ,
"phone_number" : "+5215512345678" ,
"contact_name" : "Juan Pérez" ,
"closed_by" : "agent-uuid" ,
"reason" : "resolved" ,
"duration_minutes" : 45 ,
"message_count" : 12 ,
"timestamp" : "2026-01-15T12:00:00.000Z"
}
}
Campo Tipo Descripción closed_bystring UUID de quien cerró (null si fue automático) reasonstring resolved, spam, timeout, manualduration_minutesnumber Duración total de la conversación message_countnumber Número total de mensajes
Cuando se crea un nuevo contacto (primera vez que alguien escribe).
{
"event" : "contact.created" ,
"timestamp" : "2026-01-15T10:30:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"contact_id" : "contact-uuid" ,
"phone_number" : "+5215512345678" ,
"name" : "Juan Pérez" ,
"wa_id" : "5215512345678" ,
"source" : "inbound_message" ,
"timestamp" : "2026-01-15T10:30:00.000Z"
}
}
Campo Tipo Descripción sourcestring inbound_message, import, manual
Cuando se actualizan los datos de un contacto.
{
"event" : "contact.updated" ,
"timestamp" : "2026-01-15T14:00:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"contact_id" : "contact-uuid" ,
"phone_number" : "+5215512345678" ,
"name" : "Juan Carlos Pérez" ,
"previous_name" : "Juan Pérez" ,
"updated_by" : "agent-uuid" ,
"updated_fields" : [ "name" , "email" , "tags" ],
"timestamp" : "2026-01-15T14:00:00.000Z"
}
}
Campo Tipo Descripción updated_bystring UUID de quien actualizó updated_fieldsstring[] Lista de campos modificados previous_namestring Nombre anterior (si cambió)
Cuando se elimina un contacto.
{
"event" : "contact.deleted" ,
"timestamp" : "2026-01-15T15:00:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"contact_id" : "contact-uuid" ,
"phone_number" : "+5215512345678" ,
"name" : "Juan Pérez" ,
"deleted_by" : "admin-uuid" ,
"reason" : "gdpr_request" ,
"timestamp" : "2026-01-15T15:00:00.000Z"
}
}
Campo Tipo Descripción deleted_bystring UUID de quien eliminó reasonstring gdpr_request, spam, manual, cleanup
Al eliminar un contacto, también se eliminan todas sus conversaciones y mensajes. Esta acción es irreversible.
broadcast.sent
Cuando un broadcast comienza a enviarse.
{
"event" : "broadcast.sent" ,
"timestamp" : "2026-01-15T10:00:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"broadcast_id" : "bc-uuid" ,
"name" : "Promoción Enero" ,
"template_name" : "promo_enero_2026" ,
"total_recipients" : 500 ,
"started_at" : "2026-01-15T10:00:00.000Z" ,
"created_by" : "admin-uuid"
}
}
Campo Tipo Descripción template_namestring Nombre del template usado total_recipientsnumber Número de destinatarios created_bystring UUID de quien creó el broadcast
broadcast.failed
Cuando un broadcast falla antes de completarse.
{
"event" : "broadcast.failed" ,
"timestamp" : "2026-01-15T10:05:00.000Z" ,
"businessId" : "uuid" ,
"data" : {
"broadcast_id" : "bc-uuid" ,
"name" : "Promoción Enero" ,
"template_name" : "promo_enero_2026" ,
"total_recipients" : 500 ,
"sent_count" : 150 ,
"failed_count" : 350 ,
"error" : {
"code" : "rate_limit_exceeded" ,
"message" : "Se excedió el límite de mensajes por minuto"
},
"failed_at" : "2026-01-15T10:05:00.000Z"
}
}
Campo Tipo Descripción sent_countnumber Mensajes enviados antes del fallo failed_countnumber Mensajes que no se enviaron error.codestring Código de error error.messagestring Descripción del error
Si un broadcast falla parcialmente, los mensajes ya enviados no se revierten. Puedes crear un nuevo broadcast con los contactos pendientes.
Próximos Pasos