Ir al contenido

Errores, límites e idempotencia

Cada error de la API de WhatSetter tiene la misma forma y un code estable. Esta página los lista todos, describe las cabeceras que reflejan tus límites y dice qué hacer cuando una llamada falla.

{
"error": {
"code": "insufficient_scope",
"message": "This API key is missing the \"messages:send\" scope."
}
}
  • code es estable: ramifica tu código con él.
  • message es para humanos y puede cambiar. Regístralo, no lo parsees.
  • El estado HTTP corresponde al código (mira la tabla). Las respuestas de éxito usan { "data": … }, y las listas añaden { "pagination": { "has_more", "next_cursor" } }.
HTTP code Cuándo ocurre Qué hacer
400 validation_error La ruta contiene una codificación de URL inválida (por ejemplo %ZZ). Corrige la URL.
401 missing_api_key Ni cabecera Authorization: Bearer ni cabecera X-Api-Key. Envía la clave.
401 invalid_api_key La clave es desconocida, está revocada o no tiene el formato ws_live_. Revisa la clave, o crea una nueva.
402 plan_limit_reached POST /campaigns/{id}/resume cuando el cupo de campañas activas de tu plan está lleno. Pausa otra campaña, o cambia de plan.
403 insufficient_scope La clave no tiene el scope que este endpoint necesita. El mensaje lo nombra. Crea una clave con ese scope.
404 not_found Ruta desconocida, o el recurso no existe, o pertenece a otro espacio. Revisa el id y la ruta.
409 invalid_state Pausar una campaña que no está activa, reanudar una que no está en pausa, añadir un lead a una campaña que no está activa, o pasar a un humano un lead sin conversación. Lee el mensaje y cambia el estado en el dashboard.
409 lead_not_contacted POST /messages a un lead al que nadie ha escrito todavía. El primer contacto pasa por una lista y una campaña.
409 request_in_flight Una petición con la misma Idempotency-Key todavía se está procesando. Espera unos segundos y reintenta con la misma clave.
409 already_blocked POST /blocked-contacts para un número o prefijo que ya está en la lista. Nada: ya está bloqueado.
422 validation_error Falta un parámetro o un campo del cuerpo, o es inválido: limit fuera de 1 a 100, una fecha que no es ISO 8601, un text vacío, un id de miembro desconocido en assigned_to. Corrige la petición. Reintentar tal cual volverá a fallar.
429 rate_limited Más peticiones por minuto de las que permite tu plan. Espera Retry-After segundos y reintenta.
429 quota_exceeded Se alcanzó la cuota diaria de WhatsApp del número emisor (se reinicia a medianoche UTC), o un tope del espacio: 200 listas, 10 suscripciones de webhook, 500 contactos bloqueados. Espera a X-Quota-Reset, o borra lo que no uses.
500 internal_error Un error inesperado de nuestro lado, o el backend de mensajería no está configurado. Reintenta más tarde. Contacta con soporte si persiste.
500 server_error El control del agente no está configurado en este despliegue (handoff, resume). Contacta con soporte.
502 send_failed El motor de WhatsApp no confirmó el envío. Reintenta con la misma Idempotency-Key. Sin clave, lee la conversación antes de volver a enviar.
502 upstream_error El workflow detrás de handoff, resume o POST /campaigns/{id}/leads rechazó la llamada o no estaba disponible. Reintenta en un momento.
503 agent_disconnected El número de WhatsApp de la campaña está desconectado, o no hay ningún número vinculado. Reconecta el número en el dashboard.
503 service_unavailable El backend de autenticación no está disponible. Reintenta en unos segundos.

Las peticiones se cuentan por espacio y por minuto, todas las claves juntas, con un límite fijado por tu plan (60 a 1000 por minuto, mira Autenticación y permisos). Cada respuesta lleva:

X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287
X-RateLimit-Reset: 2026-09-19T10:01:00.000Z

Cuando se supera el límite, la respuesta es 429 rate_limited y añade Retry-After en segundos:

HTTP/1.1 429 Too Many Requests
Retry-After: 23

Enviar mensajes tiene un segundo presupuesto, separado: la cuota diaria del número de WhatsApp, la misma que usa el motor de campañas (mira la protección anti-ban). Un POST /messages con éxito la devuelve:

X-Quota-Limit: 150
X-Quota-Remaining: 149
X-Quota-Reset: 2026-09-20T00:00:00.000Z

Los mismos números están en el cuerpo bajo data.quota (limit, used, remaining, resets_at). Cuando se alcanza la cuota recibes 429 quota_exceeded. Se reinicia a medianoche UTC. quota_exceeded también se usa para los topes del espacio listados en la tabla de arriba.

POST /messages se puede reintentar sin riesgo cuando envías una cabecera Idempotency-Key: cualquier cadena única de 200 caracteres como máximo, por ejemplo un UUID por intención de envío.

Terminal window
curl -X POST https://app.whatsetter.com/api/v1/messages \
-H "Authorization: Bearer $WHATSETTER_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 4d3f9b2a-7c1e-4a8b-9f0d-2e6c8a1b3d5f" \
-d '{"lead_id":"66f1a2b3c4d5e6f7a8b9c0d1","text":"¿Confirmamos mañana a las 14h?"}'

Qué hace la clave:

  • Una clave que ya produjo una respuesta la repite con la cabecera X-Idempotent-Replay: true. No se envía un segundo mensaje.
  • Un reintento lanzado mientras la primera petición sigue en curso recibe 409 request_in_flight. Espera unos segundos y reintenta con la misma clave.
  • Si un envío caduca o responde 502 send_failed, reintentar con la misma clave es seguro: WhatSetter repite el resultado registrado o responde 409 mientras se asienta. Nunca envía dos veces.
  • Las claves son propias de tu espacio y se recuerdan 15 minutos.

Cada respuesta lleva X-Api-Version: 2026-07, la versión del contrato REST. Las cargas de los webhooks tienen su propio campo api_version (2026-06-01), descrito en la página de webhooks.

Respuesta ¿Reintentar? Cómo
429 rate_limited Sí Espera Retry-After segundos.
429 quota_exceeded en POST /messages Sí, mañana Espera a X-Quota-Reset (medianoche UTC).
502 send_failed, 502 upstream_error, 503 * Sí Backoff exponencial, empezando por unos segundos. Para POST /messages, reutiliza la misma Idempotency-Key.
500 internal_error Una vez, más tarde Si persiste, contacta con soporte con la ruta y la hora de la petición.
409 request_in_flight Sí Espera unos segundos, misma Idempotency-Key.
Cualquier otro 4xx No La petición en sí está mal. Corrígela primero.