Saltar a contenido

Errores, estados y límites

Ciclo de vida de un mensaje

Todo mensaje (individual o de broadcast) avanza por estos estados, visibles en el portal (sección Mensajes):

Estado Significado
pending Recibido por Arplyx, esperando que el worker lo procese.
queued En cola para salir al canal. Si hubo un fallo transitorio, queda acá entre reintentos.
sent Entregado al proveedor del canal (Meta o WhatsApp Direct).
delivered El proveedor confirmó entrega en el dispositivo del destinatario.
read El destinatario lo leyó (cuando el canal lo informa).
failed Falló de forma definitiva: se agotaron los reintentos o el error no es recuperable. El detalle del mensaje muestra el código y la descripción del último error.

Reintentos: los errores transitorios (problemas de red, 5xx del proveedor, rate limit) se reintentan automáticamente con backoff exponencial, hasta 5 intentos por defecto. Los errores permanentes (número inválido, cuenta desvinculada, credenciales rechazadas) marcan el mensaje failed sin reintentar.

De dónde salen delivered y read

  • Meta Official: llegan por el webhook de tu app de Meta. Si no lo configuraste, los mensajes quedan en sent. Ver el paso 6 de la guía Meta.
  • WhatsApp Direct: llegan por la sesión vinculada, típicamente a los pocos segundos, mientras la sesión esté conectada. El read depende de que el destinatario tenga activadas las confirmaciones de lectura en WhatsApp.

Para reaccionar a estas transiciones en tiempo real desde tu sistema, usá webhooks.

Vida útil de un mensaje: ttlSeconds, retención e idempotencia

Estos tres conceptos están relacionados — un mensaje vive ttlSeconds (default 24 h, recortado a la retención de tu plan) desde que se crea:

  • Al expirar, el mensaje se elimina (el registro completo: estados, historial de intentos). No pasa a failed ni dispara webhook: simplemente deja de existir en la próxima pasada de la purga (corre cada pocas horas).
  • Por eso GET /messages/{id} responde 404 para mensajes purgados: diseñá tu reconciliación asumiendo que los mensajes viejos desaparecen según la retención del plan (3 días en Free, 30 en el resto).
  • La deduplicación por externalId vive lo mismo que el mensaje: mientras el registro exista, repetir el externalId devuelve el original (o 409 si el payload difiere). Una vez purgado, ese externalId queda reutilizable y un request repetido crearía (y enviaría) un mensaje nuevo. Si tu sistema puede reintentar operaciones viejas, agregale al externalId un componente temporal (ej. turno-4812-2026-07-04).

En la práctica los reintentos de envío se agotan mucho antes del TTL (5 intentos con backoff, minutos), así que un mensaje que no pudo salir va a estar failed — con su webhook y su error.code — bastante antes de expirar.

Rate limits

Hoy la API pública no aplica límite de requests por segundo: el único límite es la cuota mensual del plan (429 quota_exceeded, sin header Retry-After; usá el campo resetsAt del body). Dimensioná tus workers con criterio — si más adelante agregamos un límite operativo, lo vamos a anunciar en la documentación con antelación.

Catálogo de errores HTTP

Todas las respuestas de error tienen un campo error; algunas agregan message o campos extra.

Status error Cuándo
400 validation_error El payload no pasa la validación. El cuerpo incluye details: una lista de { "path": "campo.afectado", "message": "..." } por cada problema.
401 Missing x-api-key header / Invalid API key Falta la clave, es inválida o fue revocada.
409 conflict El externalId ya existe con un payload distinto. Elegí otro externalId o repetí el payload original.
422 invalid_whatsapp_account El whatsappAccountId no existe, no es tuyo, o su tipo no coincide con el channel (ej.: cuenta Direct con canal whatsapp_meta).
403 templates_not_available Intentaste enviar una plantilla en el plan Free. El envío de plantillas (proactivo) está disponible desde el plan Starter.
403 feature_not_available La función no está disponible en tu plan (ej.: GET /inbound-messages fuera de Pro, webhooks fuera de Basic/Pro). El cuerpo incluye plan.
403 key_scope_violation La API key está scopeada a una cuenta y pediste operar sobre otra.
404 account_not_found El whatsappAccountId indicado en una consulta no existe o no es tuyo.
422 whatsapp_account_required La consulta necesita whatsappAccountId (tenés más de una cuenta vinculada, o ninguna) — ver remitentes entrantes.
429 quota_exceeded Cuota mensual agotada. El cuerpo incluye limit, used y resetsAt (cuándo se renueva: primer día del mes, UTC).
500 Internal server error Error inesperado de Arplyx. Reintentá con el mismo externalId (es seguro por idempotencia); si persiste, contactanos.

Ejemplo de validation_error:

{
  "error": "validation_error",
  "details": [
    { "path": "to.phoneE164", "message": "Invalid E.164 phone number" }
  ]
}

Los errores exclusivos de broadcast (lista vacía, plan sin broadcasts, etc.) están en Broadcasts por API.

Cuotas y límites por plan

Plan Mensajes/mes Cuentas WhatsApp Retención de historial Broadcasts
Free 500 1 3 días No
Starter 2.000 1 30 días No
Basic 10.000 3 30 días
Pro 20.000 5 30 días
  • La cuota es compartida entre la API, el portal y los broadcasts: todo mensaje individual cuenta.
  • Se renueva el día 1 de cada mes (UTC). El cuerpo del error 429 te dice exactamente cuándo (resetsAt).
  • Tope adicional por broadcast: 5.000 destinatarios.
  • ttlSeconds se recorta a la retención del plan (en Free, un mensaje no puede vivir más de 3 días en cola).
  • La retención aplica al contenido de los mensajes (salientes y entrantes). El agregado de remitentes entrantes (hasWritten, firstInboundAt) no se purga: persiste mientras exista la cuenta de WhatsApp, en todos los planes.

¿Necesitás más volumen? Escribinos a info@arplyx.com.