Saltar al contenido principal

Errores

Todo fallo generado por Joinotify usa el mismo sobre:

{
"error": {
"message": "Frase legible que describe lo que ocurrió.",
"type": "codigo_estable"
}
}

Programa contra el campo type — es estable. message está escrito para humanos y su redacción puede cambiar sin aviso.

Campos opcionales​

Algunos campos aparecen cuando tienen sentido.

issues — fallos de validación​

422 Unprocessable Entity
{
"error": {
"message": "Invalid request payload.",
"type": "invalid_request",
"issues": [
{
"code": "too_small",
"path": ["body"],
"message": "String must contain at least 1 character(s)"
}
]
}
}

meta — error reenviado de Meta​

Cuando el rechazo viene de Meta, devolvemos su cuerpo original intacto. Ahí están el code, el error_subcode y el fbtrace_id que pide el soporte de Meta.

422 Unprocessable Entity
{
"error": {
"message": "The Meta Cloud API rejected the request.",
"type": "meta_error",
"meta": {
"error": {
"message": "(#131047) Re-engagement message",
"type": "OAuthException",
"code": 131047,
"error_subcode": 2494010,
"fbtrace_id": "AbCdEfGhIjK"
}
}
}
}

reason y action — sin derecho de uso​

Un 402 dice por qué se bloqueó el acceso y cuál es la salida: qué hacer (action.kind) y la página del panel donde se hace (action.url). Muestra ambas cosas a quien administra la integración — en un log de errores, "pago requerido" sin un enlace es un callejón sin salida.

402 Payment Required
{
"error": {
"message": "Your free trial has ended. Subscribe to keep using the API.",
"type": "payment_required",
"reason": "trial_ended",
"billable_numbers": 1,
"action": {
"kind": "subscribe",
"url": "https://app.joinotify.com/checkout"
}
}
}
action.kindCuándo
subscribePrueba terminada, suscripción cancelada o inexistente
update_payment_methodEl último pago falló (past_due)
pay_invoicesHay facturas pendientes (unpaid)
finish_checkoutEl primer pago aún no se confirmó (incomplete)
resume_subscriptionLa suscripción está en pausa (paused)
renew_licenseLa licencia caducó (license_expired)
upgrade_planSe alcanzó el techo mensual de mensajes del plan (message_limit)
manage_numbersHay más números conectados de los que la cuenta paga (capacity_exceeded)

El bloqueo no llega sin aviso. Durante el período de gracia las peticiones siguen pasando, y la respuesta trae la cabecera X-Joinotify-Billing-Warning con el motivo — past_due, por ejemplo. Registra esa cabecera en tus logs: es la que avisa que el 402 está en camino.

allowed — método no aceptado​

Una ruta que existe, llamada con el método equivocado, responde 405 y no 404. Los métodos aceptados vienen en la cabecera Allow y en error.allowed, para que busques el error en el verbo y no en la URL.

405 Method Not Allowed
{
"error": {
"message": "PUT is not supported on this route. Allowed: DELETE, GET, HEAD.",
"type": "method_not_allowed",
"allowed": ["DELETE", "GET", "HEAD"]
}
}

request_id — fallo inesperado​

Un 500 no dice qué se rompió — la causa queda en nuestros registros, nunca en la respuesta. Lo que trae es request_id, el mismo valor de la cabecera x-request-id. Envía ese id a soporte y encontraremos el fallo.

500 Internal Server Error
{
"error": {
"message": "Internal server error.",
"type": "internal",
"request_id": "0d72872e-f70e-4c25-95c6-6bec4d33a800"
}
}

Tabla de códigos​

HTTPtypeSignificadoQué hacer
400invalid_requestNingún ID de recurso después de /v1/.Corrige la ruta.
400invalid_requestEl cuerpo no es JSON válido, o la ruta tiene una secuencia % mal formada.Corrige el cuerpo o la codificación de la URL.
401authenticationToken ausente, mal formado, desconocido o revocado.Revisa la cabecera y la clave.
402payment_requiredSin derecho de uso: prueba terminada, pago pendiente, techo del plan…Sigue error.action.
403tenant_inactiveCuenta suspendida o cancelada.Habla con soporte.
403forbiddenEl recurso no pertenece a tu cuenta.Confirma el phone_number_id / waba_id.
403forbidden_endpointRuta en la lista de bloqueo del proxy.Consulta Compatibilidad con Meta.
404not_foundRuta, clave, endpoint o entrega inexistente.Comprueba el ID.
405method_not_allowedLa ruta existe, pero no con ese método.Usa uno de los métodos de Allow.
409no_phone_numberNingún número conectado y from omitido.Conecta un número o indica from.
409no_wabaNinguna cuenta de empresa conectada y wabaId omitido.Conecta una WABA o indica wabaId.
409idempotency_in_progressUna petición con el mismo Idempotency-Key sigue en curso.Espera y repite con la misma clave.
422invalid_requestEl payload no pasó la validación.Lee issues.
413payload_too_largeEl cuerpo es mayor de lo que acepta la ruta.Divide el lote o reduce el cuerpo.
415unsupported_media_typeCharset o Content-Encoding del cuerpo no aceptado.Envía JSON en UTF-8, sin compresión o con gzip.
422meta_errorMeta lo rechazó (4xx).Lee error.meta.
422idempotency_key_reusedEl Idempotency-Key ya se usó con otro cuerpo.Usa una clave nueva por operación.
429rate_limitLímite de la categoría superado.Espera Retry-After. Consulta Límites de uso.
500internalFallo inesperado por nuestro lado.Reintenta; si persiste, envíanos el request_id.
502meta_errorMeta respondió con un 5xx.Reintenta con backoff.
502upstream_errorNo pudimos alcanzar a Meta.Reintenta con backoff.
504upstream_errorMeta tardó más de 30 segundos.Reintenta con backoff.

¿Error nuestro o de Meta?​

Una regla sencilla: si existe el campo error.meta, el rechazo fue de Meta. Joinotify solo lo reenvió.

Eso importa a la hora de actuar. invalid_request significa que tu payload está mal y un reintento idéntico volverá a fallar. meta_error suele ser una regla de negocio de WhatsApp — ventana de 24 horas cerrada, plantilla no aprobada, número inválido — y la corrección es otra.

En el espejo /v1/* la regla cambia: cuando Meta responde, recibes su respuesta, en su formato, sin nuestro sobre. El sobre de Joinotify solo aparece en fallos anteriores a la llamada.

Rastrear una petición​

Cada respuesta trae x-request-id. Si envías tu propio x-request-id, te lo devolvemos — lo que facilita cruzar los registros de ambos lados.

Tu id solo se acepta si tiene de 8 a 128 caracteres, entre letras, dígitos, ., _, : y -. Si no, generamos uno nuestro (un UUID) — confía siempre en el que vuelve en la cabecera.

curl -i https://api.joinotify.com/templates \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'x-request-id: pedido-1042-intento-1'

Guarda ese identificador en tus registros. Es lo primero que pide soporte.

Qué errores conviene reintentar​

Situación¿Reintentar?
429 rate_limitSí, después de Retry-After.
500 · 502 · 504Sí, con backoff exponencial y un tope de intentos.
409 idempotency_in_progressSí, tras unos segundos, con la misma clave.
402 payment_requiredNo. Resuelve lo que indica error.action.
401 · 403 · 404 · 405 · otros 409No. El reintento fallará igual.
400 · 413 · 415 · 422 invalid_requestNo. Corrige el payload.
422 meta_errorDepende del code de Meta. Un error de ventana o de plantilla no se arregla reintentando.