El webhook y el WebSocket comparten el mismo envoltorio y el mismo catálogo de eventos. La única diferencia es el canal de entrega, data es idéntico.
Esta página documenta los 6 tipos: message.exchange , message.status , call.update , group.flow , instance.state y label.update .
Envoltorio
{
"event" : "<event-name>" ,
"data" : { /* event-specific payload */ },
"instanceData" : {
"baseUrl" : "https://api.example.com" ,
"instance" : "<name>" ,
"token" : "<instance-token>"
}
}
El filtrado se hace por el nombre del event en el campo events de la configuración. Vacío = todos los tipos.
Filtrado y enrutamiento
{ "events" : [ "message.exchange" , "call.update" , "instance.state" ] }
Cuando byEvents=true (solo webhook), el nombre del evento se agrega a la URL :
Configuración: url: "https://app/wh", byEvents: true
Entrega: POST https://app/wh/message.exchange
Útil para enrutamiento basado en endpoints sin inspeccionar el payload.
message.exchange
Mensajes enviados y recibidos (texto, media, sticker, documento, audio, encuesta, contacto, ubicación, etc.), ediciones y revocaciones.
Payload
{
"id" : "wamid.msg.123" ,
"message" : {
"id" : "wamid.msg.123" ,
"direction" : "incoming | outgoing" ,
"timestamp" : "2026-04-28T10:30:00Z" ,
"chat" : {
"jid" : "5511999999999" ,
"lid" : "100@s.whatsapp.net" ,
"name" : "João Silva" ,
"type" : "private | group | newsletter" ,
"isCommunity" : true
},
"sender" : {
"jid" : "5511999999999" ,
"lid" : "100@s.whatsapp.net" ,
"name" : "João Silva"
},
"content" : { "text" : "Hello" },
"media" : {
"type" : "image | video | audio | document | sticker | ptt | ptv" ,
"url" : "https://media-...whatsapp.net/..." ,
"s3Url" : "https://bucket.s3.amazonaws.com/..." ,
"base64" : "iVBORw0KGgo..." ,
"mimetype" : "image/jpeg" ,
"size" : 45678 ,
"caption" : "Event photo" ,
"fileName" : "doc.pdf"
},
"edit" : {
"original_id" : "wamid.original" ,
"originalContent" : "Old text" ,
"text" : "New text"
},
"forward" : { "count" : 2 },
"reply" : {
"message_id" : "wamid.replied" ,
"sender" : { "jid" : "..." , "name" : "..." },
"text" : "..." ,
"media" : { /* simplified media object */ }
},
"poll" : {
"title" : "Which flavor?" ,
"options" : [{ "name" : "Chocolate" , "count" : 0 }]
},
"location" : {
"latitude" : -23.5505 ,
"longitude" : -46.6333 ,
"address" : "Av. Paulista, 1374"
},
"contact" : {
"display_name" : "Maria" ,
"phone_number" : "5511987654321" ,
"vcard" : "BEGIN:VCARD..."
},
"list_response" : {
"title" : "..." ,
"body" : "..." ,
"list_type" : "single_select" ,
"single_select_reply" : { "option_name" : "p1" }
},
"button_response" : {
"title" : "Buy" ,
"body" : "..." ,
"selected_button_id" : "buy_camiseta"
},
"interactive" : { /* form / native flow / other surfaces */ }
}
}
Campos condicionales
Solo se completan los campos relevantes para el tipo de mensaje. Las ediciones tienen edit poblado; las revocaciones llegan con type: "message_revoke" en data.message.type.
chat.isCommunity aparece solo cuando es true , indicando que el chat es el canal de anuncios (parent / announcement channel) de una comunidad de WhatsApp. Los subgrupos vinculados a una comunidad mantienen type: "group" y no incluyen el campo isCommunity. En grupos regulares y DMs el campo también se omite.
media.base64 solo aparece cuando mediaBase64=true en la configuración (webhook o WebSocket). De lo contrario, usa media.url (whatsapp.net, expira) o media.s3Url (si S3 está configurado en la instancia).
Ejemplo (imagen recibida)
{
"event" : "message.exchange" ,
"data" : {
"id" : "wamid.123" ,
"message" : {
"id" : "wamid.123" ,
"direction" : "incoming" ,
"timestamp" : "2026-04-28T10:30:00Z" ,
"chat" : { "jid" : "5511999999999" , "name" : "João" , "type" : "private" },
"sender" : { "jid" : "5511999999999" , "name" : "João" },
"media" : {
"type" : "image" ,
"url" : "https://media-abc.whatsapp.net/..." ,
"mimetype" : "image/jpeg" ,
"size" : 45678 ,
"caption" : "Photo"
}
}
},
"instanceData" : { "baseUrl" : "https://api..." , "instance" : "minha" , "token" : "..." }
}
message.status
Acuses de entrega: delivered, read, played, etc.
Payload
{
"status" : "delivered | read | played | sender | read_self | played_self | retry | inactive | server_error" ,
"messageIds" : [ "wamid.123" , "wamid.124" ],
"timestamp" : "2026-04-28T10:45:00Z" ,
"chat" : {
"jid" : "5511999999999" ,
"type" : "private | group | broadcast" ,
"isCommunity" : true
},
"recipient" : "5511999999999" ,
"messageSender" : null ,
"isFromMe" : false
}
Enum status
Valor Significado deliveredEl mensaje llegó al dispositivo del destinatario. readEl destinatario lo leyó (con confirmaciones de lectura activas). playedAudio/nota de voz reproducida. senderEco interno (la propia fuente reportando entrega). read_selfEl propio usuario lo marcó como leído en otro dispositivo. played_selfEl propio usuario lo reprodujo en otro dispositivo. retryEl servidor solicitó re-entrega (transitorio). inactiveEl destinatario lleva demasiado tiempo offline. server_errorError genérico del servidor de WhatsApp.
messageSender en grupos : JID del autor original del mensaje (relevante cuando alguien lee un mensaje de otro participante).
chat.isCommunity sigue la misma regla que message.exchange: presente y true solo cuando el chat es el canal de anuncios de una comunidad.
call.update
Eventos de llamadas: offer, accepted, rejected, terminated, latency.
Payload
{
"type" : "offer | accepted | rejected | terminated | notification | latency" ,
"direction" : "incoming | outgoing" ,
"callId" : "call-abc123" ,
"from" : "5511999999999" ,
"to" : "5511888888888" ,
"timestamp" : "2026-04-28T10:35:00Z" ,
"groupJid" : null ,
"callMedia" : "audio | video | null" ,
"remotePlatform" : "Android | iPhone | null" ,
"remoteVersion" : "2.23.15.74" ,
"noticeType" : "group | null" ,
"reason" : null ,
"duration" : 123 ,
"latency" : 45.6 ,
"latencyStatus" : "Excellent | Good | Average | Poor"
}
Enum type
Valor Cuándo se dispara offerLlamada recibida/enviada (timbre inicial). acceptedEl lado remoto contestó. rejectedEl lado remoto rechazó. terminatedLlamada finalizada, se completa duration en segundos. notificationNotificación contextual de llamada (p. ej., llamada perdida en grupo). latencyMétrica de latencia durante la llamada (latency en ms, latencyStatus).
group.flow
Cambios de grupo: miembros, metadatos, configuraciones.
Payload, cambio de participante
{
"type" : "joined | left | promoted | demoted" ,
"groupJid" : "120363406289005073@g.us" ,
"groupName" : "Dev Team" ,
"timestamp" : "2026-04-28T11:00:00Z" ,
"participants" : [
{ "jid" : "5511999999999" , "action" : "joined" }
]
}
typeSignificado nameNombre del grupo cambiado topicDescripción cambiada locked / unlockedLos miembros pueden/no pueden editar la información announce / not_announceLos miembros pueden/no pueden enviar mensajes ephemeral / not_ephemeralMensajes efímeros activados/desactivados inviteEnlace de invitación generado link / unlinkSubgrupo vinculado/desvinculado de la comunidad deleteGrupo eliminado membership_approvalModo de aprobación para nuevos miembros cambiado suspended / unsuspendedGrupo suspendido/reactivado por WhatsApp
instance.state
Cambios en el estado de la propia instancia (conexión, QR, ban, emparejamiento).
Payload
{
"instance" : "minha" ,
"state" : "connected | disconnected | logged_out | stream_replaced | temp_banned | client_outdated | connect_failure | stream_error | cat_refresh_error | qr_ready | pair_success | pair_error | qr_scanned_no_multidevice | keepalive_timeout | keepalive_restored | manual_reconnect" ,
"timestamp" : "2026-04-28T10:50:00Z" ,
"reason" : "..." ,
"reasonCode" : 123 ,
"message" : "..." ,
"expireAt" : "2026-04-28T11:50:00Z" ,
"expireInSeconds" : 3600 ,
"onConnect" : true ,
"codes" : [ "1@abc...,xyz==,base64string" ],
"jid" : "5511999999999@s.whatsapp.net" ,
"platform" : "iPhone | Android | Desktop" ,
"errorMsg" : "..."
}
Enum state
Estado Significado Campos extra completados connectedSesión activa, lista para enviar/recibir. , disconnectedDesconectado (transitorio, usualmente reconecta solo). , logged_outSesión invalidada (requiere nuevo connect). reasonstream_replacedOtra sesión tomó el control (conflicto multi-dispositivo). , temp_bannedBan temporal aplicado por WhatsApp. expireAt, expireInSeconds, reasonclient_outdatedLa versión de WhatsApp Web en uso está desactualizada, contactar soporte. messageconnect_failureFalla durante la conexión. reason, reasonCodestream_errorError de stream de WhatsApp. errorMsgcat_refresh_errorFalla al refrescar credenciales (cat). errorMsgqr_readyNuevo QR disponible para emparejamiento. codes[], onConnectpair_successEmparejamiento completado. jid, platformpair_errorError durante el emparejamiento. errorMsgqr_scanned_no_multideviceQR escaneado pero el dispositivo destino no soporta multi-dispositivo. , keepalive_timeoutConexión inestable, keepalive no respondido. , keepalive_restoredConexión estabilizada después de keepalive_timeout. , manual_reconnectReconexión disparada manualmente vía REST. ,
Para que tu cliente sepa cuándo refrescar el QR en la UI, escucha instance.state con state=qr_ready y renderiza data.codes[0].
label.update
Ediciones/asociaciones de etiquetas (etiquetas de WhatsApp Business).
Payload
{
"type" : "edit | chat | message" ,
"labelId" : "1" ,
"timestamp" : "2026-04-28T10:00:00Z" ,
"action" : "updated | deleted | add | remove" ,
// type=edit:
"name" : "Important" ,
"color" : 0 ,
"labelType" : "CUSTOM" ,
"isActive" : true ,
"isImmutable" : false ,
"orderIndex" : 1 ,
"deleted" : false ,
// type=chat:
"chatJid" : "5511999999999@s.whatsapp.net" ,
"labeled" : true ,
// type=message:
"messageId" : "wamid.abc"
}
Combinaciones type × action
typeaction válidaCampos extra editupdated, deletedname, color, labelType, isActive, isImmutable, orderIndex, deletedchatadd, removechatJid, labeledmessageadd, removechatJid, messageId
Eventos no emitidos (internos)
Capturados por el handler de whatsmeow pero no propagados vía webhook/WS:
*events.Picture, cambio de foto de perfil (solo registrado en log).
*events.FBMessage, Facebook Business (solo registrado en log).
*events.HistorySync, sincronización de historial (procesado y almacenado en BD).
Si necesitas consumir alguno de estos cambios, haz polling de los endpoints REST correspondientes (perfil, historial).
Referencias
Configurar webhook Filtra eventos vía events[] en la configuración.
Configurar WebSocket Misma sintaxis de filtro que el webhook.
Conectar vía WebSocket Recibe eventos en tiempo real.
Resumen de eventos Comparación webhook vs WebSocket.