error: código estable ensnake_case— úsalo en tu lógica.message: explicación legible — puede cambiar, no lo parsees.
Códigos por categoría
Autenticación y permisos
| HTTP | error | Significado |
|---|---|---|
| 401 | unauthorized | Credencial ausente o inválida |
| 401 | invalid_credentials | Email o contraseña incorrectos (login) |
| 403 | account_required | El endpoint exige credencial de cuenta |
| 403 | org_admin_required | El endpoint exige credencial de administrador |
| 403 | forbidden | Nivel de credencial no permitido |
| 403 | account_blocked | La cuenta no está activa |
| 403 | service_disabled | El servicio no está habilitado para tu cuenta (consulta GET /v1/services) |
| 403 | org_suspended | El servicio está suspendido; contacta al equipo de CBPay |
| 403 | company_only | Función solo para cuentas empresa |
| 403 | company_required | Función solo para cuentas empresa (ej. wallets segregadas) |
| 403 | human_session_required | La operación maneja llave privada (import/export de wallet segregada) y exige sesión de usuario con 2FA — las API keys no se permiten |
OTP / 2FA
Detalle y flujo completo en seguridad y 2FA.| HTTP | error | Significado |
|---|---|---|
| 403 | otp_required | La acción exige OTP: verifica un desafío y reintenta con X-OTP-Token |
| 403 | otp_invalid | Token OTP inválido, expirado o ya usado |
| 403 | session_required | Los desafíos OTP requieren sesión de usuario, no API key |
| 403 | phone_binding_cooldown | Teléfono enlazado hace menos de 24 h sin verificación |
| 401 | invalid_code | El código no coincide |
| 401 | invalid_pending_token | El token intermedio del login expiró; vuelve a iniciar sesión |
| 400 | invalid_action / invalid_channel | Acción o canal fuera de catálogo |
| 409 | phone_required | La cuenta no tiene teléfono (PATCH /v1/me) |
| 409 | otp_phone_missing | El login exige OTP y la cuenta no tiene teléfono; contacta a tu operador |
| 409 | challenge_not_pending | El desafío expiró o ya se usó; crea uno nuevo |
| 429 | too_many_attempts | Límite de envíos o verificaciones; espera unos minutos |
| 503 | otp_unavailable | Servicio de verificación no disponible (la acción queda bloqueada, nunca se salta el OTP) |
Login social (OAuth)
Detalle y flujo completo en login social.| HTTP | error | Significado |
|---|---|---|
| 400 | invalid_provider | Proveedor fuera de google/apple/microsoft/facebook |
| 400 | provider_not_configured | Tu organización no tiene ese proveedor habilitado |
| 401 | invalid_credential | La credencial del proveedor es inválida, expiró o es de otra app |
| 409 | email_conflict | Ya existe una cuenta con ese email; entra con tu método actual y vincula el proveedor |
| 409 | identity_taken | Ese proveedor ya está vinculado a otra cuenta |
| 409 | last_login_method | No puedes desvincular tu único método de acceso |
Validación (400)
error | Significado |
|---|---|
invalid_json | Body no es JSON válido o tiene campos desconocidos |
invalid_type | type debe ser person o company |
invalid_email / invalid_display_name | Campo requerido inválido |
weak_password | Contraseña menor a 8 caracteres |
invalid_role | Rol de miembro inválido |
unknown_org | Slug de organización incorrecto (usa cbpay) |
invalid_request | Faltan country/currency |
idempotency_key_required | Falta la clave de idempotencia |
beneficiary_required | Falta el beneficiario del payout |
invalid_amount | Monto no es un decimal positivo válido |
recipient_required / self_transfer | Destino de transferencia inválido |
invalid_chain / invalid_asset | Red o activo no soportado |
to_address_required | Falta dirección destino del retiro |
invalid_payload | Falta un campo requerido (ej. enabled en monitoreo AML, external_customer_id en verificaciones) |
liveness_already_completed | La prueba de vida de esa verificación ya fue superada |
invalid_event_type / weak_secret / invalid_callback_url | Suscripción de webhook inválida |
invalid_phone | Teléfono no normalizable a E.164 (contactos y to_phone) |
batch_too_large | Import de contactos con más de 1.000 entradas (pagina la subida) |
invalid_status / invalid_kyc_status / invalid_direction / reason_required / account_id_required / invalid_service / invalid_fee | Validaciones de administración |
Dinero y estado (402 / 404 / 409 / 422)
| HTTP | error | Significado |
|---|---|---|
| 402 | insufficient_funds | Saldo disponible insuficiente |
| 404 | not_found | Recurso inexistente (o de otra cuenta) |
| 404 | recipient_not_found | Destino de transferencia inexistente |
| 409 | duplicate | El recurso ya existe |
| 403 | verification_required | Tu cuenta aún no aprobó su verificación de identidad (persona=KYC, empresa=KYB); hasta entonces solo puedes fondear — pide tu link en POST /v1/me/verification/link |
| 422 | verification_required | La operación exige el verification_id de una verificación aprobada del tercero (alta banking de terceros, tarjeta designada) |
| 422 | verification_not_approved | La verificación referenciada aún no está aprobada |
| 422 | verification_kind_mismatch | El kind de la verificación no calza con el producto (KYC ⇒ persona/INDIVIDUAL, KYB ⇒ empresa/COMPANY) |
| 422 | verification_invalid | Referenciaste una verificación de onboarding propio donde se exige la de un tercero |
| 403 | company_account_required | La verificación de terceros (links/submissions KYC-KYB) es solo para cuentas empresa |
| 409 | already_verified | Pediste link de onboarding con la cuenta ya verificada |
| 409 | no_screening | Rescreen/monitoreo AML sin un screening previo |
| 409 | no_banking_customer | Operación banking sin perfil bancario creado (POST /v1/banking/customer primero) |
| 409 | banking_customer_exists | La cuenta ya tiene perfil bancario (es uno por cuenta) |
| 422 | currency_not_supported | Sin tasa FX para esa moneda |
| 422 | core_rejected | El procesador rechazó la operación |
| 422 | recipient_unavailable | La cuenta destino no puede recibir |
| 422 | recipient_ambiguous | Más de una cuenta comparte el teléfono de to_phone (usa to_account_id o to_email) |
| 422 | contact_not_linked | El contacto no tiene cuenta CBPay asociada para transferirle |
| 422 | no_saved_destination | El contacto no tiene destino guardado para ese corredor/chain |
| 422 | wallet_limit_reached | Una cuenta persona intentó crear una segunda wallet en la misma red |
| 422 | insufficient_gas | La wallet segregada no tiene gas nativo (TRX/ETH) para el fee de red; fondea la dirección y reintenta |
| 409 | idempotency_conflict | Otra creación/envío de wallet con la misma clave sigue en curso; reintenta con la misma clave |
| 409 | card_limit_reached | Una cuenta persona intentó crear una segunda tarjeta del mismo tipo |
| 409 | card_cancelled | La tarjeta ya está cancelada y no se puede modificar |
| 409 | card_not_pending | Solo tarjetas en pending_activation se pueden activar |
| 409 | cardholder_kyc_pending | El titular designado requiere documentos de identidad |
| 400 | invalid_occupation | occupation no es un código del catálogo (GET /v1/cards/catalog/occupations) |
| 400 | invalid_kind_of_business | kind_of_business no es un código del catálogo (GET /v1/cards/catalog/business-activities) |
| 400 | invalid_settlement_asset | settlement_asset no es USDT, USDC, BTC ni GOLD |
| 400 | settlement_asset_disabled | Tu organización tiene deshabilitado ese asset como origen de settlement |
| 422 | settlement_limit_exceeded | La operación supera el límite por operación de los assets volátiles (BTC/GOLD); usa USDT/USDC o divide la operación |
| 422 | settlement_daily_limit_exceeded | La cuenta superó su volumen de 24 h en assets volátiles (BTC/GOLD); usa USDT/USDC o reintenta más tarde |
| 400 | invalid_pair | Swap con la misma moneda de origen y destino |
| 400 | amount_too_small | El monto del swap no alcanza la unidad mínima de la moneda destino |
| 400 | swap_asset_disabled | Una de las monedas del swap está deshabilitada para tu organización |
Servicio (5xx)
| HTTP | error | Significado |
|---|---|---|
| 500 | internal_error | Error inesperado; reintenta con la misma clave de idempotencia |
| 502 | rates_unavailable | Tasas FX temporalmente no disponibles |
| 502 | core_unavailable | Procesador temporalmente no disponible |
| 502 | compliance_unavailable | Screening AML temporalmente no disponible |
| 503 | verifications_unavailable | Verificación de identidad temporalmente no disponible (la comisión se reembolsó) |
| 503 | org_credential_missing | Servicio en configuración; contacta al soporte de CBPay |
| 503 | withdrawals_unavailable | Retiros on-chain no habilitados para el corredor |
| 503 | pricing_unavailable | Precio de ejecución de BTC/GOLD no disponible o desactualizado; reintenta más tarde o liquida en USDT/USDC |
| 503 | export_unavailable | El export de llaves privadas de wallets segregadas no está habilitado en este entorno |
Cómo manejarlos
- 4xx de validación: corrige el request. No reintentes igual.
- 402: fondea la cuenta y reintenta (clave de idempotencia nueva solo si la operación nunca se creó).
- 5xx / timeouts: reintenta con la misma clave de idempotencia; la operación nunca se duplicará.