Errores y límites
Cómo interpretar las respuestas de error de la API, qué significan los códigos por dominio y cómo manejar rate limits y reintentos.
Anatomía de un error
Todas las respuestas de error comparten la misma estructura. El campo code es un identificador estable con formato {dominio}_{número} que puedes usar para manejar cada caso de forma programática (no dependas del texto de message, que es legible pero puede cambiar).
{
"success": false,
"message": "API Key inválida o no existe.",
"status": 401,
"code": "auth_05"
}
| Campo | Descripción |
|---|---|
success | false en todo error. |
message | Descripción legible en español. |
status | Categoría del error dentro del cuerpo. No dependas de él ni del código HTTP de transporte: la API suele responder HTTP 200 incluso en errores. |
code | Código estructurado {dominio}_{número}. |
request_id | UUID de seguimiento (presente en errores 500). Inclúyelo al contactar a soporte. |
Guarda el request_id junto con el log de la operación, pero no lo reutilices como identificador propio de mensajes, campañas o transacciones: para eso usa el reference que devuelve cada envío. Los eventos de webhooks son independientes y no incluyen el request_id de la petición original.
Códigos HTTP
| HTTP | Significado | ¿Reintentar? |
|---|---|---|
200 | Éxito. | No aplica |
400 | Error de validación o de lógica de negocio (parámetros, saldo, mensaje). | No, corrige la petición. |
401 | API Key ausente, inválida o cuenta deshabilitada. | No, revisa tu llave. |
403 | La IP no está en la lista blanca de tu cuenta. | No, autoriza la IP en el panel. |
429 | Se excedió el rate limit. | Sí, con backoff. |
500 | Error interno del servidor. | Sí, con backoff; guarda el request_id. |
En la práctica, la mayoría de los errores de validación y de negocio llegan con HTTP 200 en el transporte: la columna anterior es la categoría del error. La forma confiable de detectar un error es success: false + el code; nunca dependas solo del código HTTP.
Excepción: los endpoints OTP v2 (/v2/otp*) sí responden con status HTTP semántico (201, 400, 401, 404, 409, 410, 429, 502…) y usan un envelope propio con slug en error (no code), más hint y next. Solo los errores de autenticación (auth_*), que resuelve la capa común antes de llegar al endpoint, siguen llegando con HTTP 200. Detalle abajo en la sección Verificación OTP.
Códigos por dominio
El prefijo del code indica el área de la API donde se originó la respuesta y el número identifica el caso exacto. Esta es la referencia de los códigos que la API documenta hoy; el message siempre acompaña al code con el detalle en español.
No todos los códigos son errores. Los que aparecen con HTTP 200 (por ejemplo sms_11) indican éxito y llegan con success: true. Por eso revisa siempre success antes que el code.
Autenticación y acceso (auth_)
| Código | Categoría | Significado |
|---|---|---|
auth_01 | 401 | No se proporcionó API Key. |
auth_04 | 403 | La IP no está en la lista blanca de tu cuenta. |
auth_05 | 401 | API Key inválida o inexistente. |
auth_06 | 401 | Cuenta deshabilitada o suspendida. |
auth_deprecated | 410 | Usaste el encabezado heredado token; migra al encabezado apikey. |
Verificación OTP (otp_)
Los endpoints OTP v2 (/v2/otp, /v2/otp/verify, /v2/otp/status) usan un envelope distinto al resto de la API: el slug estable va en error, el status HTTP es real (la columna HTTP de abajo es el código de transporte que efectivamente recibes) y cada error trae hint (qué hacer, en español) y next (endpoints sugeridos).
{
"success": false,
"state": "pending",
"error": "otp_incorrect_code",
"message": "El código es incorrecto.",
"hint": "Código incorrecto. Te quedan 3 intentos.",
"next": ["/v2/otp/verify", "/v2/otp"],
"attempts_remaining": 3,
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
error | HTTP | Significado |
|---|---|---|
otp_missing_param | 400 | Falta un parámetro obligatorio (hint indica cuál). |
otp_invalid_param | 400 | Parámetro inválido o que no aplica al endpoint (incluye parámetros de la API OTP anterior). |
otp_invalid_channel | 400 | channel debe ser sms, whatsapp o voice. |
otp_message_invalid | 400 | El message no incluye {{code}}/{{company}}, usa caracteres fuera de GSM-7 o excede 160 caracteres. |
otp_incorrect_code | 401 | Código incorrecto (la respuesta trae attempts_remaining). |
otp_insufficient_credits | 402 | Saldo insuficiente (incluye code: sms_07, el código estándar de saldo de toda la API). |
otp_not_found | 404 | No hay verificación activa para el número. |
otp_already_verified | 409 | El número ya está verificado. |
otp_code_expired | 410 | El código expiró. |
otp_throttled | 429 | Cooldown de reenvío (30s) activo; espera resend_available_in segundos (también en el header Retry-After). |
otp_max_attempts | 429 | Se agotaron los 7 intentos de la ronda; rota el código con code_rotate: true. |
otp_internal | 500 | Error inesperado; reintenta con backoff y guarda el request_id. |
otp_send_failed | 502 | El canal no pudo entregar el mensaje. |
Envío de mensajes (sms_, whatsapp_)
| Código | Categoría | Significado |
|---|---|---|
sms_01 | 400 | Mensaje no definido. |
sms_02 | 400 | Mensaje demasiado largo. |
sms_03 | 400 | Números no definidos. |
sms_04 | 400 | Número con formato incorrecto (deben ser 10 dígitos). |
sms_05 | 400 | Código de país no definido. |
sms_06 | 400 | Nombre de campaña de más de 100 caracteres. |
sms_07 | 400 | Créditos insuficientes. |
sms_11 | 200 | Mensajes enviados correctamente. |
sms_15 | 400 | Más de 500 destinatarios en una petición. |
sms_16 | 400 | Número repetido en la lista de destinatarios. |
sms_17 | 400 | Límite diario de sandbox excedido (1000 envíos de prueba). |
sms_18 | 400 | País no soportado. |
sms_19 | 200 | Envío exitoso a un número fijo (informativo). |
sms_20 | 400 | La fecha programada ya pasó. |
sms_21 | 400 | Formato de fecha inválido. |
sms_24 | 400 | Lada no soportada. |
sms_29 / sms_31 | 400 | Acortador: no se encontró URL en el mensaje / hay más de una URL. |
sms_36 | 400 | channel inválido (solo 1 = SMS o 2 = RCS). |
sms_37 / sms_38 | 400 | RCS: remitente obligatorio / remitente no vinculado a la cuenta. |
sms_39 / sms_40 | 400 | flash inválido (solo 0 o 1) / flash no compatible con RCS. |
whatsapp_04 / whatsapp_08 | 400 | Instancia no definida / la instancia no existe o está inactiva. |
whatsapp_21 | 200 | Mensaje de WhatsApp enviado o programado. |
whatsapp_23 | 400 | Número con formato incorrecto. |
whatsapp_28 / whatsapp_38 | 400 | Mensaje de más de 1000 caracteres / caption de más de 1000 caracteres. |
whatsapp_37 | 400 | URL multimedia inválida o con extensión no permitida. |
Contactos y listas (contact_, agenda_)
| Código | Categoría | Significado |
|---|---|---|
contact_01 | 400 | list_key no definido. |
contact_02 | 400 | Número no definido. |
contact_03 | 400 | Número con formato incorrecto (mínimo 10 dígitos, solo números). |
contact_04 / contact_06 | 400 | Nombre / email de más de 100 caracteres. |
contact_05 | 400 | Email con formato incorrecto. |
contact_07 | 400 | Lista no encontrada. |
contact_13 | 200 | Contacto agregado. |
contact_15 | 200 | Contacto actualizado. |
contact_17 | 400 | La lista está vinculada a una herramienta y no admite cambios. |
contact_19 | 400 | El contacto no existe. |
agenda_01 | 200 | Lista creada (devuelve list_key). |
agenda_02 | 400 | Nombre de lista no definido. |
agenda_03 | 400 | Ya existe una lista con ese nombre en la cuenta. |
change_name_02 / change_name_03 | 400 | Falta list_key / falta el nuevo nombre. |
get_contacts_02 | 400 | list_key no definido. |
Otros dominios
| Código | Categoría | Significado |
|---|---|---|
credit_01 | 200 | Consulta de créditos exitosa. |
report_01 / report_02 | 400 | Falta start_date / falta end_date. |
report_03 | 400 | start_date es posterior a end_date. |
report_04 | 200 | Reporte generado. |
report_05 | 400 | Límite de peticiones de reporte excedido. |
details_02 | 400 | campaign_id no definido o no numérico. |
details_03 / details_04 | 400 | La campaña no existe / aún se está procesando. |
add_webhook_01 | 200 | Webhook registrado. |
add_webhook_02 a add_webhook_06 | 400 | URL no definida, formato inválido, estado no definido o inválido, o la URL no respondió a la verificación. |
err_03 | 400/500 | Error genérico; el message describe la causa. |
rate_01 | 429 | Rate limit excedido. |
server_01 | 500 | Error interno del servidor. |
¿Recibiste un code que no está en esta tabla? El message de la respuesta describe la causa. Cada endpoint también lista en el API Reference los códigos que puede devolver, por ejemplo /sms/send o /v2/otp/verify.
Errores frecuentes de SMS
// Créditos insuficientes
{ "success": false, "message": "insufficient_credits", "code": "sms_07" }
// Mensaje excede el límite de caracteres
{ "success": false, "message": "message_too_long", "code": "sms_02" }
Antes de un envío grande, valida el saldo con /credits/consult para no fallar a mitad de la campaña con sms_07.
Rate limits
| Entorno | Límite | Al exceder |
|---|---|---|
| Producción | 100 mensajes/segundo | HTTP 429, code: rate_01 |
| Sandbox | 1000 envíos de prueba/día | success: false, code: sms_17 |
Manejo de errores
Revisa success primero
Si success es false, no proceses la respuesta como éxito aunque el HTTP sea 200.
Ramifica por code, no por message
Usa el code estructurado (sms_07, auth_04, etc.) en tu lógica. El message es para logs y humanos.
Reintenta solo 429 y 500
Implementa reintentos con backoff exponencial únicamente en 429 y 500. Los 4xx restantes requieren corregir la petición.
Registra el request_id
En errores 500, guarda el request_id y compártelo con soporte para acelerar el diagnóstico.
Con esto puedes manejar cualquier respuesta de la API de forma robusta. Vuelve a la Guía rápida o explora el API Reference.