WhatsApp Flows
Los eventos whatsapp.flows.* son especiales: Platica no sólo te notifica que algo ocurrió, también espera tu respuesta para continuar el flow de WhatsApp.
Úsalos cuando quieras que tu backend controle pantallas dinámicas, validaciones, opciones de dropdown, precios, confirmaciones o cualquier dato que deba calcularse en tiempo real.
Tu endpoint debe responder con un JSON válido en menos de 700 ms. Si no responde a tiempo, Platica devuelve un fallback { "data": { "acknowledged": true } } hacia Meta.
Eventos
| Evento | Cuándo ocurre |
|---|---|
whatsapp.flows.init | El usuario abre el flow (action: "INIT") |
whatsapp.flows.screen_advance | El usuario avanza de pantalla (action: "data_exchange") |
whatsapp.flows.back | El usuario regresa a la pantalla anterior (action: "BACK") |
whatsapp.flows.exchanges | Suscripción paraguas para recibir los tres eventos anteriores |
Ciclo de entrega
Payload recibido
{
"id": "8f2c4d6a-1b3e-4f5d-8a9c-2e7b6d1a3f4e",
"event": "whatsapp.flows.screen_advance",
"workspaceId": "ws_456",
"timestamp": "2026-05-06T19:00:30.000Z",
"source": "whatsapp.flows",
"resourceType": "flow_response",
"resourceId": "ZmxvdyQwMQ==",
"changes": null,
"data": {
"client": {
"id": "client_123",
"phoneNumber": "+5215555555555",
"creationDate": "2026-05-01T12:00:00.000Z",
"lastUpdate": "2026-05-06T18:59:00.000Z",
"name": "Juan Pérez",
"firstname": "Juan",
"email": "juan@empresa.com"
},
"conversation": {
"id": "conv_123",
"conversationId": "conv_123",
"canSendDirectMessage": true,
"workspaceId": "ws_456",
"channelId": "channel_wa_123",
"contactName": "Juan Pérez",
"phoneNumber": "+5215555555555",
"topic": "",
"platform": "whatsapp",
"owners": [],
"tags": [],
"creationDate": "2026-05-06T18:58:00.000Z",
"lastUpdate": "2026-05-06T19:00:00.000Z",
"status": "open",
"operation": "automatic",
"messageCount": 3
},
"flow": {
"id": "flow_doc_abc",
"wabaId": "1234567890",
"name": "Cotizador",
"status": "PUBLISHED",
"categories": ["LEAD_GENERATION"],
"dataApiVersion": "3.0",
"endpointUri": "https://app.platica.mx/api/whatsapp/flows/1234567890/data",
"jsonVersion": "7.1",
"creationDate": "2026-05-01T12:00:00.000Z",
"lastUpdate": "2026-05-06T18:55:00.000Z",
"lastSyncAt": "2026-05-06T18:55:00.000Z"
},
"flowResponse": {
"flowToken": "ZmxvdyQwMQ==",
"status": "IN_PROGRESS",
"sentAt": "2026-05-06T18:59:00.000Z",
"openedAt": "2026-05-06T19:00:00.000Z",
"lastActivityAt": "2026-05-06T19:00:30.000Z",
"completedAt": null,
"expiresAt": "2026-05-07T18:59:00.000Z",
"clientId": "client_123",
"conversationId": "conv_123",
"messageId": "wamid.HBgN...",
"campaignId": null,
"agentId": null,
"channelId": "channel_wa_123"
},
"flowExchange": {
"action": "data_exchange",
"screen": "WELCOME",
"version": "3.0",
"payload": {
"selected_product": "p2",
"email": "juan@empresa.com"
}
}
}
} | Campo | Tipo | Descripción |
|---|---|---|
event | string | Evento recibido |
id | string | UUID del envío |
workspaceId | string | Workspace propietario |
timestamp | string | Fecha ISO-8601 del envío |
source | whatsapp.flows | Origen estable del evento |
resourceType | flow_response | Tipo de recurso afectado |
resourceId | string \| null | flow_token de Meta cuando existe |
changes | null | Reservado para compatibilidad con el envelope común |
data.client | object \| null | Resumen del cliente asociado, si Platica pudo resolverlo |
data.conversation | object \| null | Conversación asociada, si existe un conversationId |
data.flow | object | Snapshot normalizado del flow |
data.flowResponse | object \| null | Estado de la respuesta/sesión del flow |
data.flowExchange | object | Datos del intercambio recibido desde Meta |
Los datos que Meta envía en decryptedBody.data ahora viven en data.flowExchange.payload. La pantalla actual está en data.flowExchange.screen, el token de sesión en resourceId y data.flowResponse.flowToken, y el identificador interno del flow en data.flow.id.
Campos de data.flowExchange
| Campo | Tipo | Descripción |
|---|---|---|
action | init \| data_exchange \| back | Acción recibida desde Meta, normalizada a minúsculas |
screen | string \| null | Pantalla actual enviada por Meta |
version | string \| null | Versión del protocolo de WhatsApp Flows |
payload | object | Datos capturados o enviados por Meta para ese paso |
Respuesta esperada
Tu endpoint debe responder 2xx con JSON. Platica acepta la respuesta si cumple al menos una de estas condiciones:
- Tiene
dataydataes un objeto. - Tiene
screenyscreenes un string no vacío.
Ejemplo para poblar datos dinámicos:
{
"data": {
"products": [
{ "id": "p1", "title": "Plan Básico" },
{ "id": "p2", "title": "Plan Premium" }
]
}
} Ejemplo para avanzar a una pantalla:
{
"screen": "PAYMENT",
"data": {
"amount_mxn": 499
}
} Ejemplo para mostrar un error:
{
"data": {
"error_message": "El código postal no es válido"
}
} Headers y firma
| Header | Descripción |
|---|---|
Content-Type | application/json |
User-Agent | Platica-Webhooks/1.0 |
X-Webhook-Event | Igual a payload.event |
X-Webhook-Id | Identificador del webhook configurado |
X-Webhook-Event-Id | Igual a payload.id |
X-Webhook-Resource-Type | flow_response |
X-Webhook-Resource-Id | Igual a payload.resourceId |
X-Webhook-Timestamp | Igual a payload.timestamp |
X-Webhook-Signature | sha256=<hex> si configuraste secret |
La firma se calcula con HMAC-SHA256 usando el cuerpo crudo:
sha256=<hmac_sha256(secret, rawBody)> Ejemplo en Node.js:
const crypto = require('crypto');
function verifyFlowsWebhook(secret, rawBody, signatureHeader) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader)
);
} whatsapp.flows.init
Ocurre cuando el usuario abre el flow. Normalmente se usa para precargar datos iniciales.
{
"id": "5e3a1b9c-7d12-4a8e-9c5f-1e6b8d0a2c4f",
"event": "whatsapp.flows.init",
"workspaceId": "ws_456",
"timestamp": "2026-05-06T19:00:00.000Z",
"source": "whatsapp.flows",
"resourceType": "flow_response",
"resourceId": "ZmxvdyQwMQ==",
"changes": null,
"data": {
"client": null,
"conversation": null,
"flow": {
"id": "flow_doc_abc",
"wabaId": "1234567890"
},
"flowResponse": {
"flowToken": "ZmxvdyQwMQ==",
"status": "IN_PROGRESS"
},
"flowExchange": {
"action": "init",
"screen": null,
"version": "3.0",
"payload": {}
}
}
} whatsapp.flows.screen_advance
Ocurre cuando el usuario llena una pantalla y pulsa continuar. data trae los valores capturados.
{
"id": "8f2c4d6a-1b3e-4f5d-8a9c-2e7b6d1a3f4e",
"event": "whatsapp.flows.screen_advance",
"workspaceId": "ws_456",
"timestamp": "2026-05-06T19:00:30.000Z",
"source": "whatsapp.flows",
"resourceType": "flow_response",
"resourceId": "ZmxvdyQwMQ==",
"changes": null,
"data": {
"client": null,
"conversation": null,
"flow": {
"id": "flow_doc_abc",
"wabaId": "1234567890"
},
"flowResponse": {
"flowToken": "ZmxvdyQwMQ==",
"status": "IN_PROGRESS"
},
"flowExchange": {
"action": "data_exchange",
"screen": "WELCOME",
"version": "3.0",
"payload": {
"selected_product": "p2",
"email": "juan@empresa.com"
}
}
}
} whatsapp.flows.back
Ocurre cuando el usuario pulsa regresar. Úsalo para restaurar datos de una pantalla anterior.
{
"id": "3a5b7c9d-2e4f-4a6b-8c1d-5e7f9a2b4c6d",
"event": "whatsapp.flows.back",
"workspaceId": "ws_456",
"timestamp": "2026-05-06T19:01:00.000Z",
"source": "whatsapp.flows",
"resourceType": "flow_response",
"resourceId": "ZmxvdyQwMQ==",
"changes": null,
"data": {
"client": null,
"conversation": null,
"flow": {
"id": "flow_doc_abc",
"wabaId": "1234567890"
},
"flowResponse": {
"flowToken": "ZmxvdyQwMQ==",
"status": "IN_PROGRESS"
},
"flowExchange": {
"action": "back",
"screen": "PAYMENT",
"version": "3.0",
"payload": {}
}
}
} whatsapp.flows.exchanges
Es una suscripción paraguas para recibir init, screen_advance y back en un solo webhook.
Usa whatsapp.flows.exchanges si tu backend quiere manejar todos los pasos del flow con el mismo endpoint y la misma lógica.
Si necesitas separar lógica por paso, suscríbete a los eventos granulares:
whatsapp.flows.initwhatsapp.flows.screen_advancewhatsapp.flows.back