Saltar al contenido
OmniChannels.ai

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.

Actualizado 10 min de lectura

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
  1. ¿Qué son los webhooks de WhatsApp y por qué usarlos?
  2. ¿Qué eventos y estados recibiré?
  3. ¿Cómo configuro el endpoint?
  4. ¿Qué aspecto tiene la carga?
  5. ¿Cómo escribo un gestor fiable?
  6. ¿Cómo protejo un webhook de WhatsApp?
  7. ¿Qué ocurre cuando falla la entrega?
  8. ¿Cómo recupero los eventos que faltan?
  9. Ejemplo práctico: el flujo de notificaciones de un comercio
  10. ¿Cuáles son los errores habituales?
  11. 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.

  1. Cree un endpoint que acepte POST con un cuerpo JSON, por ejemplo https://api.example.com/webhooks/omnichannels.
  2. 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.
  3. 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 2xx solo después de guardar el evento. Si falla el almacenamiento, devuelva 5xx.
  • 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

  1. ¿Está configurada la URL en la cuenta o subcuenta correcta?
  2. ¿Se puede acceder al endpoint por HTTPS con un certificado válido?
  3. ¿Responde con 2xx con rapidez, o agota el tiempo de espera?
  4. ¿Está una regla de cortafuegos o de WAF bloqueando las peticiones?
  5. ¿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.

  1. Los pedidos se envían con sendbulk, y el batchId se guarda en el pedido. Consulte enviar un mensaje de WhatsApp por API.
  2. El endpoint del webhook escribe cada evento en una tabla y después devuelve 200.
  3. Un proceso de trabajo clasifica los estados y actualiza el estado del mensaje del pedido.
  4. Los eventos FAILED crean una tarea para soporte con el motivo, y se valora un respaldo por SMS según el respaldo entre canales.
  5. Los mensajes entrantes de los clientes se derivan a la bandeja compartida para que los agentes puedan responder dentro de la ventana.
  6. Un proceso nocturno llama a bulk-insights para 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 SENT tardío que sobrescribe un DELIVERED.
  • 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

  1. Webhooks overview — WhatsApp Business Platform, Meta for Developers
  2. Create a webhook endpoint — WhatsApp Business Platform, Meta for Developers
  3. RFC 2104: HMAC: Keyed-Hashing for Message Authentication