Los errores de la API siempre traen un error.code estable. Ramificá por ese código, nunca por el texto del mensaje: el mensaje está en español, es para humanos y puede cambiar.
Forma del error
{
"success": false,
"message": "Fuera de la ventana de 24 horas de WhatsApp: el cliente debe escribir primero, o enviá una plantilla aprobada con type \"template\".",
"error": { "code": "WHATSAPP_24H_WINDOW_EXPIRED" }
}Códigos de error
Autenticación y plan
| HTTP | error.code | Cuándo |
|---|---|---|
| 401 | MISSING_API_KEY | No mandaste el header Authorization ni X-API-Key. |
| 401 | INVALID_API_KEY | La key no existe o fue revocada. |
| 403 | COMPANY_SUSPENDED | La empresa está suspendida o cancelada. |
| 402 | SUBSCRIPTION_LOCKED | Trial vencido o pago pendiente. Regularizá para volver a usar la API. |
| 403 | API_ACCESS_NOT_INCLUDED | El plan no incluye API pública. |
| 403 | INSUFFICIENT_SCOPE | La key no tiene el permiso que exige ese endpoint. |
Validación
| HTTP | error.code | Cuándo |
|---|---|---|
| 400 | INVALID_PHONE | El número no está en formato internacional (+51987654321). |
| 400 | INVALID_STATUS | status distinto de IN_PROGRESS o CLOSED. |
| 400 | INVALID_CURSOR | El cursor no existe (o ya se borró ese registro). |
| 400 | INVALID_PARAMETER | Un parámetro tiene un valor no soportado. |
| 400 | INVALID_MESSAGE_TYPE | type distinto de "text" o "template". |
| 400 | MISSING_FIELD | Falta un campo obligatorio del body. |
| 400 | TEXT_TOO_LONG | El texto pasa los 4096 caracteres que admite WhatsApp. |
| 400 | EMPTY_UPDATE | Un PATCH sin ningún campo para actualizar. |
Recursos y reglas de WhatsApp
| HTTP | error.code | Cuándo |
|---|---|---|
| 404 | CONVERSATION_NOT_FOUND | No existe o es de otra empresa. |
| 404 | TEMPLATE_NOT_FOUND | La plantilla no existe o es de otra empresa. |
| 404 | CUSTOM_FIELD_NOT_FOUND | El campo personalizado no es de tu empresa. |
| 422 | WHATSAPP_24H_WINDOW_EXPIRED | Texto libre fuera de la ventana de 24 h: usá una plantilla. |
| 422 | TEMPLATE_NOT_APPROVED | Meta todavía no aprobó esa plantilla. |
| 502 | PROVIDER_ERROR | Meta rechazó el envío. El mensaje queda como fallido. |
Límites e idempotencia
| HTTP | error.code | Cuándo |
|---|---|---|
| 429 | RATE_LIMITED | Superaste los requests por minuto de tu plan. |
| 429 | MONTHLY_QUOTA_EXCEEDED | Agotaste la cuota del mes. |
| 409 | IDEMPOTENCY_KEY_REUSED | Misma Idempotency-Key con un body distinto. |
| 409 | IDEMPOTENCY_IN_PROGRESS | Ya hay un request en curso con esa key. Reintentá en unos segundos. |
| 500 | INTERNAL_ERROR | Error nuestro. Reintentable con backoff. |
Cuotas y rate limits
El acceso a la API y sus límites vienen del plan:
| HTTP | error.code | Cuándo |
|---|---|---|
| 0 | FREE / BASIC | Sin acceso a la API pública. |
| 0 | PRO | 10 000 requests al mes · 60 por minuto por key · hasta 3 keys. |
| 0 | ENTERPRISE | 100 000 requests al mes · 120 por minuto por key · hasta 10 keys. |
Cada respuesta te dice cómo vas, así no tenés que llevar la cuenta:
RateLimit-Limit,RateLimit-Remaining,RateLimit-Reset— el minuto en curso.X-Monthly-Quota-Limit,X-Monthly-Quota-Remaining— el mes en curso.
Y con GET /usage lo consultás cuando quieras. Las entregas de webhook no consumen esta cuota: no son requests tuyos.
Idempotencia
Mandá Idempotency-Key en todos los envíos
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-enviado" \
-d '{"to":"+51987654321","type":"text","text":"Tu pedido #4512 va en camino"}'- Un reintento con la misma key y el mismo body devuelve la respuesta original, con el header
Idempotency-Replayed: true. - La misma key con un body distinto responde
409 IDEMPOTENCY_KEY_REUSED: es una señal de bug en tu lado. - Dos requests en paralelo con la misma key: el segundo recibe
409 IDEMPOTENCY_IN_PROGRESS. Reintentá y vas a obtener el replay. - La key vale 24 horas.
Cómo reintentar
- Reintentá en
429,500,502y errores de red, con backoff exponencial (1 s, 2 s, 4 s, 8 s…) y algo de aleatoriedad. - No reintentes en
400,401,403,404ni422: el resultado va a ser el mismo. Arreglá el request. - En
429, esperá lo que digaRateLimit-Resetantes de volver a intentar.
Preguntas frecuentes
¿Cuántos requests por minuto permite la API de FlujosChat?
Depende del plan: 60 requests por minuto por API key en el plan PRO y 120 en ENTERPRISE, con 10 000 y 100 000 requests al mes respectivamente. Cada respuesta incluye headers RateLimit-* y X-Monthly-Quota-Remaining con tu estado real.
¿Cómo evito enviar el mismo mensaje de WhatsApp dos veces?
Enviá el header Idempotency-Key con un valor único por acción de negocio (por ejemplo el id del pedido). Si reintentás con la misma key, la API devuelve la respuesta original con el header Idempotency-Replayed: true en vez de volver a enviar. Vale 24 horas.
¿Un error 403 por falta de permisos me consume cuota?
No. El permiso se valida antes de contar el request, así que un 403 INSUFFICIENT_SCOPE no gasta tu cuota mensual. Los requests que sí llegan al endpoint cuentan, incluso si terminan en error de validación.