Documentación / Completo
Los 26 tipos de evento con su payload
¿Programás con IA? Conectala a tu cuenta por MCP: revisa tu configuración, crea webhooks y plantillas con tu confirmación y te escribe el código. Ver cómo →
26 tipos suscribibles + webhook.test (no suscribible). Un endpoint con lista de events explícita recibe SOLO esos tipos; lista vacía/omitida = todos los de hoy. Un tipo nuevo que cambia lo que te llega (message.edited, message.revoked, onboarding.help_requested) no entra solo a un endpoint creado antes: volvé a mandar events: null (o elegilo en la lista) y queda sumado (newEventsAvailable dice cuáles faltan). El data de cada uno:
| Tipo | Suscribible | Descripción |
|---|---|---|
message.received | ✓ | Un cliente te escribió. data.message = objeto de mensaje de WhatsApp (texto/imagen/audio/…); data.contacts[] identifica al remitente. Un carrito del catálogo llega con type 'order' (ver «Carritos del catálogo»). |
message.echo | ✓ | 🌟 Diferencial de Coexistence: un mensaje que el DUEÑO envió DESDE EL TELÉFONO, con su contenido. origin='device' (no consume el plan). from = tu número de negocio. |
message.status | ✓ | Acuse de un mensaje que enviaste por /v1. origin (api|device) distingue tu envío del envío hecho a mano desde el teléfono. Un acuse 'device' NO tiene mensaje tuyo detrás — es esperado. Desde el 29/09/2026 también llega cuando Meta rechaza NUESTRO envío después del 202: status 'failed', stage 'send' (campo opcional), failureCode (el mismo catálogo de lastErrorCode) y messageId = tu msg_… (Meta nunca le asignó un wamid). Una sola vez por mensaje. |
connection.created | ✓ | Se conectó un número nuevo (panel u onboarding hospedado). Tu externalId viaja en el sobre → provisioná al cliente final en tu sistema. |
onboarding.completed | ✓ | Tu cliente terminó el alta con un enlace (número nuevo o reconectado: data.reconnected). Va al webhook del número —el que trajo el enlace— o al de cuenta. Trae tu externalId y el wh_ del webhook del enlace. |
onboarding.failed | ✓ | Un intento con tu enlace terminó sin número (falló, se canceló o se abandonó), con motivo (reason; coex_gate = Meta no habilitó la Convivencia para ese portafolio) y, si Meta los dio, su código y su session_id (metaSessionId, el que pide su soporte). Va a la URL del webhook del enlace, firmado con SU secreto; si el enlace no trajo webhook, al de cuenta (connection en null). El enlace sigue sirviendo mientras linkUsable sea true. Desde la 0.32.0, si el intento llegó sin código, lastKnownMetaErrorCode y lastKnownMetaErrorCodeAt traen el último código que Meta dio en un intento anterior del mismo enlace (y número); metaErrorCode no se pisa. Desde la 0.35.0, stage = meta_ok_connect_incomplete si Meta terminó su parte y Connect no sumó el número (por ejemplo, reason connection_limit_reached): arreglá la causa y mandá un enlace nuevo. Desde la 0.41.0, attemptId identifica el intento (el mismo de onboarding.help_requested); «Cancelar y volver a empezar» ya no emite este evento (no es un desenlace: el intento siguiente trae el real) y el GET del enlace dice reason restarted; y si el enlace no tiene código, lastKnownMetaErrorCode también mira tus otros enlaces con el mismo externalId de los últimos 7 días. |
connection.status_changed | ✓ | Cambió el estado de una conexión (ej. status='disconnected'). status='banned' (nuevo): WhatsApp (Meta) bloqueó el número; no envía ni recibe, sigue ocupando su lugar en el plan y vuelve solo a 'connected' si Meta lo libera. Alertá o pausá los envíos a ese número. |
message.edited | ✓ | El contacto editó un mensaje. Convivencia: textAvailable true y data.message = la VERSIÓN FINAL con la misma forma que message.received (texto, o imagen/video con el pie nuevo), con id = el wamid original. Sólo Cloud API: Meta no manda el texto → textAvailable false, message null, reason META_EDIT_TEXT_NOT_PROVIDED. originalMessageId = el wamid del mensaje editado (null si Meta no lo da). El texto anterior no lo guardamos: buscalo en tu base por originalMessageId. |
message.revoked | ✓ | El contacto borró un mensaje (Convivencia; hasta 2 días después de mandarlo). originalMessageId = el wamid del mensaje borrado. Sin contenido. |
connection.disconnected | ✓ | Un número dejó de estar conectado, una vez por baja: lo diste de baja vos (panel o DELETE /v1/connections/{id}, reason user_disconnected), Meta revocó el acceso (token_revoked) o el negocio quitó a WAIA Connect en Meta (partner_removed). source = api · panel · meta. Va a tus webhooks de CUENTA: los del número se apagan en ese momento. |
connection.usage_threshold_reached | ✓ | El límite de UN número (su monthlyMessageLimit) llegó a 70/90/100%. Se emite UNA vez por (número, período, umbral). El comercial: avisale a ese cliente final o subile el tope. |
usage.threshold_reached | ✓ | El cupo de la CUENTA (suma de todos los números) llegó a un umbral. Sólo existe con un plan a medida con cupo pactado: los planes públicos no tienen tope y no lo emiten. connection viene en null, sequence 0. |
history.synced | ✓ | (Coexistence) resumen de cada tanda del historial que Meta sincroniza al conectar: cuántas conversaciones y cuántos mensajes, la fase y el avance (progress 100 = Meta terminó). shared:false = el dueño no compartió su historial en la app. Los mensajes llegan en history.batch sólo si se eligió recibirlos antes de conectar. |
history.batch | ✓ | (Coexistence) los mensajes del historial —hasta 6 meses, las dos partes, sin grupos—, si el dueño eligió recibirlo antes de conectar. Hasta 500 por evento, en la forma de Meta (message), con contact y direction. Deduplicá por batchId: una tanda puede llegar dos veces. Va por una cola aparte que no demora lo en vivo. Con tu 2xx la borramos; si no, a los 7 días. |
history.completed | ✓ | (Coexistence) ya no queda nada del historial de ese número por entregar: cuántos mensajes se entregaron (por webhook o por la API), cuántos vencieron y cuántos no se pudieron guardar. Llega una vez. |
history.dropped | ✓ | (Coexistence) no pudimos guardar el historial de ese número para entregártelo (reason: storage_limit, o disabled). Lo ya guardado se entrega igual. El historial sigue en el teléfono. Llega una vez. |
template.status_changed | ✓ | Cambió el estado de una plantilla en Meta (aprobada, rechazada, pausada, deshabilitada, calidad o categoría). Trae el estado nuevo y el motivo de rechazo traducido. Llega por una sincronización periódica: con unos minutos de demora. |
template.draft_submitted | ✓ | Tu cliente final propuso una plantilla desde un enlace de propuesta. El borrador NO está en Meta: llega a Meta sólo cuando lo aprobás. |
account.trial_warning | ✓ | La prueba gratis de la cuenta termina pronto (data.days = 7, 3 o 1). connection=null. |
account.trial_expired | ✓ | Terminó la prueba gratis: se bloquea el envío (/v1 contesta 402 TRIAL_EXPIRED). Si nunca pagaste, la recepción se corta 7 días después, con aviso 2 días antes; si pagaste alguna vez, 30 días después, con aviso 7 días antes. data.receivingUntil = esa fecha (null sin un número conectado). connection=null. |
subscription.activated | ✓ | (Facturación) el primer pago se confirmó y la cuenta del cliente del reseller pasó a activa. connection=null. Reaccioná activando su servicio. |
subscription.cancelled | ✓ | (Facturación) baja de la suscripción. Sigue funcionando hasta accessUntil; después se bloquea el envío, y la recepción se corta 30 días después del bloqueo (con aviso 7 días antes). connection=null. |
billing.payment_failed | ✓ | (Facturación) un cobro recurrente falló. Entra en aviso/gracia (no corta nada todavía). Duneá a tu cliente ANTES del corte. connection=null. |
billing.payment_recovered | ✓ | (Facturación) el pago se regularizó y la cuenta se reactivó sola. connection=null. |
billing.amount_updated | ✓ | (Facturación) cambió el importe de tu próxima renovación. Podés mostrarle el importe a tu cliente antes de que se cobre. connection=null. ⚠ Con tarifa plana el cambio es del PLAN o de la cotización: overageArs y overageMessages llegan en 0 (sólo un plan a medida con cupo pactado los mueve). |
webhook.test | — | El evento del botón 'Probar' del panel. NO es suscribible: se entrega SIEMPRE a ese endpoint (aunque tenga lista de eventos explícita) para validar conectividad + firma. connection=null, data.test=true. Nunca lo trates como tráfico real. |
onboarding.help_requested | ✓ | Tu cliente se trabó en el alta por enlace y escribió qué vio. clientText = su texto TEXTUAL; metaErrorCode y metaSessionId (también sacados del texto si pegó «#código:session_id»); responsible y diagnosis (qué es y qué hacer, en el idioma del enlace; null si no hay con qué; source = catalog, o ai con su confidence sólo si el código es nuevo o no hay código); caseId (A-<n>) y hasScreenshot. Catálogo 3: se pide por nombre. |
message.received — data
{
"message": {
"id": "wamid.HBgL…",
"from": "5493511234567",
"type": "text",
"text": {
"body": "hola"
}
},
"contacts": [
{
"wa_id": "5493511234567",
"profile": {
"name": "Ana"
}
}
]
}message.echo — data
{
"origin": "device",
"message": {
"id": "wamid.HBgL…",
"from": "5493516516690",
"to": "5493511234567",
"type": "text",
"timestamp": "1754531482",
"text": {
"body": "te confirmo el turno para mañana 10hs"
}
}
}message.status — data
{
"messageId": "wamid.HBgL…",
"status": "read",
"origin": "device",
"recipient": "5493511234567",
"timestamp": "2026-08-06T00:31:25.000Z"
}connection.created — data
{
"status": "connected"
}onboarding.completed — data
{
"onboardingLinkId": "onb_ca18…",
"externalId": "pharmacy-42",
"label": "Pharmacy 42",
"reconnected": false,
"mode": "coexistence",
"displayName": "Pharmacy 42",
"historyDelivery": false,
"webhookId": "wh_5c1e…"
}onboarding.failed — data
{
"onboardingLinkId": "onb_ca18…",
"externalId": "pharmacy-42",
"label": "Pharmacy 42",
"attemptId": "sa_5f1e2d3c4b5a",
"attemptStatus": "failed",
"reason": "meta_error",
"metaErrorCode": 2655093,
"metaMessage": "The business is already sharing this WhatsApp Business account with a partner…",
"metaSessionId": "01a1b2c3-…",
"lastKnownMetaErrorCode": null,
"lastKnownMetaErrorCodeAt": null,
"stage": null,
"failedAttempts": 1,
"linkUsable": true,
"linkExpiresAt": "2026-10-09T18:00:00.000Z"
}connection.status_changed — data
{
"status": "disconnected",
"previousStatus": "connected"
}message.edited — data
{
"originalMessageId": "wamid.HBgNNTQ5MTE1NTU1MDEwMBUCABIYFjNFQjBBMUMyRDNFNEY1QTZCN0M4RDkA",
"originalConnectMessageId": null,
"editMessageId": "wamid.HBgNNTQ5MTE1NTU1MDEwMBUCABIYFjNFQjBFRElUMDAwMDAwMDAwMDAwMDEA",
"editedAt": "2026-10-23T14:05:09.000Z",
"from": "5491155550100",
"connectionId": "conn_4eede070e5a84d1590bdce2ea1d837bc",
"contacts": [
{
"profile": {
"name": "Ana"
},
"wa_id": "5491155550100"
}
],
"textAvailable": true,
"message": {
"id": "wamid.HBgNNTQ5MTE1NTU1MDEwMBUCABIYFjNFQjBBMUMyRDNFNEY1QTZCN0M4RDkA",
"from": "5491155550100",
"timestamp": "1792764309",
"type": "text",
"text": {
"body": "Te transfiero $ 15.400 (no 14.500)"
}
},
"reason": null
}message.revoked — data
{
"originalMessageId": "wamid.HBgNNTQ5MTE1NTU1MDEwMBUCABIYFjNFQjBBMUMyRDNFNEY1QTZCN0M4RDkA",
"originalConnectMessageId": null,
"revokeMessageId": "wamid.HBgNNTQ5MTE1NTU1MDEwMBUCABIYFjNFQjBSRVZPS0UwMDAwMDAwMDAwMDEA",
"revokedAt": "2026-10-23T14:20:41.000Z",
"from": "5491155550100",
"connectionId": "conn_4eede070e5a84d1590bdce2ea1d837bc",
"contacts": [
{
"profile": {
"name": "Ana"
},
"wa_id": "5491155550100"
}
]
}connection.disconnected — data
{
"status": "disconnected",
"previousStatus": "connected",
"reason": "user_disconnected",
"source": "api",
"disconnectedAt": "2026-10-11T18:04:12.000Z"
}connection.usage_threshold_reached — data
{
"scope": "connection",
"threshold": 90,
"used": 900,
"limit": 1000,
"period": "2026-08",
"connection": {
"id": "conn_4eede070e5a84d1590bdce2ea1d837bc",
"label": "Farmacia López",
"externalId": "farmacia-lopez"
}
}usage.threshold_reached — data
{
"scope": "account",
"threshold": 90,
"used": 22610,
"limit": 25000,
"period": "2026-08"
}history.synced — data
{
"conversations": 34,
"messages": 512,
"shared": true,
"phase": 0,
"chunkOrder": 1,
"progress": 55
}history.batch — data
{
"batchId": "hb_3c1f0a9b8e7d4c2b9a1f0e3d2c1b0a9f",
"seq": 1,
"phase": 1,
"chunkOrder": 3,
"progress": 40,
"count": 2,
"expiresAt": "2026-10-11T03:00:00.000Z",
"messages": [
{
"contact": "5493511234567",
"direction": "in",
"message": {
"id": "wamid.HBgL…",
"from": "5493511234567",
"timestamp": "1739230970",
"type": "text",
"text": {
"body": "¿Tienen turno el lunes?"
},
"history_context": {
"status": "READ"
}
}
},
{
"contact": "5493511234567",
"direction": "out",
"message": {
"id": "wamid.HBgM…",
"from": "5493510000000",
"timestamp": "1739231001",
"type": "text",
"text": {
"body": "Sí, a las 10."
},
"history_context": {
"status": "DELIVERED"
}
}
}
]
}history.completed — data
{
"batches": 12,
"delivered": 5480,
"deliveredByWebhook": 5480,
"deliveredByApi": 0,
"expired": 0,
"dropped": 0
}history.dropped — data
{
"reason": "storage_limit",
"messages": 500
}template.status_changed — data
{
"template": {
"name": "turno_confirmado",
"language": "es_AR"
},
"event": "message_template_status_update",
"status": "APPROVED",
"category": "UTILITY",
"qualityScore": null,
"reason": null,
"rejection": null
}template.draft_submitted — data
{
"draft": {
"id": "tpl_…",
"name": "promo_octubre",
"language": "es_AR",
"suggestedCategory": "MARKETING"
},
"proposedVia": {
"linkId": "onb_…",
"label": "Farmacia Centro"
},
"clientNotes": null,
"precheck": {
"warnings": []
}
}account.trial_warning — data
{
"days": 3,
"trialEndsAt": "2026-09-13T00:00:00.000Z"
}account.trial_expired — data
{
"trialEndsAt": "2026-09-13T00:00:00.000Z",
"receivingUntil": "2026-09-20T00:00:00.000Z"
}subscription.activated — data
{
"planCode": "growth",
"periodEnd": "2026-09-13T00:00:00.000Z"
}subscription.cancelled — data
{
"accessUntil": "2026-09-13T00:00:00.000Z"
}billing.payment_failed — data
{
"paymentId": "123456789",
"amount": 59000,
"currency": "ARS"
}billing.payment_recovered — data
{
"planCode": "growth",
"paymentId": "123456789"
}billing.amount_updated — data
{
"totalArs": 52500,
"baseArs": 52500,
"overageArs": 0,
"overageMessages": 0
}webhook.test — data
{
"type": "test",
"test": true,
"message": "WAIA Connect test event — your endpoint is reachable and verifying signatures. This is NOT real traffic."
}onboarding.help_requested — data
{
"onboardingLinkId": "olk_8f2c1d3e4b5a6978",
"externalId": "cliente-1234",
"label": "Panadería Ejemplo",
"attemptId": "sa_5f1e2d3c4b5a",
"attemptStatus": "failed",
"reason": "incomplete",
"mode": "coexistence",
"locale": "es",
"metaErrorCode": 3441060,
"metaSessionId": "0f3c2a1e-5b7d-4c9a-8e21-000000000000",
"metaErrorFrom": "client_text",
"responsible": "client",
"diagnosis": {
"key": "3441060",
"metaErrorCode": 3441060,
"title": "Meta Business AI activa en el número",
"cause": "La IA de Meta está encendida en la app WhatsApp Business de ese teléfono y Meta no deja usar la Convivencia así.",
"steps": [
"Que tu cliente abra WhatsApp Business → Herramientas para la empresa → Meta Business AI y la desactive.",
"Que vuelva a abrir el mismo enlace y conecte."
],
"links": [],
"responsible": "client",
"source": "catalog",
"catalogVersion": 1,
"locale": "es"
},
"clientText": "Me sale esto: Meta Business AI está activado en este número. #3441060:0f3c2a1e-5b7d-4c9a-8e21-000000000000",
"noteId": 12,
"caseId": "A-12",
"hasScreenshot": false,
"linkUsable": true,
"linkExpiresAt": "2026-10-27T09:14:00.000Z"
}