Guía de webhooks de WhatsApp: eventos, seguridad y reintentos
Cómo funcionan los webhooks de WhatsApp: eventos de mensajes y de estado, un gestor fiable, la firma de Meta, reintentos, idempotencia y eventos perdidos.
Por OmniChannels.ai Editorial Team · Revisado por BroadNet Messaging Solutions Team
Integración
Respuesta breve
Un webhook de WhatsApp es un endpoint HTTPS en su servidor que recibe eventos: los estados de entrega de los mensajes que usted envió y los mensajes que le envían los clientes. Con OmniChannels.ai, los estados llegan como SUBMITTED, SENT, DELIVERED, READ, FAILED o REJECTED. Guarde el cuerpo sin procesar, responda 2xx con rapidez, deduplique por ID de mensaje y procese de forma asíncrona. Meta firma sus propios webhooks; nuestra documentación no define ninguna cabecera de firma, así que proteja su endpoint a nivel de transporte y de aplicación.
En esta página
- ¿Qué son los webhooks de WhatsApp y por qué usarlos?
- ¿Qué eventos y estados recibiré?
- ¿Cómo configuro el endpoint?
- ¿Qué aspecto tiene la carga?
- ¿Cómo escribo un gestor fiable?
- ¿Cómo protejo un webhook de WhatsApp?
- ¿Qué ocurre cuando falla la entrega?
- ¿Cómo recupero los eventos que faltan?
- Ejemplo práctico: el flujo de notificaciones de un comercio
- ¿Cuáles son los errores habituales?
- Cómo le ayuda OmniChannels.ai
Puntos clave
- Los webhooks envían los estados y los mensajes entrantes a su endpoint HTTPS para que no tenga que consultar periódicamente; los estados de WhatsApp se normalizan a SUBMITTED, SENT, DELIVERED, READ, FAILED y REJECTED.
- Un gestor fiable hace tres cosas por este orden: guarda el evento sin procesar, devuelve 2xx y después procesa de forma asíncrona e idempotente.
- Los estados pueden llegar desordenados o dos veces, así que clasifíquelos y no deje nunca que un SENT tardío sobrescriba un DELIVERED guardado.
- Meta firma los webhooks enviados a las apps con una cabecera X-Hub-Signature-256, pero la especificación de la API de OmniChannels.ai no define ninguna firma de webhook, así que use HTTPS, una ruta imposible de adivinar y la validación del cuerpo.
- Si faltaron eventos, lea el estado de WhatsApp por lote en el endpoint bulk-insights en lugar de reenviar mensajes.
Esta guía está dirigida a los ingenieros que necesitan recibir eventos de WhatsApp de forma fiable: los estados de entrega de los mensajes que envían y los mensajes entrantes de los clientes. Explica cómo funcionan los webhooks, qué construir en el gestor, en qué se diferencia la seguridad entre los webhooks de Meta y los nuestros, y qué hacer cuando faltan eventos.
¿Qué son los webhooks de WhatsApp y por qué usarlos?
Los webhooks de WhatsApp son devoluciones de llamada HTTP. Cuando un mensaje cambia de estado o un cliente le escribe, la plataforma envía un POST con un cuerpo JSON a su endpoint. No tiene que consultar periódicamente, y ve los eventos a medida que ocurren.
Importan dos familias de eventos:
- Eventos de estado de los mensajes que usted envió: en cola, enviado, entregado, leído, fallido.
- Mensajes entrantes de los clientes, que abren o prolongan la ventana de atención al cliente de 24 horas. Consulte nuestra guía de la ventana.
Una llamada de envío solo le dice que los mensajes se pusieron en cola. El webhook es donde usted se entera de lo que ocurrió con cada destinatario.
¿Qué eventos y estados recibiré?
En OmniChannels.ai, todos los canales informan de los mismos estados normalizados, de modo que un único gestor puede dar servicio a WhatsApp, SMS y el resto.
| Estado | Significado | ¿Final? |
|---|---|---|
SUBMITTED |
Aceptado por la plataforma y puesto en cola | No |
SENT |
Entregado al canal | No |
DELIVERED |
Confirmado en el dispositivo del destinatario | Normalmente; puede seguir READ |
READ |
Abierto por el destinatario, donde el canal informa de las lecturas | Sí |
FAILED |
No entregado; el evento incluye un motivo cuando está disponible | Sí |
REJECTED |
Rechazado, por ejemplo por contenido bloqueado o por un remitente sin registrar | Sí |
Trate los estados como una progresión que puede llegar desordenada, y espere valores nuevos con el tiempo. No falle ante un estado que no reconozca. Consulte estados de entrega de mensajes explicados.
¿Cómo configuro el endpoint?
Exponga un endpoint HTTPS, facilite la URL a su proveedor y pruebe después con su propio número.
- Cree un endpoint que acepte
POSTcon un cuerpo JSON, por ejemplohttps://api.example.com/webhooks/omnichannels. - Envíe la URL a nuestro equipo o al administrador de su cuenta para configurarla en su cuenta. Los revendedores pueden tener un webhook distinto por subcuenta.
- Envíe un mensaje de prueba a su propio número y confirme que llegan los eventos.
Durante el desarrollo, un túnel como ngrok da a su servidor local una URL HTTPS pública. Si además conecta una app directamente a Meta, la verificación del endpoint de Meta es un paso aparte: una petición GET con un token de verificación y un valor hub.challenge, a la que usted responde 200 con el desafío, como se describe en la documentación del endpoint de webhook de Meta.
¿Qué aspecto tiene la carga?
Varía según el canal. La especificación de la API no define un único esquema de webhook, así que registre primero el cuerpo sin procesar y asigne los campos que necesite a partir de eventos reales de su cuenta. El ejemplo muestra el tipo de información que lleva un evento de entrega y es solo ilustrativo.
{
"messageId": "2603121407537573731",
"channel": "SMS",
"destination": "447700900123",
"status": "DELIVERED",
"timestamp": "2026-10-10T09:15:42Z",
"reason": null
}
No programe contra estos nombres de campo sin comprobar un evento real. Pida a su ingeniero de alta cargas de ejemplo por canal y consérvelas como datos de prueba. Para consultar la documentación más amplia, vea la referencia de webhooks.
¿Cómo escribo un gestor fiable?
Haga tres cosas por este orden: guarde el evento sin procesar, confirme con un 2xx y procese después de forma asíncrona. El gestor siguiente sigue el patrón de nuestra documentación.
import express from "express";
const app = express();
// Capture el cuerpo sin procesar para guardarlo exactamente como se recibió.
app.post(
"/webhooks/omnichannels",
express.json({ verify: (req, _res, buf) => { req.rawBody = buf.toString("utf8"); } }),
async (req, res) => {
try {
await db.webhookEvents.insert({ receivedAt: new Date(), body: req.rawBody });
} catch (err) {
return res.sendStatus(500); // falló el almacenamiento: deje que se reintente el evento
}
res.sendStatus(200); // confirme rápido
queue.publish("status-events", req.body); // haga el trabajo real en otro lugar
},
);
// Worker: procesamiento idempotente
async function processEvent(event) {
const id = event.messageId ?? event.message_id ?? event.id; // asigne según el canal
const status = event.status;
const rank = { SUBMITTED: 1, SENT: 2, DELIVERED: 3, READ: 4, FAILED: 4, REJECTED: 4 };
const current = await db.messages.findOne({ providerId: id });
if (!current) return; // mensaje desconocido: regístrelo e investigue
if ((rank[status] ?? 0) <= (rank[current.status] ?? 0)) return; // duplicado u obsoleto
await db.messages.update({ providerId: id }, { status, updatedAt: new Date() });
}
from datetime import datetime, timezone
from flask import Flask, request
app = Flask(__name__)
RANK = {"SUBMITTED": 1, "SENT": 2, "DELIVERED": 3, "READ": 4, "FAILED": 4, "REJECTED": 4}
@app.post("/webhooks/omnichannels")
def omnichannels_webhook():
raw = request.get_data(as_text=True)
try:
db.webhook_events.insert({"received_at": datetime.now(timezone.utc), "body": raw})
except Exception:
return "", 500 # falló el almacenamiento: deje que se reintente el evento
queue.publish("status-events", raw) # procese de forma asíncrona
return "", 200 # confirme rápido
Los objetos db y queue son su propio almacenamiento y su propia cola de trabajos. El mapa rank es lo que hace idempotente el procesamiento: un evento duplicado u obsoleto no supone un avance, por lo que se ignora.
Reglas para un gestor fiable
- Responda en pocos segundos. No haga trabajo pesado antes de responder.
- Devuelva
2xxsolo después de guardar el evento. Si falla el almacenamiento, devuelva5xx. - Deduplique por ID de mensaje. El mismo estado puede llegar más de una vez.
- No reenvíe nunca un mensaje desde el gestor. Decida los reintentos en un trabajo aparte, tras comprobar el estado.
- Conserve el cuerpo sin procesar para diagnosticar problemas y para reproducir eventos tras una corrección.
¿Cómo protejo un webhook de WhatsApp?
Depende de quién envíe a su endpoint. Meta firma los webhooks que envía a las apps que usted registra en Meta. La especificación de la API de OmniChannels.ai no define ninguna cabecera de firma de webhook, así que no espere a que exista.
| Meta a su propia app | OmniChannels.ai a su endpoint | |
|---|---|---|
| Firma | Cabecera X-Hub-Signature-256, según la documentación de Meta |
Ninguna definida en la especificación de la API |
| Verificación | HMAC-SHA256 de la carga sin procesar con el secreto de su app, comparado con la cabecera | No procede |
| Sus controles | Compruebe la firma en cada POST |
HTTPS con un certificado válido, una ruta imposible de adivinar, validación estricta del cuerpo |
Para el esquema de Meta, la documentación de Meta indica generar un hash HMAC-SHA256 a partir de la carga JSON con el secreto de su app y compararlo con el valor posterior a sha256= de la cabecera X-Hub-Signature-256. El propio HMAC está definido en la RFC 2104. Calcúlelo sobre los bytes sin procesar, no sobre un objeto reserializado, y compare en tiempo constante.
Para nuestro endpoint:
- Use solo HTTPS, con un certificado válido.
- Use una ruta larga e imposible de adivinar, y rótela si se filtra.
- Valide la forma del cuerpo y rechace todo lo inesperado.
- Trate los eventos como notificaciones. Antes de una acción irreversible, como un reembolso o un bloqueo por fraude, confirme el estado con una llamada de consulta.
¿Qué ocurre cuando falla la entrega?
Los reintentos dependen del remitente. La documentación de Meta dice que reintenta una entrega fallida con una frecuencia decreciente durante hasta 7 días, lo que puede producir notificaciones duplicadas. Es un motivo para ser idempotente aunque rara vez vea duplicados. Para los eventos de nuestra plataforma, confirme el comportamiento de los reintentos con su ingeniero de alta en lugar de suponer que coincide con el de Meta.
En cualquier caso, construya pensando tanto en la pérdida como en la duplicación: su endpoint puede estar caído durante un despliegue, y una regla de cortafuegos puede descartar peticiones en silencio.
¿Cómo recupero los eventos que faltan?
Consulte el lote, no reenvíe el mensaje.
| Necesidad | Llamada | Notas |
|---|---|---|
| Leer el estado de WhatsApp por destinatario | GET /whatsapp/message/bulk-insights |
Filtre un lote por status o destination; paginado con page y size |
| Reenviar los informes de entrega de SMS | POST /sms/resendDlr |
Filtre por startDate, endDate, senderId y messageStatus |
curl "https://api.broadnet.me/whatsapp/message/bulk-insights?batch_Id=2609091405000000001&page=0&size=100" \
-H "Authorization: Bearer $BROADNET_TOKEN"
batch_Id es el ID devuelto por la llamada de envío, y page empieza en cero. Ejecute una conciliación nocturna que lea los lotes del día anterior y rellene las lagunas.
Lista de verificación para solucionar problemas
- ¿Está configurada la URL en la cuenta o subcuenta correcta?
- ¿Se puede acceder al endpoint por HTTPS con un certificado válido?
- ¿Responde con
2xxcon rapidez, o agota el tiempo de espera? - ¿Está una regla de cortafuegos o de WAF bloqueando las peticiones?
- ¿Registra el cuerpo sin procesar antes de analizarlo, para que un error del analizador no pueda ocultar el evento?
Ejemplo práctico: el flujo de notificaciones de un comercio
"Souq Rafiq" (ficticia), un comercio de los Emiratos Árabes Unidos, envía actualizaciones de pedidos por WhatsApp y quiere un estado exacto en su herramienta de pedidos.
- Los pedidos se envían con
sendbulk, y elbatchIdse guarda en el pedido. Consulte enviar un mensaje de WhatsApp por API. - El endpoint del webhook escribe cada evento en una tabla y después devuelve
200. - Un proceso de trabajo clasifica los estados y actualiza el estado del mensaje del pedido.
- Los eventos
FAILEDcrean una tarea para soporte con el motivo, y se valora un respaldo por SMS según el respaldo entre canales. - Los mensajes entrantes de los clientes se derivan a la bandeja compartida para que los agentes puedan responder dentro de la ventana.
- Un proceso nocturno llama a
bulk-insightspara los lotes del día anterior y corrige lo que se haya perdido.
¿Cuáles son los errores habituales?
- Trabajo pesado antes de responder, que provoca tiempos de espera agotados y entregas repetidas.
- Confirmar antes de guardar, que pierde eventos cuando falla el almacenamiento.
- Fiarse del orden. Un
SENTtardío que sobrescribe unDELIVERED. - Programar contra la carga ilustrativa en lugar de contra eventos reales.
- Reserializar el JSON antes de comprobar una firma, lo que cambia los bytes.
- Reenviar desde el gestor, lo que puede crear un bucle.
- Límites: esta guía describe nuestro comportamiento documentado. La política de reintentos y los campos de la carga de su cuenta deben confirmarse durante el alta.
Cómo le ayuda OmniChannels.ai
OmniChannels.ai envía a su webhook, en un único formato normalizado, los estados de entrega y los mensajes entrantes de WhatsApp, SMS, RCS y otros canales, con webhooks distintos por subcuenta para los revendedores. BroadNet es un Meta Business Solution Provider oficial, por lo que los requisitos de webhook de la propia Meta se gestionan por usted, y usted trabaja con nuestra API. Lea la referencia de webhooks y la documentación de la API de WhatsApp, o vea cómo aparecen los eventos en la bandeja compartida.
Preguntas frecuentes
¿Qué es un webhook de WhatsApp?
Un webhook de WhatsApp es una URL de su servidor a la que la plataforma llama con un HTTP POST cada vez que ocurre algo, como que un mensaje se entregue, se lea o falle, o que un cliente le envíe un mensaje. Sustituye a la consulta periódica (polling). Usted expone un endpoint HTTPS, lo comparte con su proveedor y procesa los eventos JSON que recibe.
¿Cómo verifico la firma de un webhook de WhatsApp?
Para un webhook que Meta envía directamente a su propia app, la documentación de Meta indica calcular un HMAC-SHA256 de la carga sin procesar con el secreto de su app y compararlo con la cabecera X-Hub-Signature-256. Para el webhook de OmniChannels.ai, la especificación de la API no define ninguna cabecera de firma, así que confíe en HTTPS, una ruta imposible de adivinar y la validación del cuerpo.
¿Qué ocurre si mi endpoint de webhook está caído?
La documentación de Meta dice que reintenta las entregas fallidas con una frecuencia decreciente durante hasta 7 días, lo que puede producir duplicados. Para los eventos de nuestra plataforma, confirme el comportamiento de los reintentos con su ingeniero de alta, y use el endpoint bulk-insights para leer el estado de WhatsApp por destinatario de un lote. Para el SMS, el endpoint resendDlr vuelve a enviar los informes.
¿Cómo gestiono los eventos de webhook duplicados?
Haga que el procesamiento sea idempotente. Busque el mensaje por su ID, compare el rango del estado entrante con el almacenado e ignore los eventos que no supongan un avance. Aplicar el mismo evento dos veces debe tener el mismo efecto que aplicarlo una. Guarde el cuerpo sin procesar de cada evento para poder reproducirlo tras corregir un error.
¿Con qué rapidez debe responder mi endpoint?
En pocos segundos. Guarde el evento sin procesar, devuelva un 2xx y haga el trabajo real en una cola o en un proceso de trabajo. No llame a otros servicios, no envíe mensajes ni ejecute consultas pesadas antes de responder. Si falla el almacenamiento, devuelva un 5xx para que el remitente pueda reintentar, en lugar de confirmar un evento que ha perdido.
¿Puedo usar un webhook para enviar una respuesta automática?
Sí, pero no desde dentro del gestor. Confirme el evento y decida la respuesta en un trabajo aparte. Dentro de la ventana de atención al cliente de 24 horas puede enviar un mensaje de texto libre; fuera de ella necesita una plantilla aprobada. No reenvíe nunca un mensaje desde el propio gestor del webhook, para evitar bucles y duplicados.
Fuentes
Siga leyendo
Más guíasIntegración
Enviar mensajes de WhatsApp por API: cURL, Node, Python y PHP
Cómo enviar un mensaje de WhatsApp por API: ejemplos en cURL, Node.js, Python y PHP para plantillas y mensajes de sesión, con seguimiento de estados y errores.
10 min de lectura
Integración
Integración de WhatsApp con CRM: 3 formas de conectar y sincronizar
Integración de WhatsApp con tu CRM: conectores nativos, middleware o API y webhooks, con flujo de datos, mapeo de campos, consentimiento y una lista para elegir.
10 min de lectura
WhatsApp
Mensajes gratuitos de la API de WhatsApp Business en 2026
Qué mensajes de la API de WhatsApp Business son gratuitos en 2026: 1.000 mensajes de servicio por número, ventana de entrada gratuita de 72 horas y qué se cobra.
9 min de lectura
