Intermedio → avanzado
10–14 horas
10 módulos completos
Cómo estudiar este curso
Requisitos previos
Debes manejar HTTP/HTTPS, JSON, APIs REST, autenticación Bearer, conceptos básicos de backend y una base de datos. Los ejemplos usan cURL y pseudocódigo para que puedas trasladarlos a Laravel, Node.js, Python, Java u otro stack.
vXX.X como versión deliberadamente. Utiliza una versión vigente de Graph API y revisa su calendario de soporte antes de producción.Módulo 1. Arquitectura de Cloud API y Graph API
1.1 Modelo cliente-servidor
Cloud API está alojada por Meta. Tu backend realiza llamadas HTTPS a graph.facebook.com/{version}/... y recibe eventos en un endpoint HTTPS propio. Meta administra la infraestructura de WhatsApp; tú administras identidad interna, lógica de negocio, credenciales, almacenamiento, reintentos y experiencia de agentes.
1.2 Separación de plano de control y mensajería
Conceptualmente hay dos grupos de operaciones. El plano de mensajería envía mensajes, media y estados. El plano de administración gestiona activos como números, plantillas, suscripciones a webhooks y Flows. Separarlos en servicios o módulos internos facilita permisos y auditoría.
1.3 Contrato interno
No permitas que toda tu aplicación conozca directamente la forma de Graph API. Crea una capa de integración —por ejemplo WhatsAppGateway— que normalice solicitudes, errores y IDs. Así puedes actualizar versión, headers o proveedor sin modificar el dominio comercial completo.
Usuario WhatsApp
↓ evento
Meta / Cloud API ───→ Webhook Controller ─→ Cola de eventos ─→ Procesador
↑ ↓
└──────── POST /{PHONE_NUMBER_ID}/messages ← Servicio de envío
↓
CRM / BandejaPráctica del módulo
Comprueba lo aprendido
¿Por qué poner una cola detrás del webhook?
Permite responder rápido a Meta, desacoplar procesamiento pesado y reintentar fallos sin bloquear la recepción.
¿Por qué encapsular Graph API?
Reduce acoplamiento y centraliza versionado, autenticación, errores, observabilidad y políticas de reintento.
Módulo 2. Activos, IDs y entornos
2.1 IDs principales
WABA ID identifica la WhatsApp Business Account; Phone Number ID identifica un número dentro de Cloud API; App ID identifica tu aplicación de Meta; los mensajes usan IDs del tipo wamid...; la media y los Flows tienen sus propios IDs. Nunca asumas que un número E.164 puede reemplazar un Phone Number ID en un endpoint.
2.2 Consultar números de una WABA
La colección oficial de Meta expone GET /{WABA-ID}/phone_numbers para listar los números asociados a una WABA. La respuesta puede incluir el nombre verificado, número visible, ID y calidad. Usa este endpoint para inventario o sincronización, no para descubrir el ID en cada mensaje.
GET https://graph.facebook.com/vXX.X/{WABA_ID}/phone_numbers
Authorization: Bearer {TOKEN}2.3 Entornos
Separa desarrollo, staging y producción en configuración y, cuando sea posible, en activos de prueba. No publiques un webhook de desarrollo que escriba en la base de datos productiva. Identifica cada evento con ambiente, WABA y Phone Number ID para prevenir cruces.
Práctica del módulo
META_APP_ID, WABA_ID, PHONE_NUMBER_ID, versión de Graph API y nombre de ambiente. Separa secretos de valores no secretos.Comprueba lo aprendido
¿Puede el mismo backend manejar varios números?
Sí, si identifica correctamente el Phone Number ID/WABA de cada evento y aplica configuración/tenant correspondiente.
¿Debes hacer GET /phone_numbers antes de cada envío?
No. Los IDs estables de configuración deberían estar cacheados/almacenados; consulta el endpoint para administración o sincronización.
Módulo 3. Autenticación, tokens y permisos
3.1 Bearer token
Las llamadas a Graph API utilizan Authorization: Bearer .... Un token es una credencial: nunca lo incrustes en JavaScript del navegador, HTML, logs públicos ni repositorios. El backend es responsable de almacenarlo en un gestor de secretos o variables seguras.
3.2 Permisos
Las operaciones de mensajería utilizan whatsapp_business_messaging; las operaciones de administración requieren whatsapp_business_management. En escenarios de incorporación/gestión de activos también puede intervenir business_management. Aplica mínimo privilegio y registra qué servicio requiere cada permiso.
3.3 Ciclo de vida
No diseñes una app móvil o frontend que dependa de un token permanente de Meta. Tu backend debe ser el intermediario. Para integraciones empresariales, utiliza el mecanismo de credenciales adecuado a la arquitectura y define rotación, revocación, alerta de expiración/error y procedimiento de recuperación.
| Riesgo | Síntoma | Control |
|---|---|---|
| Token en frontend | Cualquier usuario puede extraerlo | Solo backend |
| Token en logs | Secreto aparece en observabilidad | Redacción/masking |
| Permisos excesivos | Compromiso con mayor alcance | Mínimo privilegio |
| Sin rotación | Incidente difícil de contener | Runbook y gestor de secretos |
Práctica del módulo
Comprueba lo aprendido
¿Por qué no enviar el token al navegador?
Porque el usuario puede inspeccionarlo y reutilizarlo fuera de tu aplicación.
¿Qué permiso necesitas para enviar mensajes?
whatsapp_business_messaging, además de que el token y activo estén correctamente autorizados.
Módulo 4. Registro y administración del número
4.1 Obtener el Phone Number ID
Antes de registrar un número necesitas su Phone Number ID. Meta documenta la lectura mediante GET /{WABA-ID}/phone_numbers. Guarda el resultado en la configuración del tenant/empresa correspondiente.
4.2 Registrar
El endpoint oficial de registro es POST /{Phone-Number-ID}/register. El cuerpo incluye messaging_product: "whatsapp" y un PIN de seis dígitos para la verificación en dos pasos.
POST https://graph.facebook.com/vXX.X/{PHONE_NUMBER_ID}/register
Authorization: Bearer {TOKEN}
Content-Type: application/json
{
"messaging_product": "whatsapp",
"pin": "123456"
}No uses 123456 en producción; es solo un ejemplo.
4.3 Cambios y migraciones
Un cambio de nombre mostrado, migración o reconfiguración puede requerir operaciones adicionales. Antes de ejecutar, documenta el estado actual y comprueba la guía oficial específica. Evita automatizar operaciones administrativas irreversibles sin confirmación explícita y logs.
Práctica del módulo
Comprueba lo aprendido
¿Qué añade el registro además de habilitar mensajería?
Meta exige configurar verificación en dos pasos mediante el PIN en el proceso documentado.
¿Por qué validar WABA/Phone Number ID?
Evita ejecutar acciones sensibles sobre el activo equivocado en sistemas multiempresa.
Módulo 5. Enviar mensajes
5.1 Endpoint de mensajes
El endpoint central es POST /{Phone-Number-ID}/messages. Para texto libre dentro de una ventana permitida, el cuerpo define producto, destinatario, tipo y objeto text.
POST https://graph.facebook.com/vXX.X/{PHONE_NUMBER_ID}/messages
Authorization: Bearer {TOKEN}
Content-Type: application/json
{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "573001234567",
"type": "text",
"text": {"preview_url": false, "body": "Hola, ¿en qué podemos ayudarte?"}
}5.2 Guarda antes de enviar
Crea un registro interno con UUID, conversación, destinatario, tipo, payload normalizado y estado queued antes de llamar a Meta. Después de la respuesta, asocia el wamid. Este patrón permite reintentos controlados y auditoría si tu proceso cae entre la llamada externa y la escritura en base de datos.
5.3 Errores síncronos
No conviertas todos los errores HTTP en “falló WhatsApp”. Registra código HTTP, código/subcódigo de Graph cuando exista, mensaje técnico, WABA, número y correlation ID. Clasifica errores en reintentables, de configuración, de política/contenido y de datos del destinatario.
Práctica del módulo
sendMessage() que: valida E.164, crea registro interno, llama a Graph API, almacena wamid y clasifica el error. No incluyas token en logs.Comprueba lo aprendido
¿La respuesta del POST confirma entrega?
No. La entrega y lectura llegan posteriormente mediante webhooks de estado.
¿Por qué crear registro antes del envío?
Permite correlación y recuperación incluso si el proceso falla después de llamar a Meta.
Módulo 6. Media, plantillas e interactivos
6.1 Media
Meta documenta POST /{phone-number-ID}/media para subir, GET /{media-ID} para obtener una URL temporal y DELETE /{media-ID} para eliminar. La URL recuperada para descargar media es temporal y requiere autenticación. Por eso tu sistema debe descargar/transferir el contenido de forma controlada si necesita conservación propia.
6.2 Plantillas
Las plantillas se administran bajo la WABA. El endpoint documentado es /{WABA-ID}/message_templates para listar o crear. El envío de una plantilla sigue pasando por /{PHONE_NUMBER_ID}/messages indicando tipo template, nombre, idioma y componentes/variables.
6.3 Interactivos
Botones y listas reducen errores de entrada cuando las opciones son discretas. Modela cada opción con un ID interno estable; no uses el texto visible como clave de negocio, porque el copy puede cambiar. Cuando el webhook devuelva una selección, resuelve ese ID a una acción segura.
| Recurso | Endpoint base | Diseño recomendado |
|---|---|---|
| Mensajes | POST /{PHONE_NUMBER_ID}/messages | Servicio central de salida |
| Media | POST /{PHONE_NUMBER_ID}/media | Almacenar Media ID y metadatos |
| URL de media | GET /{MEDIA_ID} | Tratar URL como temporal |
| Plantillas | GET/POST /{WABA_ID}/message_templates | Sincronizar estado y categoría |
Práctica del módulo
whatsapp_media y whatsapp_templates. Incluye IDs externos, WABA/número, estado, idioma, categoría, timestamps y payload normalizado.Comprueba lo aprendido
¿Conviene guardar una URL de descarga de media como permanente?
No. Meta indica que la URL recuperada es temporal; guarda el Media ID y administra tu propia persistencia si la necesitas.
¿Por qué usar IDs internos en botones?
Porque el texto visible puede cambiar o traducirse; el ID permite una lógica estable y verificable.
Módulo 7. Webhooks y verificación de eventos
7.1 Verificación inicial
El producto Webhooks valida tu callback mediante una solicitud GET con parámetros de verificación. Tu endpoint compara el token de verificación que configuraste y devuelve el challenge cuando coincide. Ese token de verificación no es el mismo que el access token de Graph API.
7.2 Firma de POST
En producción, valida la autenticidad del cuerpo usando la firma x-hub-signature-256 y el App Secret según el mecanismo de Meta. La validación debe usar el cuerpo crudo exacto antes de parsearlo de una forma que altere bytes. Rechaza firmas inválidas y registra el incidente sin exponer secretos.
7.3 Suscripción a la WABA
Configurar un webhook en la app no basta: la aplicación debe estar suscrita a la WABA. Meta documenta POST /{WABA-ID}/subscribed_apps. Una suscripción a la WABA cubre eventos de sus números, por lo que no necesitas repetirla por cada número.
7.4 Responde rápido
El controlador debe validar, persistir el evento o enviarlo a una cola y responder 2xx con rapidez. Procesos como descarga de media, IA, CRM o notificaciones internas deben ejecutarse fuera del ciclo HTTP del webhook.
GET /webhook
validar verify_token
devolver hub.challenge
POST /webhook
leer raw_body
verificar x-hub-signature-256
calcular event_key
persistir/encolar si no existe
responder 200
worker → procesar negocioPráctica del módulo
Comprueba lo aprendido
¿El verify token y el access token son iguales?
No. El verify token lo defines para comprobar el handshake del webhook; el access token autoriza llamadas a Graph API.
¿Por qué responder rápido?
Porque Meta espera confirmación del webhook y puede reintentar; el trabajo pesado debe desacoplarse para evitar timeouts y duplicados.
Módulo 8. Persistencia, estados, idempotencia y reintentos
8.1 Idempotencia entrante
Los webhooks pueden repetirse. Usa el ID del mensaje/evento y el contexto para crear una clave única. La segunda recepción debe convertirse en un no-op o actualización idempotente, no en una segunda venta, ticket o respuesta.
8.2 Máquina de estados
No sobrescribas ciegamente estados. Define transiciones válidas para mensajes salientes y guarda historial de eventos. Esto permite explicar por qué un mensaje terminó en error y detectar eventos fuera de orden.
8.3 Reintentos salientes
Reintenta solo errores transitorios y con backoff. Un 400 por payload inválido no se arregla repitiendo 20 veces. Cada reintento debe mantener un mismo comando lógico para prevenir duplicación y registrar contador, próximo intento y causa.
| Tipo de fallo | ¿Reintentar? | Ejemplo de tratamiento |
|---|---|---|
| Timeout/red temporal | Sí, controlado | Backoff + límite |
| 429/rate limit | Sí, respetando límites | Esperar y reprogramar |
| Payload inválido | No automático | Corregir datos/código |
| Permiso/token | No en bucle | Rotar/corregir credencial |
| Evento duplicado | No reprocesar | Idempotencia |
Práctica del módulo
webhook_events con clave única y una tabla outbound_jobs con intentos. Explica cómo evitarías enviar dos veces una confirmación de pago.Comprueba lo aprendido
¿Idempotencia significa ignorar todos los repetidos?
Significa que repetir el mismo comando/evento produce el mismo efecto lógico. Puede actualizar metadatos, pero no debe duplicar el resultado de negocio.
¿Debes reintentar un 400 indefinidamente?
No. Los errores de validación/configuración requieren corrección, no repetición automática.
Módulo 9. WhatsApp Flows e integraciones avanzadas
9.1 Qué resuelve un Flow
WhatsApp Flows permite construir experiencias estructuradas dentro de WhatsApp, útiles para registro, reservas, generación de leads, soporte, encuestas y otros procesos. Es apropiado cuando necesitas formularios o pasos guiados en lugar de una conversación completamente libre.
9.2 API de Flows
Meta documenta la creación mediante POST /{WABA-ID}/flows. Al crear se indica nombre y una o más categorías, como LEAD_GENERATION, APPOINTMENT_BOOKING, CUSTOMER_SUPPORT u OTHER. Dependiendo del Flow, puede existir un endpoint de datos para interacción con tu backend.
9.3 Diseña contratos de datos
Trata el Flow como una interfaz de usuario distribuida. Versiona el esquema, valida server-side, usa IDs de dominio en lugar de confiar en etiquetas visibles y no aceptes una acción sensible solo porque el cliente la envió desde el Flow. El backend sigue siendo la autoridad.
Práctica del módulo
Comprueba lo aprendido
¿Un Flow reemplaza el backend?
No. Puede capturar y presentar datos, pero las reglas de negocio y validaciones críticas pertenecen al backend.
¿Por qué versionar el contrato?
Porque UI, campos y backend pueden evolucionar; versionar evita interpretar datos antiguos con reglas nuevas.
Módulo 10. Seguridad, observabilidad y despliegue a producción
10.1 Logs estructurados
Cada operación debería incluir correlation ID, tenant/WABA, Phone Number ID, tipo de evento, message ID cuando exista, latencia y resultado. Nunca registres tokens, App Secret ni datos sensibles innecesarios. La observabilidad debe ayudar a responder “qué pasó con este mensaje” en minutos.
10.2 Métricas y alertas
Mide tasa de errores por endpoint, latencia, tamaño de colas, edad del evento más antiguo, reintentos, webhooks por minuto, porcentaje de firmas inválidas y tiempo hasta procesar un mensaje entrante. Alertas sin runbook solo generan ruido; cada alerta importante debe apuntar a una acción.
10.3 Despliegue seguro
Antes de producción realiza pruebas de carga, replay de webhooks, token inválido, caída de base de datos, duplicados, media grande, plantilla rechazada y rate limit. Utiliza feature flags para cambios de alto impacto y despliegues reversibles.
Práctica del módulo
Comprueba lo aprendido
¿Qué métrica detecta que el worker no da abasto?
La edad del evento más antiguo y el crecimiento sostenido de la cola son señales directas.
¿Qué debe tener una alerta útil?
Umbral significativo, contexto suficiente y un procedimiento claro para diagnosticar/responder.
Laboratorio guiado: backend mínimo listo para crecer
Este laboratorio no intenta construir una plataforma completa. Su objetivo es mostrar una arquitectura que pueda recibir y enviar mensajes sin perder trazabilidad cuando hay reintentos, duplicados o fallos parciales.
1. Tablas mínimas
| Tabla | Campos esenciales | Restricción clave |
|---|---|---|
| wa_accounts | id, tenant_id, waba_id, phone_number_id, graph_version, status | unique tenant + phone_number_id |
| conversations | id, tenant_id, contact_id, wa_account_id, status, assignee_id | índices por tenant/status |
| messages | id, conversation_id, direction, wamid, type, body, status, created_at | unique wamid cuando no sea null |
| message_status_events | id, message_id, status, occurred_at, raw_event_id | no sobrescribir historial |
| webhook_events | id, event_key, received_at, payload, processed_at | unique event_key |
| outbound_jobs | id, command_key, message_id, attempts, next_attempt_at, status | unique command_key |
2. Flujo entrante
POST /meta/whatsapp/webhook
raw = request.raw_body
assert valid_signature(raw, header)
event_key = stable_event_key(raw)
if webhook_events.exists(event_key): return 200
webhook_events.insert(event_key, raw)
queue.push(event_key)
return 200
worker(event_key)
event = load(event_key)
for each message/status in normalize(event.payload):
upsert idempotently
emit internal domain event
mark processed
La normalización desacopla tu dominio del JSON específico de Meta. Un objeto interno puede tener kind=inbound_text, external_message_id, sender, phone_number_id y occurred_at. Si Meta añade campos, no necesitas propagar el payload completo a todos los servicios.
3. Flujo saliente con outbox
Un patrón robusto es registrar el comando saliente dentro de la misma transacción que cambia el negocio. Por ejemplo, cuando un pago se confirma, escribes el evento/command “send_payment_confirmation” con una clave única basada en payment_id. Un worker realiza el POST a WhatsApp. Si la aplicación cae, el comando sigue pendiente. Si se procesa dos veces, la clave única evita duplicación lógica.
4. Errores y reintentos
Define una taxonomía interna: TRANSIENT, RATE_LIMIT, AUTH, VALIDATION, POLICY, DESTINATION. El código exacto de Graph se conserva, pero la aplicación decide reintento por la categoría interna. Esto evita llenar el dominio de condiciones específicas de una versión.
5. Seguridad de webhook
La firma debe verificarse sobre el cuerpo crudo. Compara HMAC de forma segura y limita tamaño de request. El endpoint debe ser HTTPS y no debe exponer paneles administrativos. Considera rate limiting defensivo que no bloquee los callbacks legítimos de Meta; si implementas WAF, prueba el flujo real después de cualquier cambio.
6. Observabilidad mínima
| Señal | Etiqueta útil | Alerta |
|---|---|---|
| webhook_received_total | tenant, type | caída brusca por número activo |
| webhook_processing_seconds | tenant, type | p95 fuera de objetivo |
| outbound_errors_total | category, graph_code | spike por categoría |
| queue_oldest_age | queue | edad creciente |
| signature_invalid_total | source | cualquier aumento inusual |
| message_delivery_ratio | tenant/campaign | deterioro significativo |
7. Pruebas que debes automatizar
- Handshake GET correcto e incorrecto.
- POST con firma válida e inválida.
- Replay del mismo webhook dos veces.
- Mensaje entrante desconocido.
- Status antes/después del registro saliente.
- Timeout de Graph API.
- 429 con backoff.
- 400 no reintentable.
- Token inválido.
- Plantilla inexistente/pausada.
- Descarga de media expirada y recuperación de nueva URL.
- Caída temporal de base de datos/cola.
Examen técnico
¿Por qué un webhook 200 debe darse antes de ejecutar IA?
Porque el callback necesita confirmación rápida; IA puede tardar o fallar y debe procesarse de forma asíncrona.
¿Qué propiedad debe tener event_key?
Ser estable para el mismo evento lógico y suficientemente específica para no colisionar con eventos distintos.
¿Dónde guardas el token?
En backend/secret manager o configuración protegida, nunca en frontend, repositorio o logs.
¿Cómo correlacionas estados de entrega?
Con el wamid/external message ID asociado al registro saliente y eventos de estado posteriores.
¿Qué endpoint suscribe la app a la WABA?
POST /{WABA-ID}/subscribed_apps según la colección oficial de Meta.
¿Qué riesgo hay en reintentar cualquier error?
Puedes crear ruido, rate limits o duplicaciones sin resolver errores de validación, permisos o política.
¿Qué devuelve GET /{MEDIA_ID}?
Metadatos/URL temporal para acceder al media; la descarga requiere autorización y la URL no debe tratarse como permanente.
¿Qué ventaja tiene un outbox?
Hace más confiable coordinar cambios de base de datos con efectos externos asincrónicos.
Proyecto final del curso
Construye una integración de referencia —puede ser real o en pseudocódigo— que reciba un mensaje, lo persista de forma idempotente, lo muestre a una capa de aplicación y envíe una respuesta. Debe incluir:
- Configuración por ambiente y IDs.
- Servicio de autenticación sin secretos en frontend.
- POST /messages con correlación interna.
- Webhook GET y POST con validación de firma.
- Cola + worker.
- Persistencia de eventos, mensajes y estados.
- Reintentos con backoff y clasificación de errores.
- Manejo de media o plantillas.
- Logs, métricas y alertas.
- Documento de despliegue y rollback.
Competencias que debes dominar al terminar
- Identificar endpoints y IDs principales.
- Gestionar tokens y permisos con mínimo privilegio.
- Registrar y administrar un número.
- Enviar mensajes y correlacionar wamid.
- Procesar media y plantillas.
- Validar y suscribir webhooks.
- Implementar idempotencia y reintentos.
- Diseñar un Flow conectado al backend.
- Operar la integración con observabilidad y seguridad.
Fuentes oficiales para continuar estudiando
- WhatsApp Business — Developer Hub
- Meta — colección oficial de WhatsApp Business Platform en Postman
- WhatsApp Business — Política de mensajes
- Meta for Developers — WhatsApp Cloud API
- Meta for Developers — precios de WhatsApp Business Platform