Esta página es el diccionario global de error codes del API. Cada respuesta de error lleva un code estable, machine-readable, que tu cliente debe usar para ramificar lógica. El detail es legible y se traduce; el code nunca cambia ni se traduce.
Para la introducción suave al envelope de errores, ver Errores. Para los flujos relacionados, ver Idempotencia, Límites de uso y Arquitectura de webhooks.
Forma de la respuesta
Toda respuesta de error sigue el envelope JSON:API estándar — un array errors y un objeto meta. Una sola respuesta puede llevar varios errores cuando aplica (típicamente en validación de campos).
{
"errors": [
{
"status": "422",
"code": "phone_not_verified",
"detail": "El teléfono no está verificado.",
"source": { "pointer": "/data/attributes/phone" },
"meta": { "masked_phone": "+52 55 ••••1234" }
}
],
"meta": {
"version": "1.47.0",
"api_version": "v1",
"request_id": "a1b2c3d4e5f6",
"datetime": { "timezone": "UTC", "format": "ISO 8601" }
}
}Reglas globales
codees estable, snake_case ASCII, y nunca se traduce. Ramifica lógica de cliente por este campo.detailes legible para humanos y se traduce segúnAccept-Language(es/en).statuses el código HTTP como string (mismo número que el response status).source.pointer(opcional) apunta al campo del request body que falló (JSON Pointer, RFC 6901).metapor-error es opcional — algunos códigos incluyen contexto (ej.masked_phone,retry_after_seconds).- Una respuesta 422 puede llevar varios errores — uno por cada campo inválido.
meta.request_idcorrela con los logs del servidor — inclúyelo siempre que reportes un bug.
Códigos HTTP comunes
| Status | Significado en este API |
|---|---|
| 400 Bad Request | El request está malformado — JSON inválido, query param ausente, formato incorrecto. No vale reintentar sin corregir. |
| 401 Unauthorized | Falta autenticación, token expirado, o credenciales inválidas. El cliente debe re-autenticar antes de reintentar. |
| 403 Forbidden | Autenticado pero sin permiso para ese recurso o acción. No vale reintentar — el cliente no tiene el rol o el método de auth requerido. |
| 404 Not Found | El recurso no existe o no es visible para el caller. |
| 405 Method Not Allowed | Path correcto pero método HTTP equivocado. |
| 409 Conflict | Estado en conflicto — típicamente idempotencia en vuelo, recurso ya en estado terminal, o race condition. Puede tener sentido reintentar después de Retry-After. |
| 410 Gone | Recurso existió pero fue eliminado. |
| 413 Payload Too Large | El body o el upload supera el tope del endpoint. |
| 422 Unprocessable Entity | El request fue parseado pero falló la validación de negocio. Una sola respuesta puede listar varios errores. |
| 429 Too Many Requests | Rate limit excedido. Esperá Retry-After segundos antes de reintentar. |
| 500 Internal Server Error | Falla inesperada del servidor. Reintentá con backoff exponencial. |
| 502 Bad Gateway | Servicio upstream (Banxico, Stripe) no responde o devuelve algo malformado. Reintentá con backoff. |
| 503 Service Unavailable | Subsistema temporalmente desactivado o sobrecargado. Reintentá tras Retry-After. |
Taxonomía por categoría
Autenticación y sesión
code | Status | Cuándo se dispara | Endpoints | Cliente debe |
|---|---|---|---|---|
unauthorized | 401 | Credenciales ausentes, inválidas o token expirado. | Todos los protegidos | Re-autenticar (relogin o renovar API key) |
invalid_credentials | 401 | Email o contraseña incorrectos. | POST /v1/auth/login, POST /v1/auth/request-verification-otp | Mostrar mensaje genérico al usuario — no revelar cuál falló |
auth_header_empty | 401 | El header Authorization está vacío. | Todos los protegidos | Adjuntar Authorization: Bearer ... |
unrecognized_auth_format | 401 | El header Authorization no parsea como Bearer <token>. | Todos los protegidos | Usar Bearer mxcep_... (API key) o cookie JWT |
invalid_api_key | 401 | El prefijo o el formato de la API key es inválido. | Endpoints M2M | Regenerar la key desde /uso o POST /v1/users/me/api-key/regenerate |
invalid_encrypted_api_key | 401 | La API key no descifra contra el secret server-side (rotación de claves o key falsificada). | Endpoints M2M | Regenerar la key |
api_key_decryption_failed | 401 | Falla criptográfica al descifrar la API key. | Endpoints M2M | Regenerar la key |
auth_method_denied | 403 | El endpoint exige cookie (UI) pero llegaste con API key, o viceversa. | Endpoints con audience restringido | Usar el método de auth correcto — ver Autenticación |
dashboard_token_misuse | 401 | Usaste un token de dashboard contra un endpoint M2M. | /v1/* M2M | Usar API key, no cookie |
token_expired | 401 | El JWT venció. | Endpoints con cookie | Renovar (re-login) o limpiar cookie |
token_lifetime_exceeded | 401 | El JWT supera el lifetime máximo absoluto del servidor. | Endpoints con cookie | Re-login |
invalid_token_format | 401 | El JWT no tiene 3 segmentos. | Endpoints con cookie | Re-login |
invalid_token_encoding | 401 | El JWT no decodifica como base64url. | Endpoints con cookie | Re-login |
invalid_token_signature | 401 | La firma del JWT no valida. | Endpoints con cookie | Re-login |
invalid_token_payload | 401 | El payload del JWT no parsea como JSON. | Endpoints con cookie | Re-login |
invalid_token_issuer | 401 | El iss del JWT no es el esperado. | Endpoints con cookie | Re-login |
invalid_token_audience | 401 | El aud del JWT no es el esperado. | Endpoints con cookie | Re-login |
unsupported_token_algorithm | 401 | El JWT usa un alg fuera de la whitelist. | Endpoints con cookie | Re-login |
session_invalidated | 401 | La sesión fue invalidada manualmente (force-logout, reset 2FA). | Endpoints con cookie | Re-login |
csrf_missing | 401 | El header X-CSRF-Token no está presente en una mutación cookie-only. | Mutaciones UI cookie | Refrescar la cookie y reenviar |
csrf_mismatch | 401 | El token CSRF no coincide con la cookie. | Mutaciones UI cookie | Refrescar la cookie y reenviar |
forbidden | 403 | Permiso denegado por RBAC. | Cualquiera | Pedir el permiso al admin de tu cuenta |
permission_denied | 403 | Variante de forbidden para endpoints admin. | /v1/admin/* | Verificar el rol del usuario |
account_suspended | 403 | La cuenta del caller está suspendida. | Todos los autenticados | Contactar soporte |
account_deleted | 403 | La cuenta fue soft-deleted. | Todos los autenticados | Contactar soporte |
registration_disabled | 403 | El flag global registration.enabled está OFF. | POST /v1/auth/register | Esperar a que se reactive |
OTP, 2FA y verificación
code | Status | Cuándo se dispara | Endpoints | Cliente debe |
|---|---|---|---|---|
phone_not_verified | 403 | El teléfono no fue verificado vía OTP todavía. | POST /v1/auth/login, mutaciones | Disparar POST /v1/auth/request-verification-otp |
phone_already_verified | 409 | El teléfono ya está verificado — no hay nada que hacer. | POST /v1/auth/request-verification-otp | Continuar al flujo normal |
otp_not_found | 404 | No hay OTP activo para esa combinación user+purpose. | POST /v1/auth/verify-phone, GET /v1/auth/otp-status | Re-solicitar OTP |
otp_invalid_code | 422 | El código OTP enviado es incorrecto. | POST /v1/auth/verify-phone, POST /v1/auth/verify-password-reset | Reingresarlo (máximo N intentos) |
otp_expired | 422 | El OTP venció antes de ser usado. | POST /v1/auth/verify-phone, password-reset | Re-solicitar OTP |
otp_max_attempts | 422 | Se agotaron los intentos para ese OTP. | POST /v1/auth/verify-phone, password-reset | Re-solicitar un OTP nuevo |
otp_already_used | 422 | El OTP ya fue consumido. | Confirmación OTP | Solicitar otro |
otp_cooldown | 429 | Estás pidiendo OTPs muy rápido — espera Retry-After. | POST /v1/auth/resend-otp | Esperar el cooldown |
invalid_otp_id | 422 | El otp_id enviado no tiene formato UUID. | Confirmación OTP | Re-solicitar OTP y usar el otp_id del response |
invalid_otp_parameters | 422 | El payload de OTP está incompleto. | Confirmación OTP | Revisar los campos requeridos |
missing_otp_id | 422 | Falta el otp_id en el body. | Confirmación OTP | Agregar otp_id |
invalid_verification_token | 422 | El token de un flujo de verificación es inválido o malformado. | POST /v1/auth/reset-password | Reiniciar el flujo de verificación |
invalid_reset_context | 422 | El contexto del reset (purpose, audience, expiry) no coincide. | POST /v1/auth/reset-password | Reiniciar el flujo |
purpose_mismatch | 422 | El OTP fue generado para otro propósito (ej. login vs. password reset). | POST /v1/auth/verify-password-reset, reset-password | Solicitar OTP con el purpose correcto |
confirmation_token_missing | 422 | Falta el confirmation_token en una operación de step-up. | POST /v1/auth/* step-up | Agregar confirmation_token |
confirmation_token_invalid | 422 | Token de confirmación malformado. | Step-up | Re-solicitar el token |
confirmation_token_malformed | 422 | Variante: el token no tiene la estructura esperada. | Step-up | Re-solicitar |
confirmation_token_mismatch | 422 | El token corresponde a otra acción distinta. | Step-up | Re-solicitar para la acción correcta |
confirmation_token_already_used | 409 | El token ya fue consumido (single-use). | Step-up | Re-solicitar uno nuevo |
confirmation_token_slug_mismatch | 422 | El token fue emitido para otro plan/slug. | Step-up de billing/plans | Re-emitir para el slug correcto |
twofa_required | 401 | El usuario tiene 2FA activo — necesitas resolver el challenge. | POST /v1/auth/login | Disparar POST /v1/auth/2fa/verify-challenge |
twofa_invalid_challenge | 422 | El código del challenge 2FA es incorrecto. | POST /v1/auth/2fa/verify-challenge | Reingresarlo |
twofa_invalid_context | 422 | El contexto 2FA (purpose / audience) no matchea. | 2FA endpoints | Reiniciar el flujo |
twofa_not_enabled | 422 | Intentaste verificar 2FA pero el usuario no lo tiene activo. | 2FA endpoints | Habilitar 2FA primero |
twofa_already_disabled | 422 | Se pidió disable de 2FA pero ya está disabled. | POST /v1/auth/2fa/confirm-disable | Ignorar — estado correcto |
twofa_already_enabled | 422 | Se pidió enable de 2FA pero ya está enabled. | POST /v1/auth/2fa/confirm-enable | Ignorar — estado correcto |
twofa_method_same | 422 | Se pidió cambiar a un método 2FA igual al actual. | POST /v1/auth/2fa/confirm-method-change | Elegir un método distinto |
twofa_method_unsupported | 422 | El método 2FA pedido no está soportado. | 2FA endpoints | Usar sms o email |
twofa_action_mismatch | 422 | El challenge resuelto no corresponde a la acción solicitada. | 2FA step-up | Re-iniciar el step-up |
Registro y cuenta
code | Status | Cuándo se dispara | Endpoints | Cliente debe |
|---|---|---|---|---|
email_already_registered | 422 | El email ya tiene cuenta verificada. | POST /v1/auth/register | Mostrar mensaje genérico anti-enum |
email_already_taken | 422 | Variante: el email no está libre. | Cambio de email | Pedir otro email |
email_already_verified | 409 | El email ya está verificado. | POST /v1/auth/email/request-verify | Continuar al flujo normal |
email_same_as_current | 422 | El email nuevo coincide con el actual. | POST /v1/auth/email/request-change | Ingresar uno distinto |
email_delivery_failed | 502 | El proveedor de email rechazó el envío. | Cambio/verify de email | Reintentar; si persiste, soporte |
phone_taken | 422 | El teléfono ya está en otra cuenta verificada. | Registro / cambio de phone | Pedir otro teléfono |
phone_required_for_email_change | 422 | Necesitás teléfono verificado antes de cambiar email. | POST /v1/auth/email/request-change | Verificar phone primero |
password_mismatch | 422 | password y password_confirmation no coinciden. | Registro / reset | Reingresar coincidentes |
password_too_short | 422 | La contraseña no cumple el largo mínimo. | Registro / reset / cambio | Usar contraseña más larga |
password_too_weak | 422 | La contraseña no cumple las reglas de complejidad. | Registro / reset / cambio | Reforzar la contraseña |
new_password_too_short | 422 | Variante: en cambio de contraseña, la nueva es muy corta. | Cambio de contraseña | Usar más caracteres |
current_password_required | 422 | Falta current_password en un cambio de contraseña. | Cambio de contraseña | Agregar el campo |
current_password_incorrect | 422 | El current_password no matchea. | Cambio de contraseña | Reingresar correctamente |
invalid_email_change_context | 422 | El contexto del cambio de email no es válido (purpose mismatch). | Email change apply | Reiniciar el flujo |
sms_delivery_failed | 502 | El proveedor SMS rechazó el envío del OTP. | Re-OTP / register | Reintentar; si persiste, soporte |
unsupported_locale | 422 | El locale enviado no está en la whitelist. | PUT /v1/users/me | Usar es o en |
language_unsupported | 422 | Variante de unsupported_locale. | PUT /v1/users/me | Usar es o en |
Validación general de campos
Estos códigos los emite Support\Validator y pueden aparecer en cualquier endpoint con body.
code | Status | Cuándo se dispara | Cliente debe |
|---|---|---|---|
required | 422 | Campo obligatorio ausente. | Incluir el campo |
missing_field | 422 | Variante de required para campos concretos. | Incluir el campo |
invalid_format | 422 | El campo no matchea el formato esperado. | Corregir el formato |
invalid_value | 422 | El valor está fuera del set permitido. | Usar un valor del enum |
invalid_type | 422 | El tipo PHP/JSON no coincide. | Ajustar el tipo |
invalid_email | 422 | El email no parsea. | Corregir |
invalid_phone | 422 | El phone no parsea como E.164. | Usar formato +52... |
invalid_uuid | 422 | No es un UUID v4. | Pasar UUID válido |
invalid_url | 422 | La URL es malformada. | Corregir |
invalid_url_scheme | 422 | Solo https aceptado. | Usar https:// |
url_ssrf_blocked | 422 | La URL apunta a una IP privada / DNS rebind. | Usar host público |
url_dns_failed | 422 | DNS no resolvió el host. | Verificar el host |
max_length_exceeded | 422 | Supera el max length del campo. | Acortar |
invalid_amount | 422 | Monto no parsea como decimal positivo. | Corregir |
invalid_date | 422 | Fecha malformada o fuera de rango. | Usar ISO 8601 |
invalid_account_format | 422 | El número de cuenta no es CLABE 18 / tarjeta 16 / phone E.164. | Corregir según account_type |
invalid_clabe_checksum | 422 | El dígito verificador de la CLABE falla. | Verificar la CLABE |
invalid_card_luhn | 422 | El check Luhn de la tarjeta falla. | Verificar el PAN |
invalid_account_length | 422 | Largo incorrecto para el tipo (18 CLABE / 16 tarjeta / 10 phone). | Ajustar largo |
invalid_bank_code | 422 | El código de banco no existe en el catálogo. | Usar GET /v1/public/banks |
invalid_image | 422 | La imagen no decodifica. | Reenviar |
invalid_image_format | 422 | Formato fuera de JPEG/PNG/WEBP. | Convertir a JPEG/PNG |
image_too_large | 422 | Imagen supera el tope. | Reducir tamaño |
validation_error | 422 | Error de validación genérico. | Revisar source.pointer y meta |
validation_failed | 422 | Variante para fallas combinadas. | Revisar el detalle |
invalid_request_body | 400 | El JSON no parsea. | Enviar JSON válido |
invalid_payload | 400 | El shape del payload no coincide con el contrato. | Revisar la spec del endpoint |
payload_too_large | 413 | Body completo supera el tope. | Reducir |
payload_corrupt | 422 | El payload llegó truncado o malformado. | Reenviar |
message_build_failed | 422 | El server no pudo armar el mensaje a despachar. | Reintentar |
Idempotencia
Ver Idempotencia para el contrato completo.
code | Status | Cuándo se dispara | Cliente debe |
|---|---|---|---|
invalid_idempotency_key | 400 | El header Idempotency-Key no cumple [A-Za-z0-9_-]{1,255}. | Generar key nueva con caracteres válidos |
idempotency_key_in_progress | 409 | Hay otra request en vuelo con la misma key. | Esperar Retry-After segundos y reintentar |
idempotency_key_reused | 422 | La misma key se usó antes con un body distinto. | Generar key nueva o reusar el body exacto |
Validación de transferencias SPEI
Estos códigos aplican a POST /v1/validate, POST /v1/validate-ocr, y al pipeline de validación masiva.
code | Status | Cuándo se dispara | Cliente debe |
|---|---|---|---|
clabe_prefix_not_recognized | 422 | Los primeros 3 dígitos de la CLABE no mapean a ningún banco. | Verificar la CLABE — puede no ser SPEI |
bank_code_required_for_phone | 422 | Para account_type=phone se requiere bank_code explícito. | Agregar bank_code |
bank_code_unresolvable_for_phone | terminal | Async: no se pudo resolver el banco del teléfono via DiMo. | Esperar — el estado final es error |
intra_bank_no_cep | terminal | Async: la transferencia es intra-banco y Banxico no emite CEP. | Buscar el comprobante interno del banco |
clave_or_ref_required | 422 | Se requiere clave_rastreo o referencia_numerica. | Incluir uno |
image_or_image_url_required | 422 | Para OCR: ni image ni image_url. | Enviar uno |
image_not_readable | 422 | OCR no pudo leer la imagen. | Adjuntar imagen más clara |
image_not_readable_receipt | 422 | OCR leyó pero no parece un comprobante SPEI. | Adjuntar el comprobante correcto |
image_not_spei_receipt | 422 | El recibo no es SPEI (otro tipo de transferencia). | Adjuntar comprobante SPEI |
Códigos terminales async de validaciones
Estos códigos aparecen en validations.error_code cuando una validación async termina en estado error o failed. Ver Validaciones async y Política de reintentos.
code | Cuándo se dispara | Dispara webhook validation.error |
|---|---|---|
network | Banxico inaccesible tras agotar reintentos de red. | Sí |
captcha | Banxico devolvió captcha activo. | Sí |
max_retries_exceeded | Se agotó el tope de reintentos del plan. | Sí |
ttl_expired | TTL del job venció antes de transicionar a terminal. | No |
preflight_failed | Falló la validación previa al despacho. | No |
rate_limit_exhausted | Se agotaron los slots de proxy/rate. | No |
dispatch_failed | El handler no pudo despachar el job. | No |
bank_code_unresolvable_for_phone | Phone account_type sin banco resoluble. | No |
intra_bank_no_cep | Intra-banco — Banxico no emite CEP. | No |
banxico_rate_limit | Banxico devolvió rate-limit puntual. | (interno, reintenta) |
banxico_rate_limit_exhausted | Se agotaron los retries por rate-limit. | (interno) |
banxico_captcha_active | Banxico tiene captcha global activo. | (interno, reintenta) |
rate_limit_batch | Lote de batch chocó con rate-limit. | No |
ocr_error | OCR upstream (Gemini) falló. | No |
banxico_error | Error genérico Banxico. | No |
Beneficiarios
code | Status | Cuándo se dispara | Endpoints | Cliente debe |
|---|---|---|---|---|
query_param_account_required | 400 | Falta ?account= en la query. | GET /v1/beneficiaries/lookup, /validate-account | Agregar el param |
query_param_last_digits_required | 400 | Falta ?last_digits= en la query. | GET /v1/account-directory/lookup | Agregar el param |
invalid_last_digits_length | 422 | last_digits no tiene 4 dígitos. | GET /v1/account-directory/lookup | Pasar 4 dígitos |
account_number_required | 422 | Falta account_number en el body. | POST /v1/beneficiaries | Agregar el campo |
beneficiary_already_registered | 422 | La cuenta ya está en tus beneficiarios. | POST /v1/beneficiaries | Reutilizar el existente |
beneficiary_not_found_for_account | 404 | No hay beneficiario asociado a esa cuenta. | GET /v1/beneficiaries/lookup | Crearlo |
account_not_in_catalog | 404 | La cuenta no aparece en el directorio cross-tenant. | GET /v1/account-directory/lookup | Comportarse como "sin info" |
Importes masivos (validations + beneficiaries)
code | Status | Cuándo se dispara | Endpoints | Cliente debe |
|---|---|---|---|---|
file_required | 422 | El upload no incluye archivo. | POST /v1/{validations,beneficiaries}/imports | Adjuntar el archivo |
multi_upload_not_allowed_for_text | 422 | Multi-select pero los archivos son texto plano. | POST /v1/validations/imports | Subir uno por uno o usar imágenes/ZIP |
parse_failed | 422 | El parser no pudo extraer filas. | Imports (preview) | Revisar el formato del archivo |
commit_failed | 422 | El commit del import falló a mitad. | POST /v1/{validations,beneficiaries}/imports/{id}/commit | Revisar el detalle; reintentar si idempotent |
job_not_editable | 422 | Querés editar un job en estado terminal. | PATCH /v1/validations/imports/{id}/rows/{row_id} | El job ya fue committed |
job_not_cancellable | 409 | Cancel pedido sobre un job en estado no cancelable. | Imports admin | El job ya terminó |
no_valid_fields | 422 | El payload de edit no tiene campos válidos. | PATCH de import row | Incluir al menos un campo |
zip_pack_failed | 422 | Falló el repacking de un ZIP de imágenes. | POST /v1/validations/imports con ZIP | Reintentar con ZIP estándar |
no_valid_test_files | 422 | Ningún archivo del upload es procesable. | Tests admin | Revisar el set |
duplicate_clave_rastreo | (row) | Mismo clave_rastreo en filas distintas del mismo import. | preview rows | Editar o descartar la fila duplicada |
duplicate_tuple | (row) | Tupla (emisor, receptor, fecha, monto) duplicada. | preview rows | Editar o descartar |
account_duplicate | (row) | Beneficiary import: el account_number repite. | preview rows | Editar/descartar |
alias_duplicate | (row) | Beneficiary import: el alias repite. | preview rows | Editar/descartar |
alias_missing | (row) | Beneficiary import: falta alias. | preview rows | Agregarlo |
clabe_checksum_failed | (row) | CLABE de la fila falla checksum. | preview rows | Corregir |
card_luhn_failed | (row) | Tarjeta de la fila falla Luhn. | preview rows | Corregir |
bin_unknown | (row) | El BIN de la tarjeta no mapea a banco. | preview rows | Verificar el PAN |
masked_account_unresolved | (row) | La cuenta venía enmascarada y no resolvió. | preview rows | Editar con la cuenta completa |
no_match | (row) | El free-form parser no detectó campos. | preview rows | Editar manualmente |
no_usable_data | (row) | La fila no tiene datos suficientes. | preview rows | Editar o descartar |
tracking_missing | (row) | Falta clave_rastreo. | preview rows | Agregar |
clabe_missing | (row) | Falta CLABE/cuenta. | preview rows | Agregar |
fecha_missing | (row) | Falta fecha. | preview rows | Agregar |
fecha_invalid | (row) | Fecha no parsea. | preview rows | Corregir |
monto_missing | (row) | Falta monto. | preview rows | Agregar |
monto_invalid | (row) | Monto no parsea. | preview rows | Corregir |
Plan y facturación
code | Status | Cuándo se dispara | Endpoints | Cliente debe |
|---|---|---|---|---|
plan_not_found | 404 | El plan referenciado no existe. | /v1/billing/*, /v1/plans/{slug} | Listar planes con GET /v1/plans/public |
billing_plan_not_found | 422 | Variante: el slug no resuelve a un plan activo. | POST /v1/billing/checkout-session | Revisar el slug |
billing_plan_is_free | 422 | Intentas subscribir a un plan free vía Stripe checkout. | Checkout | El plan free se asigna sin Stripe |
billing_plan_not_self_service | 422 | Plan no asignable vía self-service. | Checkout | Contactar soporte |
billing_plan_slug_required | 422 | Falta plan_slug en el body. | Checkout | Agregar el campo |
billing_plan_slug_too_long | 422 | El slug supera el max. | Checkout / admin | Acortar |
billing_plan_slug_invalid | 422 | El slug no cumple ^[a-z0-9-]+$. | Checkout / admin | Corregir |
billing_intent_invalid_shape | 422 | La cookie app_checkout_intent está corrupta. | Checkout | Limpiar la cookie y reiniciar |
billing_invalid_interval | 422 | El interval no es month ni year. | Checkout / admin prices | Usar uno permitido |
billing_invalid_kind | 422 | El kind del precio no es base ni metered. | Admin prices | Usar uno permitido |
billing_disabled | 503 | El módulo de billing está desactivado en este deploy. | /v1/billing/* | Esperar a que se active |
no_active_subscription | 404 | El usuario no tiene suscripción activa. | /v1/billing/subscription/* | Iniciar checkout |
subscription_race_retry | 409 | Race condition mutando la subscripción — el cliente debe reintentar. | /v1/billing/subscription/* | Reintentar tras 1-2s |
subscription_not_found | 404 | La suscripción no existe. | Admin subs | Verificar el user_id |
stripe_cancel_failed | 502 | Stripe rechazó el cancel. | /v1/billing/subscription/cancel | Reintentar |
stripe_checkout_failed | 502 | Stripe rechazó la creación de session. | Checkout | Reintentar |
stripe_unreachable | 502 | Stripe API timeout/connection error. | Cualquier billing | Reintentar |
stripe_invoice_malformed | 422 | El invoice de Stripe llegó incompleto en el webhook. | (interno) | (logged y resync) |
stripe_subscription_malformed | 422 | El subscription de Stripe llegó incompleto. | (interno) | (logged y resync) |
stripe_meter_lookup_failed | 502 | Stripe meter lookup falló. | Billing metered | Reintentar |
stripe_meter_missing | 422 | El meter esperado no existe en Stripe. | Billing metered | Crear el meter o desactivar metered |
stripe_product_create_failed | 502 | Falló creación de Product en Stripe. | Admin plans | Reintentar |
stripe_price_not_found | 404 | El stripe_price_id no existe en Stripe. | Admin prices | Verificar el ID |
stripe_price_archive_failed | 502 | Stripe rechazó archivar el precio. | Admin prices | Reintentar |
stripe_price_id_required | 422 | Falta stripe_price_id. | Admin prices | Agregar |
stripe_price_id_format_invalid | 422 | El stripe_price_id no matchea price_*. | Admin prices | Corregir |
price_not_found | 404 | El precio local no existe. | Admin prices | Verificar el ID |
price_local_id_required | 422 | Falta price_local_id. | Admin prices | Agregar |
price_not_active | 422 | El precio está archived. | Checkout / admin | Activar primero |
price_already_in_catalog | 422 | El stripe_price_id ya está usado por otro precio. | Admin prices | Reutilizar el existente |
price_combo_already_exists | 422 | Combinación (plan, interval, currency, kind) duplicada. | Admin prices | Editar el existente |
price_plan_mismatch | 422 | El precio referenciado no pertenece a ese plan. | Admin prices | Usar el plan correcto |
source_price_id_required | 422 | Falta source_price_id en migrate. | Admin price migrate | Agregar |
unit_amount_required | 422 | Falta unit_amount. | Admin prices | Agregar |
overage_update_failed | 422 | Falló el toggle de overage. | /v1/billing/overage/{enable,disable} | Reintentar |
overage_not_applicable | 422 | El plan actual no soporta overage. | Overage toggle | Upgradear plan |
refund_amount_invalid | 422 | Monto de refund inválido (negativo o > invoice). | Admin refund | Corregir |
refund_invoice_not_found | 404 | El invoice referenciado no existe en Stripe. | Admin refund | Verificar el invoice_id |
admin_grant_failed | 422 | Falló el grant de plan admin-side. | Admin grant-plan | Revisar el detalle |
comp_amount_invalid | 422 | Monto de compensación inválido. | Admin comp | Corregir |
comp_user_missing | 422 | Falta target user en comp. | Admin comp | Agregar |
comp_user_not_found | 404 | El user de comp no existe. | Admin comp | Verificar |
Finanzas
code | Status | Cuándo se dispara | Endpoints | Cliente debe |
|---|---|---|---|---|
invalid_month | 400 | Param month ausente o no YYYY-MM. | /v1/finance/*, /v1/api-usage/* | Pasar YYYY-MM |
invalid_period | 400 | Param period no matchea convención. | Finance / usage | Corregir |
invalid_range | 400 | Range from/to incoherente. | Finance / usage | Corregir |
range_required | 400 | Falta range o from/to. | Finance / usage | Pasar uno |
pdf_render_failed | 500 | dompdf falló al renderizar. | Finance PDF | Reintentar; reportar si persiste |
xlsx_render_failed | 500 | PhpSpreadsheet falló. | Finance XLSX | Reintentar |
csv_stream_failed | 500 | El stream CSV falló. | Finance / exports CSV | Reintentar |
invalid_export_format | 422 | Formato pedido fuera de json/csv/xlsx/pdf. | Exports | Elegir uno soportado |
no_ceps | 404 | No hay CEPs disponibles en el rango. | GET /v1/finance/ceps | Ajustar rango |
Webhooks y notificaciones
code | Status | Cuándo se dispara | Endpoints | Cliente debe |
|---|---|---|---|---|
webhook_url_required | 422 | Falta url. | POST /v1/webhooks | Agregar |
webhook_url_empty | 422 | url enviado pero vacío. | PATCH /v1/webhooks/{id} | Agregar URL real |
webhook_url_too_long | 422 | url supera 2048 chars. | Webhooks CRUD | Acortar |
webhook_url_invalid_format | 422 | url no parsea. | Webhooks CRUD | Corregir |
webhook_url_not_https | 422 | El scheme no es https. | Webhooks CRUD | Usar HTTPS |
webhook_events_required | 422 | Falta el array events. | Webhooks CRUD | Agregar al menos uno |
webhook_events_too_many | 422 | Más eventos que el max. | Webhooks CRUD | Reducir |
webhook_event_invalid | 422 | Evento fuera del registry. | Webhooks CRUD | Ver eventos en docs |
webhook_status_invalid | 422 | status no es active/disabled. | Webhooks CRUD | Corregir |
webhook_limit_reached | 422 | Tope de webhooks del plan alcanzado. | POST /v1/webhooks | Upgradear plan o borrar uno |
signature_invalid | 401 | El webhook entrante no firma — Stripe ingress. | POST /v1/webhooks/stripe | Verificar el secret y timestamp |
push_subscription_invalid_shape | 422 | El objeto PushSubscription es inválido. | POST /v1/notifications/push/subscribe | Re-suscribir desde el SW |
push_subscription_endpoint_required | 422 | Falta endpoint. | POST /v1/notifications/push/test | Agregar |
push_subscription_endpoint_not_https | 422 | Endpoint no es HTTPS. | Push subscribe | Verificar el provider |
push_subscription_endpoint_too_long | 422 | Endpoint excede el max. | Push subscribe | (problema del provider) |
push_subscription_none_active | 422 | No tienes suscripciones push activas. | POST /v1/notifications/push/test | Suscribirse primero |
push_not_configured | 503 | El servidor no tiene VAPID configurado. | Push endpoints | Soporte |
notifications_event_unknown | 422 | Evento fuera del registry. | /v1/notifications/preferences | Revisar eventos válidos |
notifications_channel_unknown | 422 | Canal fuera del registry. | Preferences | Usar email/sms/push/in_app/telegram |
notifications_preference_invalid | 422 | Shape de la preferencia inválido. | Preferences | Revisar el body |
notifications_preference_locked | 422 | La preferencia es forzada por el server (broadcast). | Preferences | No editable por el user |
notifications_preferences_empty | 422 | El body no trae preferencias. | Preferences | Agregar al menos una |
notification_not_found | 404 | Notificación referenciada no existe. | GET /v1/notifications/{id} | Verificar el id |
invalid_notification_id | 422 | El id no es UUID. | Notifications | Corregir |
telegram_bot_not_configured | 503 | El bot de Telegram no está configurado server-side. | /v1/notifications/telegram/link | Soporte |
quiet_hours_invalid_timezone | 422 | El timezone no existe. | PUT /v1/notifications/quiet-hours | Usar IANA TZ |
quiet_hours_minutes_out_of_range | 422 | Minutos fuera de [0, 1440]. | Quiet hours | Corregir |
Rate limit
Ver Límites de uso para la convención completa de headers + buckets.
code | Status | Cuándo se dispara | Cliente debe |
|---|---|---|---|
rate_limited | 429 | Bucket genérico de rate limit excedido. | Esperar Retry-After segundos |
rate_limit_exceeded | 429 | Variante para buckets internos (e.g. login). | Esperar y reintentar |
too_many_requests | 429 | Variante semántica del 429 estándar. | Esperar |
Recursos genéricos
code | Status | Cuándo se dispara | Cliente debe |
|---|---|---|---|
not_found | 404 | El recurso pedido no existe o no es visible. | Verificar el id |
user_not_found | 404 | El user referenciado no existe. | Verificar el id |
key_not_available | 404 | La API key del user no está disponible para mostrar. | Regenerar |
cep_not_available | 404 | El CEP de la validación todavía no está disponible. | Reintentar más tarde |
image_not_available | 404 | La imagen original no está accesible. | Re-uploadear (si aplica) |
changelog_not_found | 404 | La versión solicitada no existe. | Listar con GET /v1/public/changelog |
changelog_empty | 404 | No hay changelog publicado. | Esperar publicación |
method_not_allowed | 405 | Método HTTP no soportado para ese path. | Usar uno de los permitidos |
internal_error | 500 | Falla inesperada. | Reintentar con backoff |
dispatch_failed | 500/503 | El job no pudo despacharse al worker. | Reintentar |
Admin (operación interna)
Estos códigos son emitidos por endpoints /v1/admin/* y requieren rol con permiso. La lista completa abarca panels de queues, scheduled tasks, services, migrations, seeders, tests, plans, security, i18n. Mostramos los más recurrentes; el resto sigue la convención <dominio>_<accion>_failed para 5xx y <dominio>_<accion>_invalid para 4xx.
code | Status | Dominio |
|---|---|---|
permission_denied | 403 | Cualquier admin |
admin_self_target_forbidden | 403 | Acciones sobre el propio user (force-logout, reset-2fa) |
cannot_delete_self / cannot_suspend_self / cannot_change_own_role / cannot_change_own_status / cannot_force_logout_self / cannot_reset_own_2fa | 403 | Guardrails para no dispararse en el pie |
cannot_suspend_owner / cannot_suspend_admin / only_owner_can_* | 403 | RBAC role hierarchy |
cannot_deactivate_default_plan / cannot_set_inactive_as_default / plan_cannot_delete_default | 422 | Default plan invariants |
queues_*_failed | 500 | Familia /admin/queues — overview, states, stats, pause, resume, start, stop, restart, cancel, retry, discard, dlq, batches, jobs, workers, config. Cada uno tiene su _failed y, donde aplica, su _prepare_failed (paso 1 de step-up). |
scheduled_task_*_failed | 500 | Familia /admin/scheduled-tasks — create, update, delete, detail, run_now, executions, drift, list. |
migration_* | 422 / 404 | migration_not_found, migration_file_missing, migration_filename_required, migration_filename_format, migration_target_version_format, migration_target_invalid_type, migration_mark_bulk_limit. |
seeder_* | 422 / 404 | seeder_not_found, seeder_invalid, seeder_bulk_limit, seeder_filename_format, seeder_filename_required. |
test_* | 422 / 404 | test_file_not_found, test_filename_format, test_files_limit, test_files_must_be_array, test_name_invalid, test_suite_empty, test_suite_invalid, compare_runs_must_differ. |
worker_not_found / instance_not_found / scheduled_task_not_found / dlq_entry_not_found / execution_not_found / run_not_found | 404 | Queues subresources |
worker_not_running / job_not_cancellable / run_not_cancellable | 409 | Conflict — estado terminal |
dlq_entry_unreadable | 422 | Entrada DLQ malformada |
payload_corrupt / payload_too_large / invalid_payload | 400 / 413 | Admin step-up payloads |
invalid_action / invalid_instance / invalid_service / invalid_stream / invalid_user_id / invalid_audit_id / invalid_event_id / invalid_delivery_id / invalid_otp_id / invalid_run_id / invalid_id / invalid_param / invalid_status_filter / invalid_source_filter / invalid_target_user_id / invalid_bucket_target / invalid_version / missing_version | 400 | Param validation en admin paneles |
bulk_action_invalid / bulk_slugs_required / bulk_slugs_too_many | 422 | Admin bulk endpoints |
name_already_exists | 422 | Naming collision (broadcasts, plans) |
broadcast_not_found / broadcast_empty_audience | 404 / 422 | /admin/notifications/broadcasts |
logs_read_failed / heartbeat_persist_failed / execution_failed | 503 | /admin/services |
openapi_spec_missing / openapi_spec_invalid_yaml | 500 | /admin/i18n bundle generator |
target_required / probe_label_required | 422 | /admin/banxico probes |
retry_policy_invalid | 422 | Per-plan retry caps |
permissions_must_be_object | 422 | /admin/roles editor |
plan_* | 422 / 404 | Plan CRUD: plan_name_required, plan_name_too_long, plan_slug_required, plan_slug_format_invalid, plan_duplicate_same_slug, plan_currency_invalid, plan_features_invalid_shape, plan_included_validations_negative, plan_limit_negative, plan_sort_order_negative, plan_trial_days_out_of_range, plan_tax_behavior_invalid, plan_free_cannot_have_initial_prices, plan_stripe_product_missing, plan_price_* (migrate, archive, range). |
initial_price_base_required / initial_price_metered_required | 422 | Plan create con prices iniciales |
out_of_range / invalid_type | 422 | /admin/queues worker config validator |
worker_failed / run (queues) | 500 | Genéricos |
recent_executions_failed / scheduled_tasks_drift_failed / scheduled_tasks_list_failed | 500 | Scheduler |
i18n — placeholder_parity_drift, icu_malformed, html_unsafe, brand_removed | 422 | /admin/i18n lint |
Cómo reportar un error
Cuando algo falle de manera inesperada:
- Captura
meta.request_idde la respuesta. - Anota el
codey elstatus. - Si es 5xx repetible, abre ticket de soporte adjuntando ambos.
El request_id correla con los logs server-side y permite rastrear la causa exacta sin que tu equipo tenga que reproducirlo.