Documentación de referencia de webhook de WhatsApp — tabla de campos de suscripción, especificación de header HMAC y línea de tiempo de reintentos sobre fondo oscuro de terminal de desarrollador
Secciones: Fundamentos de webhook · Referencia completa de propiedades · Todos los campos de suscripción · Eventos por campo · Programa de reintentos · Comportamiento de respuesta HTTP · Especificación HMAC-SHA256 · WABA vs nivel de teléfono · Entrega at-least-once · Esquemas de payload · Lista de verificación de seguridad · Catálogo de errores · Formato normalizado de SocialHook · FAQ

Fundamentos de webhook

La WhatsApp Cloud API entrega eventos a tu aplicación a través de un sistema de webhook basado en push. En lugar de consultar un endpoint en busca de nuevos eventos, los servidores de Meta envían mediante POST un payload JSON a una URL que registras — tu endpoint de webhook. Tu servidor procesa el payload y devuelve HTTP 200 para confirmar la recepción.

Dos métodos HTTP operan en la misma URL:

  • GET — desafío de verificación único durante el registro. Tu endpoint debe devolver el parámetro de consulta hub.challenge como texto plano.
  • POST — notificaciones de eventos en vivo. Cada mensaje entrante, actualización de estado y evento de cuenta llega aquí.

Las restricciones más importantes que debes internalizar antes de escribir cualquier código:

  • Responder dentro de 20 segundos — cualquier cosa más lenta desencadena un reintento. Confirma inmediatamente, procesa de forma asíncrona.
  • Entrega at-least-once — el mismo evento puede llegar más de una vez. Tu handler debe ser idempotente.
  • Sin garantía de orden — los eventos pueden llegar fuera de secuencia. Nunca asumas orden cronológico.
  • Límite de tamaño de payload de 3 MB — los payloads individuales de webhook no excederán 3 MB. Los archivos multimedia no se incluyen en línea.
  • HTTPS requerido — Meta rechaza endpoints HTTP simples. Se requiere certificado SSL válido.

Referencia completa de propiedades de webhook

Propiedad Valor / Especificación Notas
ProtocoloSolo HTTPSSe requiere certificado SSL válido. HTTP rechazado por completo.
DirecciónProveedor → Consumidor (push)Meta hace push hacia ti. No se requiere polling.
Método de verificaciónGET + hub.verify_tokenÚnico durante el registro. Devolver hub.challenge como texto plano.
Autenticación en eventosX-Hub-Signature-256 HMAC-SHA256Firmado con App Secret. Verificar cada POST.
Método HTTP para eventosPOSTSiempre POST. Nunca GET para eventos en vivo.
Formato de datosJSONContent-Type: application/json
Timeout de respuesta20 segundosExcederlo desencadena reintento. Devolver 200 inmediatamente.
Criterio de éxitoHTTP 200Cualquier no-200 desencadena mecanismo de reintento.
Límite de tamaño de payload3 MBMedios no en línea — referenciados por ID.
Duración de reintentoHasta 7 díasBackoff exponencial. Ver programa completo abajo.
Garantía de entregaAt-least-onceDuplicados posibles. Hacer handler idempotente.
Garantía de ordenNingunaLos eventos pueden llegar fuera de orden cronológico.
Niveles de webhookNúmero de teléfono + WABAEl número de teléfono tiene prioridad sobre el fallback de WABA.
Replay manualNo soportadoSin replay nativo. Eventos perdidos después de 7 días.
Algoritmo de firmaHMAC-SHA256Clave = App Secret. Entrada = bytes brutos del body.
Header de firmaX-Hub-Signature-256Formato: sha256=<hex>
Entregas concurrentesMúltiples por segundoNúmeros de alto volumen reciben muchos eventos/segundo.

Todos los campos de suscripción de webhook

Te suscribes a campos individuales en el Meta Developer Dashboard (WhatsApp → Configuration → Webhooks → Manage). Suscribirse a messages es obligatorio para la mayoría de las integraciones. Cada campo representa una categoría de eventos — suscríbete solo a lo que necesites para reducir el volumen de payload.

Campo Cobertura Prioridad
messages Todos los mensajes entrantes de clientes + todas las actualizaciones de estado salientes (sent / delivered / read / failed). El campo principal para cualquier integración de mensajería. Suscribirse siempre
account_update Violaciones de política (ACCOUNT_VIOLATION) y restricciones activas (ACCOUNT_RESTRICTION) en tus números de teléfono. Esencial para monitoreo en producción. Recomendado
message_template_status_update Aprobación, rechazo, pausa y desactivación de templates. Se dispara cuando Meta cambia el estado de un template que has enviado. Recomendado
phone_number_quality_update Cambios en la calificación de calidad (GREEN / YELLOW / RED) para tus números de teléfono. La calidad afecta tu tier de mensajería. Suscribirse para detectar degradación temprano. Recomendado
phone_number_name_update Eventos de aprobación o rechazo del nombre para mostrar. Se dispara cuando Meta revisa un nombre que has enviado para tu número de WhatsApp Business. Situacional
business_capability_update Cambios de tier de límite de mensajería — cuando Meta mejora o degrada tu tier diario de conversaciones (1K / 10K / 100K / Unlimited). Situacional
flows Interacciones de WhatsApp Flows — eventos de envío de datos cuando un usuario completa o interactúa con un Flow adjunto a tu número. Si usas Flows
security Eventos de PIN de verificación en dos pasos. Se dispara cuando se cambia o desactiva el PIN de un número de teléfono. Opcional
message_template_components_update Se dispara cuando Meta modifica los componentes de un template aprobado (raro — Meta puede ajustar templates para cumplir con políticas). Opcional
account_alerts Alertas de facturación, advertencias de capacidad y otros avisos operativos a nivel de cuenta de Meta. Opcional

Eventos entregados por campo de suscripción

Campo messages — tipos de mensajes entrantes

El campo messages entrega dos categorías: mensajes entrantes de clientes (identificados por un array messages en el objeto value) y actualizaciones de estado de entrega salientes (identificadas por un array statuses). Verifica qué array está presente antes de procesar.

msg.typeUbicación del contenidoNotas
textmsg.text.bodyCuerpo de mensaje de texto plano. Puede incluir URLs.
imagemsg.image.id, .mime_type, .captionID → resolver URL → descargar. Caption opcional.
audiomsg.audio.id, .voicevoice: true si se grabó en la app. Siempre descargar inmediatamente.
videomsg.video.id, .captionCaption opcional.
documentmsg.document.id, .filename, .mime_typeFilename incluido — guárdalo.
stickermsg.sticker.id, .animatedEl flag animated distingue WebP vs sticker animado.
locationmsg.location.latitude, .longitude, .name, .addressName y address son opcionales.
contactsmsg.contacts[n].name, .phonesArray — el cliente puede compartir múltiples contactos.
reactionmsg.reaction.emoji, .message_idReferencia el ID del mensaje al que reaccionó el cliente.
interactivemsg.interactive.type, luego .button_reply o .list_replyVerifica type antes de leer el sub-objeto.
ordermsg.order.catalog_id, .product_itemsWhatsApp Commerce — pedido de producto desde catálogo.
systemmsg.system.body, .typeEventos del sistema: cliente cambió de número, etc.
buttonmsg.button.text, .payloadToque de botón de respuesta rápida desde un mensaje template.
referralmsg.referral.source_url, .source_type, .source_idDatos de referencia de anuncio Click-to-WhatsApp junto al mensaje.

Campo messages — actualizaciones de estado salientes

valor de statusSignificadoCampos adicionales
sentMensaje aceptado por Meta y reenviado a WhatsApp. Aún no entregado al dispositivo.timestamp, recipient_id, conversation, pricing
deliveredEl mensaje llegó al dispositivo del destinatario (doble tick gris).timestamp, recipient_id, conversation, pricing
readEl destinatario abrió el chat (doble tick azul). Solo se dispara si las confirmaciones de lectura están activadas.timestamp, recipient_id
failedEntrega fallida permanentemente. Revisa status.errors[0].code para el error específico.array errors con code y title

Programa exacto de reintentos con tiempos de backoff

Meta reintenta entregas de webhook fallidas hasta por 7 días usando backoff exponencial. "Fallido" significa que tu endpoint devolvió no-200, tuvo timeout (tardó más de 20 segundos) o fue inalcanzable. Después de 7 días, el evento se descarta permanentemente — no existe ruta de recuperación nativa.

1Inmediato (primera entrega)
2~5 segundos~5 segundos después del primer intento
3~30 segundos~35 segundos
4~2 minutos~2.5 minutos
5~10 minutos~12 minutos
6~30 minutos~42 minutos
7~2 horas~2 horas 42 minutos
8~6 horas~8.5 horas
9~12 horas~20 horas
10~24 horas~44 horas
11+~24 horasCada 24h hasta 7 días
FinalDespués de 7 díasEvento descartado permanentemente — sin recuperación
Implicación práctica: Si tu servidor está caído por mantenimiento, los eventos se encolan en Meta hasta por 7 días. Cuando tu servidor vuelve en línea y comienza a devolver 200, Meta entrega el backlog — potencialmente un burst de miles de eventos llegando simultáneamente. Diseña tu handler de webhook y tu queue para absorber bursts de alta concurrencia sin perder eventos o causar fallos en cascada.

Comportamiento de códigos de respuesta HTTP

Lo que devuelves a Meta determina si el evento se considera entregado, reintentado o si desencadena una alerta. El árbol de decisiones es más simple de lo que la mayoría de desarrolladores espera — todo es binario: 200 significa éxito, cualquier otra cosa significa reintento.

200
Confirmado — entrega completa
Meta considera el evento entregado. Sin reintento programado. Devuelve 200 inmediatamente después de la verificación HMAC, independientemente de si has terminado el procesamiento. Mueve todo el procesamiento a una queue asíncrona.
403
Prohibido — desencadena reintento
Devuelve 403 cuando falla la verificación HMAC. Meta trata esto como una entrega fallida y programa un reintento. Útil para rechazar explícitamente solicitudes que no coinciden con tu firma — aunque en la práctica, devuelve 200 para solicitudes de seguridad que pasan la verificación y 403 solo para intentos genuinos de falsificación.
4xx (otros)
Error de cliente — desencadena reintento
Cualquier respuesta 4xx desencadena el mecanismo de reintento de Meta. Nunca devuelvas 4xx para errores de lógica de negocio dentro de payloads válidos — siempre devuelve 200 para confirmar recepción y maneja el error en tu aplicación. Un 400 o 404 de tu handler es indistinguible de un error de configuración de servidor desde la perspectiva de Meta.
5xx
Error de servidor — desencadena reintento
Errores genuinos de servidor (crash, OOM, excepción no manejada) devuelven 5xx. Meta reintenta. El punto crítico de diseño: nunca dejes que errores de lógica de negocio emerjan como 5xx. Envuelve todo el procesamiento en try-catch, devuelve 200 para confirmar recepción y registra el error internamente. 5xx solo debe dispararse en fallos genuinos de infraestructura.
Timeout
Sin respuesta en 20s — desencadena reintento
Si tu handler tarda más de 20 segundos en responder, Meta lo trata como un fallo. Este es el problema de producción más común. Cada operación síncrona (escritura en base de datos, llamada a LLM, solicitud HTTP) dentro de tu handler de webhook es un riesgo de timeout. Devuelve 200 en <100ms. Empuja todo lo demás a un worker de queue.

Especificación completa de firma HMAC-SHA256

Cada solicitud POST de Meta incluye una firma criptográfica que te permite verificar que la solicitud genuinamente vino de Meta y no fue manipulada en tránsito. Omitir esta verificación significa que cualquier atacante que descubra tu URL de webhook puede alimentar datos arbitrarios a tu aplicación.

Formato del header

Especificación de header
X-Hub-Signature-256
X-Hub-Signature-256: sha256=a1b2c3d4e5f6... Formato: sha256= + hex_digest (hex en minúsculas, 64 caracteres) Clave: Tu App Secret (NO tu access token) Mensaje: Bytes brutos del body de la solicitud — ANTES de cualquier parsing JSON Ubicación: Meta Developer Dashboard → App Settings → Basic → App Secret

Verificación en Node.js

Node.js
verify.js
const crypto = require('crypto'); function verifyWebhook(rawBody, signatureHeader, appSecret) { if (!signatureHeader?.startsWith('sha256=')) return false; const received = signatureHeader.slice(7); // quitar 'sha256=' const expected = crypto .createHmac('sha256', appSecret) .update(rawBody) // ← Buffer crudo, NO JSON parseado .digest('hex'); try { return crypto.timingSafeEqual( // previene ataques de timing Buffer.from(received, 'hex'), Buffer.from(expected, 'hex') ); } catch { return false; // longitud no coincide → inválido } } // Uso en Express — requiere middleware express.raw() app.post('/webhook', (req, res) => { const valid = verifyWebhook( req.body, // Buffer crudo req.headers['x-hub-signature-256'], // header process.env.WHATSAPP_APP_SECRET ); if (!valid) return res.sendStatus(403); res.sendStatus(200); enqueue(JSON.parse(req.body)); });

Verificación en Python

Python
verify.py
import hmac, hashlib def verify_webhook(raw_body: bytes, sig_header: str, app_secret: str) -> bool: if not sig_header.startswith("sha256="): return False received = sig_header[7:] # quitar prefijo 'sha256=' expected = hmac.new( app_secret.encode(), raw_body, # ← bytes crudos, NO decodificados/parseados hashlib.sha256 ).hexdigest() return hmac.compare_digest(received, expected) # seguro contra timing
El bug #1 de HMAC: calcular la firma después del parsing JSON. JSON.stringify(JSON.parse(body)) produce bytes diferentes al original — espacios en blanco, orden de claves y precisión de números pueden cambiar. Siempre calcula HMAC sobre los bytes exactos que llegaron en la solicitud HTTP, antes de cualquier transformación.

Webhooks a nivel de WABA vs nivel de número de teléfono

La WhatsApp Cloud API soporta dos niveles de configuración de webhook que interactúan en un orden de prioridad específico. Entender esto previene la pérdida silenciosa de eventos al gestionar múltiples números de teléfono.

AspectoWebhook de número de teléfonoWebhook de WABA
Ubicación de configuraciónMeta Developer Dashboard → WhatsApp → ConfigurationMeta Business Settings → WhatsApp Accounts → [WABA] → Webhook
AlcanceUn número de teléfono específicoTodos los números de teléfono en el WABA
PrioridadMayor — tiene precedenciaMenor — solo fallback
Comportamiento de fallbackSi está configurado, el webhook de WABA no recibe eventos para este númeroRecibe eventos para números sin webhook de número de teléfono
Mejor paraIntegraciones de un solo número, lógica de enrutamiento por númeroOperaciones multi-número, agencia gestionando muchos números de clientes
Enrutamiento de eventosLos eventos van solo a esta URLLos eventos para todos los números no configurados llegan aquí
Campos de suscripciónConfigurados por separadoConfigurados por separado
Patrón para agencias: Configura un webhook a nivel de WABA apuntando a un endpoint central que despacha eventos por metadata.phone_number_id. Añade un webhook a nivel de número de teléfono solo para números que necesiten enrutamiento diferente. Esto es más limpio que mantener webhooks separados por número de cliente, y SocialHook soporta múltiples números por cuenta bajo un único stream de eventos normalizado.

Entrega at-least-once — implicaciones y deduplicación

El sistema de webhook de Meta garantiza que un evento dado será entregado al menos una vez. No garantiza exactamente una vez. El escenario de duplicado: tu servidor procesa un evento, devuelve 200, pero la confirmación se pierde en tránsito antes de que el sistema de Meta la registre. Meta reintenta. Tu handler procesa el mismo evento nuevamente.

Para un bot de eco simple esto es intrascendente. Para un agente de IA que desencadena una acción de CRM, un pago o un mensaje saliente — los duplicados causan problemas reales. Tu handler debe ser idempotente.

Patrón de deduplicación

Node.js + Redis
dedup.js
const redis = getRedisClient(); const DEDUP_TTL = 86400; // 24 horas — más largo que la ventana de reintento de 7 días por seguridad async function processIfNew(messageId, processFn) { const key = `whatsapp:dedup:${messageId}`; // SET ... NX — solo establece si la clave no existe const isNew = await redis.set(key, '1', 'EX', DEDUP_TTL, 'NX'); if (!isNew) { console.log(`Evento duplicado omitido: ${messageId}`); return; // ya procesado — omisión idempotente } await processFn(); } // En tu worker: await processIfNew(msg.id, () => handleMessage(msg));

El valor msg.id (la cadena wamid.HBgL...) es único por mensaje. Las actualizaciones de estado usan el ID del mensaje saliente. Las reacciones y otros tipos de evento tienen sus propios IDs únicos. Siempre usa el ID específico del evento — nunca un valor derivado — como tu clave de deduplicación.

Esquemas de payload para tipos de evento clave

Envelope de nivel superior (todos los eventos)

JSON
top-level-envelope.json
{ "object": "whatsapp_business_account", // siempre esta cadena "entry": [{ // array — normalmente 1 entrada "id": "WABA_ID", "changes": [{ "field": "messages", // nombre del campo de suscripción "value": { /* datos específicos del evento */ } }] }] }

Mensaje de texto entrante (objeto value)

JSON
inbound-text-value.json
{ "messaging_product": "whatsapp", "metadata": { "display_phone_number": "+1 555 000 1234", "phone_number_id": "PHONE_NUMBER_ID" // usar para enrutamiento en setups de WABA }, "contacts": [{ "profile": { "name": "Alice" }, "wa_id": "15550002345" }], "messages": [{ "id": "wamid.HBgL...", // ID de mensaje único — usar para dedup "from": "15550002345", // sin prefijo + "timestamp": "1747231892", // cadena — parseInt() antes de usar "type": "text", "text": { "body": "Hello!" } }] }

Actualización de estado de mensaje saliente (objeto value)

JSON
status-update-value.json
{ "messaging_product": "whatsapp", "metadata": { "display_phone_number": "...", "phone_number_id": "..." }, "statuses": [{ // nota: 'statuses' no 'messages' "id": "wamid.HBgL...", // tu ID de mensaje saliente "status": "delivered", // sent | delivered | read | failed "timestamp": "1747231900", "recipient_id": "15550002345", "conversation": { "id": "CONVERSATION_ID", "origin": { "type": "service" } // categoría de precio }, "pricing": { "billable": true, "pricing_model": "CBP", "category": "service" } }] }

Lista de verificación de seguridad

Verificar firma HMAC-SHA256 en cada POST
Rechazar con 403 cualquier solicitud donde el header X-Hub-Signature-256 falte, esté malformado o no coincida. Sin esto, tu endpoint acepta eventos falsificados de cualquiera.
Usar comparación segura contra timing para coincidencia de firma
crypto.timingSafeEqual() en Node.js, hmac.compare_digest() en Python. La igualdad simple de cadenas (=== / ==) filtra información de timing que los atacantes pueden explotar para falsificar firmas carácter por carácter.
Parsear body crudo antes de HMAC, no después
Calcular HMAC sobre los bytes brutos del body de la solicitud. El parsing JSON altera la representación de bytes. En Express: middleware express.raw() en la ruta de webhook. En FastAPI: await request.body() antes de cualquier deserialización JSON.
Almacenar App Secret en variables de entorno, nunca en código fuente
El App Secret es la clave de firma HMAC. Cualquiera que lo tenga puede falsificar firmas de webhook válidas. Almacenar en process.env.WHATSAPP_APP_SECRET (Node.js) o os.environ["WHATSAPP_APP_SECRET"] (Python). Rotar inmediatamente si se filtra.
Implementar procesamiento de eventos idempotente
La entrega at-least-once significa que llegarán eventos duplicados. Almacenar valores msg.id procesados en Redis con TTL de 24h+. Verificar antes de procesar. Omitir duplicados silenciosamente — nunca generar error por ellos.
Implementar rate limiting en tu endpoint de webhook
Durante operación normal, Meta envía bursts durante períodos de alto tráfico. Implementar rate limiting por IP que permita la tasa de entrega legítima de Meta pero limite solicitudes de alto volumen inesperadas desde IPs desconocidas. 1000 req/min es un techo razonable para la mayoría de integraciones.
Usar un verify token no adivinable
El verify token se verifica solo durante el registro, pero usar una cadena predecible ("mytoken", "test", "whatsapp") hace que tu setup sea más fácil de sondear. Generar una cadena hex aleatoria de 32 bytes: crypto.randomBytes(32).toString('hex').
Monitorear tu ventana de reintento de 7 días con alertas
Si tu endpoint de webhook es inalcanzable, los eventos se encolan silenciosamente en Meta por 7 días. Después de eso — desaparecen. Configurar monitoreo de uptime (Uptime Robot, Better Uptime o similar) con alertas en fallos HTTP en tu URL de webhook. Notificar en el primer fallo; no esperar reportes de mensajes perdidos.

Errores comunes y soluciones

Error / SíntomaCausa raízSolución
Verificación falla en registro Devolver JSON en lugar de texto plano, verify token incorrecto, o devolver hub.challenge envuelto en un objeto JSON Devolver hub.challenge como texto plano con Content-Type: text/plain. Cadena exacta — sin wrapping JSON.
HMAC siempre falla Calcular HMAC después del parsing JSON, usar access token en lugar de App Secret, doble codificación del body Usar express.raw() en Express. Llamar await request.body() en FastAPI antes de parsear. Clave = App Secret desde App Settings → Basic.
Los eventos no llegan en absoluto Campo messages no suscrito, o webhook no guardado/verificado Dashboard → WhatsApp → Configuration → Webhooks → Manage → habilitar campo messages. Confirmar que el webhook muestra como verificado.
Recibir el mismo evento múltiples veces Entrega at-least-once — comportamiento esperado, no un bug Implementar deduplicación usando msg.id como clave de Redis con TTL de 24h. Verificar antes de procesar.
Eventos llegando fuera de orden Sin garantía de orden — por diseño Usar campo timestamp para ordenar al construir hilos de conversación. Nunca asumir orden de entrega secuencial.
Los reintentos de Meta siguen llegando Handler devolviendo no-200, timeout (>20s), o crash Devolver 200 inmediatamente. Mover todo el procesamiento a queue asíncrona. Envolver handler en try-catch. Monitorear estabilidad del servidor.
Descarga de medios devuelve 401 Usando token incorrecto o token expirado Las descargas de medios requieren el mismo access token usado para la API. Asegurar que es un token de System User permanente, no un token de usuario temporal. Verificar que el token no haya sido revocado.
Array messages falta en payload Es una actualización de estado — el array es statuses, no messages Verificar value.messages Y value.statuses por separado. Ambos llegan bajo la suscripción del campo messages.
El número del remitente no tiene prefijo + Cloud API entrega from sin el prefijo + (ej. 15550001234 no +15550001234) Normalizar al recibir: '+' + msg.from para obtener formato E.164. SocialHook normaliza esto automáticamente.
Timestamp es una cadena, no un entero Cloud API entrega timestamp como cadena de timestamp Unix Siempre parseInt(msg.timestamp, 10) (Node.js) o int(msg["timestamp"]) (Python) antes de operaciones de fecha.

SocialHook: la capa de webhook gestionada

Todo en esta referencia — verificación HMAC, extracción de payload desde la envelope anidada, parsing de timestamp, normalización del remitente, manejo de reintentos, infraestructura de deduplicación, enrutamiento WABA vs nivel de teléfono — es trabajo de infraestructura en el que tu producto no se diferencia.

SocialHook maneja toda la capa de webhook de Cloud API y entrega un evento normalizado a tu endpoint, para que tu código de aplicación solo vea JSON limpio y consistente — nunca el payload crudo anidado de Meta con sus peculiaridades.

AspectoCloud API crudoSocialHook normalizado
Extracción de mensajeentry[0].changes[0].value.messages[0]Objeto message plano en nivel superior
Formato del remitente"15550001234" — sin prefijo +"+15550001234" — normalizado E.164
Timestamp"1747231892" — cadena1747231892 — entero
Verificación HMACTú lo implementasHecho — signature_verified: true
Reintento en tu downtimeMeta reintenta (7 días)SocialHook reintenta (3x exponencial)
Formato multi-canalEsquema diferente por plataformaMismo esquema: WhatsApp + FB + Instagram
Logs de entregaNo disponiblesLog completo por evento
Costo mensualTus costos de infraestructura de servidor$50 fijo

SocialHook cubre WhatsApp, Facebook Messenger y Instagram DMs — los tres canales de mensajería de Meta — bajo una cuenta, una URL de webhook, un formato de payload normalizado. Consulta la referencia completa de payload o comienza con el quickstart de 5 minutos.

Preguntas frecuentes

¿Qué campos de suscripción de webhook soporta la WhatsApp Cloud API?
10+ campos: messages (mensajes entrantes + estado saliente), account_update (violaciones, restricciones), message_template_status_update (aprobación/rechazo), phone_number_quality_update (calificación de calidad), phone_number_name_update (nombre para mostrar), business_capability_update (cambios de tier), flows (interacciones de WhatsApp Flows), security (cambios de 2FA), message_template_components_update (modificaciones de template) y account_alerts. Suscríbete por campo en el Meta Developer Dashboard.
¿Cuánto tiempo reintenta WhatsApp las entregas de webhook fallidas?
Hasta 7 días con backoff exponencial: ~5s → ~30s → ~2m → ~10m → ~30m → ~2h → ~6h → ~12h → ~24h, luego cada 24h hasta 7 días. Después de 7 días, el evento se descarta permanentemente sin ruta de recuperación nativa disponible.
¿Qué es la entrega at-least-once y cómo la manejo?
Meta garantiza que los eventos se entregan al menos una vez pero puede entregar el mismo evento más de una vez. Haz tu handler idempotente: almacena el msg.id en Redis con TTL de 24h usando SET ... NX. Antes de procesar cualquier evento, verifica si el ID existe. Si existe, omítelo. Si no, añádelo y procesa. Esto previene registros duplicados en CRM, respuestas dobles de IA y pagos duplicados.
¿Cuál es el formato del header X-Hub-Signature-256?
Formato: sha256=<hex en minúsculas de 64 caracteres>. El valor hex es HMAC-SHA256 de los bytes brutos del body de la solicitud, usando tu App Secret como clave (encuéntralo en App Settings → Basic — esto es diferente de tu access token). Quita el prefijo sha256= antes de comparar. Usa siempre comparación segura contra timing.
¿Por qué los mensajes entrantes y las actualizaciones de estado están en el mismo campo de webhook?
Tanto los mensajes entrantes como las actualizaciones de estado salientes (sent/delivered/read/failed) se entregan bajo el campo de suscripción messages, pero difieren en el objeto value: los eventos entrantes contienen un array messages, las actualizaciones de estado contienen un array statuses. Siempre verifica qué array está presente antes de procesar — intentar leer value.messages[0] en un payload de actualización de estado devuelve undefined.
¿Cuál es la diferencia entre webhooks a nivel de WABA y a nivel de número de teléfono?
Los webhooks de número de teléfono se configuran por número y tienen prioridad. Los webhooks de WABA se configuran a nivel de WhatsApp Business Account y actúan como fallback para números sin webhook de número de teléfono. Para agencias que gestionan múltiples números de clientes, configura un webhook a nivel de WABA como receptor central que despacha por metadata.phone_number_id, y añade webhooks a nivel de número de teléfono solo donde se necesite enrutamiento diferente.

Referencia leída.
Ahora recibe el primer webhook.

Conoces la especificación. SocialHook maneja la verificación HMAC, normalización de payload, lógica de reintentos y logging de entrega — para que tu código de aplicación solo vea JSON limpio. Conecta tu número en menos de 5 minutos.

No se requiere tarjeta de crédito · $50/mes después del trial · Cancelar en cualquier momento