/messagesscope: messages:send · templates:sendLa ventana de 24 horas
Esta es la regla que más sorprende al integrar WhatsApp, y no es de FlujosChat sino de Meta:
- Cada mensaje del cliente abre una ventana de 24 horas. Dentro de esa ventana respondés lo que quieras, gratis, como en un chat normal.
- Cuando se cierra, el texto libre deja de funcionar (
422 WHATSAPP_24H_WINDOW_EXPIRED) y para retomar el contacto necesitás una plantilla aprobada.
La regla práctica
Enviar texto
Cuerpo
| Campo | Tipo | Descripción |
|---|---|---|
toreq. | string | Número en formato internacional: +51987654321. |
type | "text" | Por defecto "text". |
textreq. | string | Hasta 4096 caracteres (el máximo de WhatsApp). |
customerName | string | Solo se usa al crear el contacto o si aún no tiene nombre. |
curl -X POST https://www.flujoschat.foo/api/public/v1/messages \
-H "Authorization: Bearer fjc_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-4512-confirmacion" \
-d '{
"to": "+51987654321",
"type": "text",
"text": "Tu pedido #4512 fue enviado 🚚"
}'{
"success": true,
"message": "Mensaje enviado",
"data": {
"message": {
"id": "9c1e…",
"conversationId": "3f2b…",
"direction": "OUTBOUND",
"type": "TEXT",
"text": "Tu pedido #4512 fue enviado 🚚",
"status": "SENT",
"sentAt": "2026-07-26T14:32:00.000Z",
"createdAt": "2026-07-26T14:32:00.000Z"
},
"conversation": { "id": "3f2b…", "customerPhone": "+51987654321" }
}
}status: "SENT" significa que Meta lo aceptó, no que el cliente ya lo vio. Para saber si se entregó o se leyó, escuchá el evento message.status_updated.
Enviar una plantilla
Primero conseguí el templateId y su mapa de variables en GET /templates.
Cuerpo
| Campo | Tipo | Descripción |
|---|---|---|
toreq. | string | Número en formato internacional. |
typereq. | "template" | Fija el modo plantilla. |
templateIdreq. | string | El id de GET /templates. La plantilla debe estar APPROVED. |
variables | object | Valores por posición: { "1": "4512" }. Lo que el variableMapping resuelve solo se puede omitir. |
customerName | string | Útil cuando el número es nuevo y querés que el chat nazca con nombre. |
curl -X POST https://www.flujoschat.foo/api/public/v1/messages \
-H "Authorization: Bearer fjc_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-4512-plantilla" \
-d '{
"to": "+51987654321",
"type": "template",
"templateId": "b7f1c3e0-0000-0000-0000-000000000000",
"variables": { "1": "4512", "2": "mañana entre 9 y 12" },
"customerName": "Ana Pérez"
}'templates:send no viene con messages:send
templates:send marcado.Errores del envío
Los que vas a ver en producción
422 WHATSAPP_24H_WINDOW_EXPIRED— la ventana se cerró. No reintentes con texto: cambiá a plantilla.422 TEMPLATE_NOT_APPROVED— Meta no aprobó la plantilla todavía. Revisá su estado en el panel.502 PROVIDER_ERROR— Meta rechazó el envío. El mensaje queda enFAILEDy el detalle está en el chat. Reintentable con backoff.409 IDEMPOTENCY_KEY_REUSED— reusaste la key con otro contenido. Es un bug de tu lado: la key tiene que ser única por acción.
Preguntas frecuentes
¿Puedo enviar un WhatsApp a alguien que nunca me escribió?
Solo con una plantilla aprobada por Meta: POST /messages con type "template". El texto libre únicamente funciona si el cliente te escribió en las últimas 24 horas; fuera de esa ventana la API responde 422 WHATSAPP_24H_WINDOW_EXPIRED.
¿Qué es la ventana de 24 horas de WhatsApp?
Es la regla de Meta: cada mensaje del cliente abre una ventana de 24 horas durante la cual podés responder con texto libre. Cuando se cierra, para retomar el contacto necesitás una plantilla aprobada, que es un mensaje con formato preaprobado por Meta.
¿Por qué enviar plantillas necesita un permiso aparte?
Porque cada envío de plantilla tiene costo facturable por Meta y puede iniciar conversaciones en frío. Por eso vive en el scope templates:send y no viene incluido en messages:send: una key que solo responde chats abiertos no puede generar gasto.