Por Avi
Publicado el 31 jul 2026
Webhooks de WhatsApp: cómo configurar y gestionar eventos entrantes con Chakra Chat
Toda integración bidireccional con WhatsApp comienza con un webhook. Esta guía abarca los dos tipos de webhook de Chakra, cargas útiles de eventos reales, verificación de firmas HMAC, gestión de entregas duplicadas y creación de un enrutador de mensajes: todo lo que necesitan n8n, Zapier y quienes desarrollan integraciones personalizadas.
Toda integración bidireccional con WhatsApp —un chatbot, una sincronización con un CRM, una automatización de n8n o un flujo personalizado de notificaciones— comienza en el mismo lugar: un webhook. La Cloud API de WhatsApp es asíncrona. Cuando envías un mensaje, recibes una confirmación de que se aceptó, no de que llegó. Todo lo que sucede después —la entrega, las confirmaciones de lectura, las respuestas y los errores— te llega como un evento a través de un webhook que controlas.
Esta guía explica cómo Chakra pone esos eventos a tu disposición, cuáles son los dos tipos de webhook, cómo se estructuran sus cargas útiles, cómo verificar que los eventos realmente provengan de Chakra, cómo gestionar las entregas duplicadas que tarde o temprano recibirás y un patrón sencillo para enrutar todo de forma ordenada desde un solo lugar.
Dos tipos de webhook, no uno
Chakra te ofrece dos formas distintas de recibir eventos entrantes, y elegir la adecuada influye en cuánto trabajo de análisis tendrás que hacer.
| Webhook de eventos sin modificar | Webhook de eventos de Chakra | |
|---|---|---|
| Formato | Carga útil sin modificar de la Cloud API de Meta | Carga útil simplificada y normalizada por Chakra |
| Cobertura de eventos | Todo lo que envía Meta: mensajes, estados, envíos de plantillas y actualizaciones de cuenta | Un conjunto seleccionado: message, status, message_echo, smb_message_echo |
| Ideal para | Equipos que ya conocen el esquema de webhooks de Meta o que están migrando integraciones existentes | Equipos que buscan reducir el trabajo de análisis y mantener un formato uniforme, independientemente de los cambios en el esquema de Meta |
| Uso habitual | Flujos de n8n/Zapier basados en el formato de webhook documentado por Meta | Integraciones de backend personalizadas, sincronización con CRM y lógica de enrutamiento más sencilla |
Puedes configurar cualquiera de los dos o ambos: son opciones independientes y nada te impide usar un webhook de Chakra para tu integración principal y otro sin modificar para una herramienta específica, como n8n, que espera el formato nativo de Meta.
Configurar el webhook de eventos sin modificar
Este webhook entrega el formato de eventos propio de Meta, sin modificar, exactamente como se documenta en la referencia de webhooks de la Cloud API de Meta.
Para configurarlo:
- Ve a la página Configuración de WhatsApp en tu panel de Chakra
- Haz clic en la pestaña Más
- Busca el campo llamado "URL del webhook sin modificar para eventos de Meta"
- Pega la URL de tu endpoint
- Haz clic en Guardar
Esta es la opción que usan la mayoría de las integraciones con n8n y Zapier, ya que esas plataformas suelen esperar (o están diseñadas para usar) el formato de webhook propio de Meta.
Configurar el webhook de eventos de Chakra
Este es el formato normalizado propio de Chakra: un contenedor uniforme, independientemente de los cambios que haga Meta.
Para configurarlo:
- Ve a Configuración de WhatsApp → Más
- Activa la opción "Activar webhooks de Chakra"
- Ingresa tu endpoint en el campo "URL del webhook de Chakra"
- Selecciona los tipos de eventos que quieres recibir en "Qué eventos enviar" : solo se enviarán los tipos que selecciones
- Haz clic en Guardar
Tu endpoint debe estar accesible públicamente y no requiere autenticación de tu lado (la verificación se realiza mediante una firma HMAC, como explicamos más adelante).
Tipos de eventos y estructura de las cargas útiles
Formato sin modificar (formato nativo de Meta)
Todo llega dentro de la estructura estándar de Meta entry → changes → value , con un campo field que indica el tipo de evento. Dos de los más comunes:
Un mensaje de texto entrante (field: "messages"):
{ "object": "whatsapp_business_account", "entry": [ { "id": "83784929738012", "changes": [ { "value": { "messaging_product": "whatsapp", "metadata": { "display_phone_number": "917259775805", "phone_number_id": "694794789348887" }, "contacts": [ { "profile": { "name": "John" }, "wa_id": "919911223344" } ], "messages": [ { "from": "919911223344", "id": "wamid.HBgMOTEST3AxMjU4NDMzFQIAEhgUM0VCMDM3RjlEQTNDQTBEMjQ2Q275", "timestamp": "1756322278", "type": "text", "text": { "body": "Hi, can you please help me out with an issue" } } ] }, "field": "messages" } ] } ]}Un mensaje de imagen entrante : el mismo contenedor, con un type:
{ "messages": [ { "from": "919911223344", "id": "wamid.HBgMOTEST3AxMjU4NDMzFQIAEhgUM0VCMDM3RjlEQTNDQTBEMjQ2Q275", "timestamp": "1756322278", "type": "image", "image": { "mime_type": "image/jpeg", "sha256": "t8K9fuzwAoeBSOoIZWkBlI1hTflUHqSwCvGwJ+exm74=", "id": "1402363984735353" } } ]}Ten en cuenta que el evento de imagen solo proporciona un ID de medio, no el archivo en sí. Debes obtener el archivo por separado a través del endpoint de Chakra para obtener medios, usando ese ID.
Un evento de envío de plantilla (field: "message_template_sends"): se activa cuando se envía un mensaje de plantilla iniciado por API. Es útil para registrar exactamente qué se envió y a quién, sin tener que mantener un registro de envíos aparte:
{ "field": "message_template_sends", "value": { "message_template_sends": [ { "id": "wamid.HBgMOTE5OTAxMjU4NDMzFQIAERgSMjNBQkQ5NDA5NTFEN0MzOTU2AA==", "timestamp": 1763821065, "to": "919911223344", "type": "template", "template": { "language": { "policy": "deterministic", "code": "en_US" }, "name": "new_year_promo_24", "components": [ { "type": "body", "parameters": [{ "type": "text", "text": "John" }] } ] } } ] }}Como este webhook transmite el formato de Meta sin modificar, los eventos de estado de entrega y lectura, los eventos de error y las actualizaciones a nivel de cuenta siguen la misma estructura documentada en la referencia de webhooks de la Cloud API de Meta. Te recomendamos guardar esa página en tus favoritos junto con esta guía.
Formato de eventos de Chakra
Cada evento llega con exactamente dos claves de nivel superior:
{ "event": "message", "payload": { /* MessagePayload or DeliveryStatusPayload, depending on event type */ }}event puede tener uno de cuatro valores:
message: un mensaje entrante recibido en tu númerostatus: una actualización del estado de entrega de un mensaje enviado anteriormente mediante API (enviado, entregado, leído o fallido)message_echo: un mensaje saliente enviado desde la plataforma de Chakra (por ejemplo, la respuesta de un agente desde la bandeja de entrada compartida). Es útil si sincronizas el historial completo de conversaciones con un sistema externo.smb_message_echo: un mensaje saliente enviado desde WhatsApp Business App. Es relevante específicamente si usas Coexistencia y necesitas registrar los mensajes enviados desde la aplicación, no solo desde la API
Este formato normalizado facilita crear un enrutador para el webhook de Chakra: siempre puedes decidir qué hacer según el mismo campo event , sin importar cómo sea el esquema subyacente de Meta ese mes.
Verificar la autenticidad del webhook con HMAC
Como tu endpoint de webhook es público y no requiere autenticación entrante, es importante verificar que el evento realmente provenga de Chakra (y que no haya sido alterado) antes de actuar.
Configuración:
- Ve a Administración → Equipo → Secretos y configura un secreto HMAC
- Una vez configurado, cada llamada al webhook incluirá el encabezado
X-Chakra-Signature-256
Lógica de verificación: genera un hash HMAC-SHA256 de la cadena del cuerpo sin procesar de la solicitud usando tu secreto como clave y compáralo con el valor del encabezado.
const crypto = require("crypto");function verifyChakraSignature(rawBody, signatureHeader, secret) { const expectedHash = crypto .createHmac("sha256", secret) .update(rawBody) // must be the raw string, not a parsed object .digest("hex"); // timing-safe comparison return crypto.timingSafeEqual( Buffer.from(expectedHash, "utf8"), Buffer.from(signatureHeader, "utf8") );}app.post("/webhooks/chakra", express.raw({ type: "*/*" }), (req, res) => { const rawBody = req.body.toString("utf8"); const signature = req.headers["x-chakra-signature-256"]; if (!verifyChakraSignature(rawBody, signature, process.env.CHAKRA_HMAC_SECRET)) { return res.status(401).send("Invalid signature"); } const event = JSON.parse(rawBody); handleEvent(event); res.sendStatus(200);});Un detalle que suele causar problemas: debes calcular el hash de la cadena del cuerpo sin procesar, no de una versión convertida a JSON y serializada de nuevo. La mayoría de los frameworks analizan el cuerpo automáticamente antes de que se ejecute tu función, así que normalmente necesitas un middleware para el cuerpo sin procesar (como express.raw, mostrado arriba) específicamente para esta ruta. Ten en cuenta también que el valor del encabezado no tiene el prefijo sha256= , a diferencia de las convenciones HMAC de algunos otros proveedores. No elimines un prefijo que no existe.
Cómo gestionar eventos duplicados
La entrega de webhooks de WhatsApp funciona, en la práctica, con una garantía de al menos una vez, no exactamente una vez: los reintentos de red, las interrupciones temporales del endpoint o los reintentos del lado de Meta pueden hacer que el mismo evento llegue más de una vez. Incorporar la deduplicación desde el inicio evita procesar un mensaje dos veces (enviar dos respuestas, contabilizar dos veces un estado de entrega o activar un flujo de trabajo dos veces).
La clave confiable para deduplicar es el ID del mensaje (wamid... para los mensajes, o el campo de ID correspondiente para los eventos de estado): guarda los ID procesados recientemente y compruébalos antes de actuar:
const processedIds = new Map(); // swap for Redis/DB in productionconst DEDUP_TTL_MS = 10 * 60 * 1000; // 10 minutes is generally enoughfunction isDuplicate(messageId) { const seenAt = processedIds.get(messageId); if (seenAt && Date.now() - seenAt < DEDUP_TTL_MS) return true; processedIds.set(messageId, Date.now()); return false;}function handleEvent(event) { const messageId = event.payload?.id || event.payload?.messages?.[0]?.id; if (messageId && isDuplicate(messageId)) { return; // already processed, skip silently } routeEvent(event);}En producción, usa Redis o tu base de datos con un TTL, en lugar de un Map en memoria: un Map de un solo proceso no sobrevive a un reinicio ni funciona en varias instancias del servidor.
Cómo crear un enrutador de mensajes sencillo
Una vez verificados y deduplicados los eventos, el siguiente paso práctico es dirigirlos al controlador adecuado según su tipo. Aquí tienes un enrutador básico para el formato de eventos de Chakra:
function routeEvent(event) { switch (event.event) { case "message": return handleInboundMessage(event.payload); case "status": return handleStatusUpdate(event.payload); case "message_echo": return handleAgentReply(event.payload); case "smb_message_echo": return handleAppSentMessage(event.payload); default: console.warn("Unhandled event type:", event.event); }}function handleInboundMessage(payload) { const messageType = payload.type; // "text", "image", "interactive", etc. switch (messageType) { case "text": return processTextMessage(payload); case "image": case "document": return processMediaMessage(payload); // fetch media by ID separately case "interactive": return processButtonOrListReply(payload); default: console.log("Unrecognized message type:", messageType); }}function handleStatusUpdate(payload) { // payload.status: "sent" | "delivered" | "read" | "failed" updateMessageRecordInDb(payload.id, payload.status, payload.errorCode);}Este mismo patrón de ramificación también funciona sin problemas si usas el webhook de paso directo: en ese caso, crearías ramas según changes[].field (messages, message_template_sends, message_status equivalentes) en lugar de event, y luego profundizarías en value.messages[0].type de la misma manera.
Una nota sobre los eventos de llamadas
Si usas la Calling API de Chakra junto con la mensajería, los eventos relacionados con llamadas (iniciadas, conectadas, finalizadas, perdidas) se entregan mediante el mismo mecanismo de paso directo y siguen el esquema de webhook de eventos de llamadas de Meta. Si tu enrutador ya crea ramas según field para los eventos relacionados con mensajes, ampliarlo para manejar un campo calls sigue el mismo patrón: consulta la documentación oficial de Cloud API de Meta para conocer los nombres exactos de los campos de los eventos de llamadas, ya que Chakra transmite ese esquema sin modificarlo ni normalizarlo.
Cómo usar esto con n8n
En n8n, la configuración habitual usa el webhook de paso directo, ya que los flujos de trabajo de n8n orientados a WhatsApp y las plantillas de la comunidad suelen estar creados para el formato nativo de Meta:
- Configura un nodo de activación Webhook en n8n y copia su URL de prueba o de producción.
- Pega esa URL en el campo "URL del webhook de paso directo para eventos de Meta" , en WhatsApp Setup → More.
- Agrega pronto en tu flujo de trabajo un nodo de filtro/condición para descartar los eventos que no sean de mensajes (actualizaciones de estado, confirmaciones de envío de plantillas) si tu flujo solo necesita mensajes entrantes.
- A partir de ahí, dirige los eventos a un nodo de agente de IA, un nodo de CRM o lo que necesite tu flujo de trabajo: podrás consultar la carga sin procesar de Meta en todo momento.
Este es también un caso en el que vale la pena agregar un filtro al inicio: sin él, cada evento de estado y entrega activa todo el flujo de trabajo innecesariamente, lo que agrega costos y ruido en las plataformas que cobran por ejecución.
Algunas notas prácticas antes de poner esto en marcha
- Responde rápido con
200y procesa de forma asíncrona. Confirma la recepción del webhook de inmediato y realiza cualquier tarea lenta (como escribir en la base de datos o llamar a otra API) después de responder o mediante un trabajo en segundo plano. Si tardas en responder, Meta o Chakra podrían considerar fallida la entrega y volver a intentarlo, lo que aumentaría la carga de gestionar duplicados. - Registra las cargas sin procesar durante el desarrollo. El esquema de Meta varía ligeramente según el tipo de mensaje, y tener ejemplos sin procesar a mano te ahorrará tiempo cuando encuentres un caso límite que tu enrutador aún no contempla.
- No guardes el secreto HMAC en el control de versiones, por supuesto, pero también rótalo si alguna vez sospechas que quedó expuesto: al regenerarlo en Admin → Team → Secrets, la firma anterior se invalida de inmediato.
- Vigila el tiempo de actividad de tu endpoint. Si tu webhook no está disponible cuando se genera un evento, perderás un mensaje o una actualización de estado, y no habrá una recuperación automática. Conviene supervisar este endpoint como cualquier otro servicio crítico para producción.
En resumen
Los webhooks son la base poco vistosa de la que depende todo lo demás en una integración con WhatsApp. Configura bien una sola vez el formato de los eventos, la verificación y la deduplicación, y todos los chatbots, sincronizaciones de CRM y automatizaciones que crees sobre esa base heredarán esa confiabilidad. Empieza con el tipo de webhook que mejor se adapte a tus herramientas (el formato normalizado de Chakra para backends personalizados, o el de paso directo para n8n y las herramientas nativas del esquema de Meta), verifica las firmas desde el primer día en lugar de incorporarlo después y deduplica por ID de mensaje antes de que tu enrutador realice cualquier acción que afecte el estado.
Si estás implementando esto, la documentación completa de la API de Chakra incluye la referencia completa de eventos y endpoints mencionada a lo largo de esta guía.