> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cbpayapp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Errores

> Formato de error y catálogo completo de códigos

Todos los errores comparten el mismo formato:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "error": "insufficient_funds",
  "message": "account balance is not enough for this operation"
}
```

* `error`: código estable en `snake_case` — úsalo en tu lógica.
* `message`: explicación legible — puede cambiar, no lo parsees.

<Note>
  **Mensajes de error saneados.** El `message` de un error nunca expone nombres de proveedores, detalles de infraestructura, URLs, bodies crudos del proveedor (JSON/HTML) ni configuración interna — ni en respuestas de la API, ni en webhooks, ni en los campos de estado persistidos. Los rechazos de negocio del procesador conservan su motivo accionable (por ejemplo, por qué se rechazó un documento o una cuenta); las fallas de infraestructura se reemplazan por el mensaje genérico fijo `"the payment provider could not process the request"` — reintenta esas operaciones con la misma `idempotency_key`.
</Note>

\| `payin_corridor_unsupported` | El corredor solicitado no tiene un proveedor de payin activo — vuelve a leer `GET /v1/payins/methods` |
\| 502 | `deposit_account_failed` | No se pudo aprovisionar la cuenta receptora dedicada; lee la lista antes de reintentar |

## 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) |
| 401 | `invalid_refresh_token` | Refresh token inválido, expirado, ya usado o revocado — vuelve al 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; también bloquea `POST /v1/me/verification/link` — pide al administrador que la restaure |
| 403 | `verification_rejected_requires_admin` | Compliance rechazó el KYC/KYB de la cuenta; el onboarding self-service está deshabilitado — contacta al administrador para reabrir la verificación |
| 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. [banking para terceros](/es/guides/banking)) |
| 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 |
| 403 | `member_disabled` | El usuario está bloqueado para iniciar sesión; contacta al administrador de tu organización |
| 403 | `passkey_rejected` | La verificación de la passkey falló; reintenta o entra con contraseña |
| 403 | `owner_required` | Solo el miembro owner de la cuenta puede ejecutar esa acción |

### OTP / 2FA

Detalle y flujo completo en [seguridad y 2FA](/es/security-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 y sin factor alternativo (app autenticadora o email verificado) |
| 409 | `phone_verification_required` | Verifica tu teléfono (desafío OTP) antes de activar el 2FA de login por SMS/WhatsApp |
| 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 (el `channel` del desafío debe ser `sms`, `whatsapp`, `email` o `totp`) |
| 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) |
| 409 | `otp_disabled_for_org` | Tu organización no tiene habilitado el 2FA por acción; contacta a tu operador |
| 409 | `totp_not_started` | No hay enrolamiento TOTP en curso; parte con `POST /v1/me/totp/enroll` |

### Login social (OAuth)

Detalle y flujo completo en [login social](/es/guides/social-login).

| 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 |

### Panel de administración de organización

Estos códigos provienen de **superficies de administración de organización** (el panel [CBPay Admin](https://cbpayapp.com)), no de la API a nivel cuenta documentada arriba — nunca aparecen en los endpoints `/v1/*` de cuenta.

| HTTP | `error` | Significado |
| - | - | - |
| 403 | `global_treasury_access_disabled` | La organización no tiene habilitada la visibilidad de tesorería global en su panel admin; pide a un administrador de plataforma que habilite `global_treasury_read` en los settings de la organización |
| 503 | `core_admin_credential_missing` | Un catálogo de administrador de plataforma requiere una credencial del core configurada; contacta al administrador de plataforma |
| 400 | `invalid_value` | Un setting de la organización se envió con el tipo incorrecto (ej. `global_treasury_read` debe ser booleano, no un string) |
| 409 | `approval_in_progress` | Otro administrador está aplicando este ajuste; espera a que termine antes de intentarlo de nuevo |

### Validación (400)

| `error` | Significado |
| - | - |
| `invalid_json` | Body no es JSON válido o tiene campos desconocidos |
| `invalid_lang` | `lang` debe ser `es`, `en` o `zh` |
| `invalid_settings` | Los settings de la organización no son un objeto JSON válido o no pudieron normalizarse; corrige el payload y reintenta |
| `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` |
| `invalid_purpose` | `purpose` debe ser `fondeo` o `banking`; elige un corredor Banking soportado |
| `idempotency_key_required` | Falta la clave de idempotencia |
| `reserved_idempotency_key` | La clave usa un prefijo reservado del sistema (`payin-convert:` o `checkout-swap:`, propios de las auto-conversiones) — usa otra clave |
| `beneficiary_required` | Falta el beneficiario del payout |
| `invalid_cuit` | Los payouts de Argentina (`AR`) exigen que `beneficiary.tax_id` sea un CUIT o CUIL de 11 dígitos — envía el identificador de 11 dígitos del beneficiario; consulta la [guía de payouts](/es/guides/payouts) |
| `invalid_cbu` | Los payouts de Argentina (`AR`) exigen que `beneficiary.account_number` sea un CBU o CVU de 22 dígitos; los payouts en USD aceptan solo CBU — envía un CBU/CVU válido de 22 dígitos (CBU para USD); consulta la [guía de payouts](/es/guides/payouts) |
| `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) |
| `invalid_iban` | El IBAN del payout SEPA es inválido, no es compatible con la ruta `EU` o pertenece a GB en SEPA Instant V1; corrígelo y reintenta con una clave de idempotencia nueva |
| `invalid_qr_payload` | QR de payout ilegible o no soportado (BR Code corrupto, checksum inválido o QR PIX dinámico); el `message` explica la razón exacta |
| `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`) |
| `invalid_language` | `lang` del informe PDF no es `en`, `es` ni `zh` (informe AML e informe de verificación) |
| `invalid_locale` | `PATCH /v1/me` (o el registro) envió un locale no vacío fuera de `en` / `es` / `zh`. El string vacío guarda inglés. Distinto de `invalid_language` (`lang` de informe PDF) y del `invalid_value` de org-admin en `default_locale` |
| `invalid_format` | `format` del informe de verificación no es `pdf` ni `json` |
| `batch_too_large` | Import de contactos con más de 1.000 entradas (pagina la subida) |
| `invalid_body` | No se pudo leer el body del request (subidas binarias, webhooks) |
| `invalid_image` | El body del avatar está vacío o no es una imagen soportada (PNG/JPEG/WebP, máx 512 KB) |
| `invalid_interval` | El `interval` de la suscripción debe ser `daily`, `weekly`, `monthly` o `yearly` |
| `query_required` | `GET /v1/resolve` necesita `alias=` o `qr=` |
| `same_email` | El nuevo email de login es el actual; usa `POST /v1/me/email/verify` para verificarlo |
| `invalid_status` / `invalid_kyc_status` / `invalid_direction` / `reason_required` / `account_id_required` / `invalid_service` / `invalid_fee` | Validaciones de administración |
| `invalid_settlement_hours` | `settlement_hours` solo se acepta en el servicio de comisión `payin_card` y debe ser un entero no negativo (`0` = acreditación inmediata) — ver [comisiones](/es/concepts/fees) |
| `invalid_country` | Código de país mal formado o ausente — ISO 3166-1 alpha-2 (ej. `GET /v1/aml/catalogs/cities?country=`); también un filtro de país válido pero no soportado (ej. un `country` distinto de `US` en el lookup del directorio bancario) |

### Idempotencia de payins QR

En `POST /v1/payins` con `method: "qr"`, `idempotency_key` es opcional.
Puedes enviarla en el body JSON o como `Idempotency-Key`; si envías ambas,
gana la del body. La clave admite hasta 256 caracteres y no puede contener
CR/LF. La plataforma la scopea por cuenta del lado servidor; el integrador
envía su clave sin modificarla.

* `400 invalid_idempotency_key`: la clave supera 256 caracteres o contiene
  CR/LF. Acórtala o elimina esos caracteres y reintenta el mismo request
  corregido.
* `409 idempotency_conflict`: la misma clave se usó con otro país, moneda,
  método o monto numérico. Conserva el payload original para replay o usa una
  clave nueva para crear otro cobro.

Con la misma clave y el mismo payload, la API devuelve el cobro original con
`idempotency_hit: true` y no crea otro. Los montos se comparan por valor
(`100` equivale a `100.00`); `glosa` es texto de presentación y no forma parte
de la identidad del replay. Consulta el estado vivo con
`GET /v1/payins/{payinID}`.

### Dinero y estado (402 / 404 / 409 / 422 / 429)

| HTTP | `error` | Significado |
| - | - | - |
| 402 | `insufficient_funds` | Saldo disponible insuficiente — en transferencias banking salientes el chequeo incluye la comisión del riel (`saldo >= monto + comisión`); la operación no se crea si no se cumple |
| 404 | `not_found` | Recurso inexistente (o de otra cuenta) |
| 404 | `not_available` | El documento no existe en el idioma pedido |
| 404 | `country_not_found` | Código de país ISO 3166-1 alpha-2 desconocido (catálogo de ciudades) |
| 404 | `bank_not_found` | El routing number o SWIFT/BIC no está en el directorio bancario embebido — el formulario de payout/contraparte sigue manual |
| 404 | `postal_code_not_found` | Código postal desconocido, o un país sin dataset (lookup por código postal) — los campos de dirección siguen manuales |
| 404 | `recipient_not_found` | Destino de transferencia inexistente |
| 404 | `verification_not_found` | La cuenta aún no tiene ninguna verificación enviada (`GET /v1/me/verification/report` antes del onboarding) |
| 409 | `duplicate` | El recurso ya existe |
| 409 | `receipt_not_available` | El comprobante del payout no está disponible mientras espera el screening técnico o después de un fallo antes del débito; espera un estado financiero final |
| 403 | `verification_requires_2fa` | Un link nuevo de KYC/KYB self-service exige un TOTP confirmado o un passkey registrado; un link abierto se re-sirve antes de este gate. **Solución:** enrola un factor y reintenta `POST /v1/me/verification/link` |
| 403 | `verification_required` | Tu cuenta aún no aprobó su verificación de identidad (persona=KYC, empresa=KYB); hasta entonces se bloquean la creación autenticada de payins, collect, cuentas de depósito dedicadas, cobros con tarjeta guardada y suscripciones — pide tu link en `POST /v1/me/verification/link`; los abonos pasivos y las lecturas siguen disponibles |
| 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 | `identity_locked` | Con la verificación aprobada, `display_name`, `tax_id` y `country` vienen de la identidad verificada y no se cambian por `PATCH /v1/me`; contacta al soporte |
| 409 | `no_screening` | Rescreen/monitoreo AML sin un screening previo |
| 409 | `no_banking_customer` | Operación Banking clásica sin perfil bancario genérico (`POST /v1/banking/customer` primero); EUR Banking usa un vIBAN directo |
| 400 | `banking_eur_requires_viban` | Las cuentas Banking EUR se abren como vIBAN. Solicita `purpose: "banking_eur"` desde Operar EUR en vez de crear un customer Banking genérico |
| 409 | `banking_customer_exists` | La cuenta ya tiene perfil bancario (es uno por cuenta) |
| 409 | `idempotency_conflict` | El claim del mismo perfil banking sigue pendiente o cambió el payload — conserva la misma solicitud y espera la reconciliación |
| 503 | `banking_recovery_pending` | El resultado del alta es ambiguo — operaciones debe reconciliar el claim durable antes de reintentar con una clave nueva |
| 409 | `bank_ref_conflict` | La referencia bancaria ya fue reclamada por otra operación (la respuesta la nombra con `claimed_by`/`claimed_kind` cuando pertenece a tu tenant) — corrige la evidencia en vez de reintentar con otra referencia |
| 503 | `bank_ref_recovery_pending` | Una confirmación anterior dejó la referencia en estado ambiguo y la plataforma está reconciliando el claim durable — reintenta la misma confirmación (misma referencia e idempotency key); el reintento reanuda en vez de duplicar |
| 400 | `unsupported_document_type` | El tipo de documento bancario no está admitido. Usa uno de los tipos incluidos en la allowlist de documentos bancarios. |
| 400 | `invalid_document_file` | El archivo del documento bancario está vacío, no se puede leer, está corrupto o no es PDF, JPEG ni PNG. Sube un archivo válido. |
| 422 | `currency_not_supported` | Sin tasa FX para esa moneda |
| 422 | `core_rejected` | El procesador rechazó la operación — cuando el mensaje reporta una **dirección de facturación incompleta** (o estado/región faltante), la tarjeta guardada no tiene una dirección utilizable en archivo: pide al pagador guardarla de nuevo con `save_card: true` ; el procesador también puede rechazar un payout argentino antes del débito si el alias no resuelve o si un alias USD resuelve a CVU; corrige el beneficiario y reintenta con una clave de idempotencia nueva. |
| 400 | `invalid_payload` | En `method: "card"`, `expires_at` debe ser RFC3339, quedar al menos 5 minutos hacia adelante y no superar 48 horas |
| 422 | `checkout_expiring_soon` | El link de checkout vence en menos de 5 minutos. Pide al comercio un link nuevo antes de pagar con tarjeta |
| 429 | `too_many_open_card_sessions` | La cuenta tiene 50 sesiones de tarjeta abiertas y no hay una sesión `pending` sin intentos que se pueda desalojar — completa o espera que venza una sesión existente antes de crear otra |
| 503 | `card_recovery_pending` | La plataforma está reconciliando un cobro con tarjeta ambiguo. Reintenta con la **misma** clave de idempotencia; no generes una nueva ni un segundo cobro |
| 409 | `core_charge_terminal` | El cobro de tarjeta subyacente ya tiene un estado final, por lo que la plataforma no lo volverá a acreditar — lee el estado actual del payin/cobro y no crees un segundo cobro |
| 503 | `card_reuse_unavailable` | La plataforma no pudo verificar un cobro con tarjeta abierto existente. Reintenta la solicitud original con la **misma** clave de idempotencia; no crees un segundo cobro |
| 503 | `core_reuse_unavailable` | El core no devolvió una sesión de tarjeta completa y recuperable. Reintenta la solicitud original con la **misma** clave de idempotencia |
| 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` | La cuenta ya tiene su wallet de esa combinación red+activo (depósito: todas las cuentas; [segregadas](/es/guides/segregated-wallets): personas) |
| 422 | `insufficient_gas` | La [wallet segregada](/es/guides/segregated-wallets) 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 |
| 400 | `physical_temporarily_unavailable` | La emisión de tarjetas físicas nuevas está pausada temporalmente; solicita una tarjeta digital. Las físicas existentes siguen operativas. |
| 409 | `card_limit_reached` | La cuenta persona ya tiene una tarjeta viva o una solicitud de tarjeta abierta; resuelve la solicitud existente antes de pedir otra |
| 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`) |
| 422 | `issuer_data_incomplete` | Faltan o son inválidos los campos del emisor después de ensamblar el request, el autofill de verificación y los datos KYB; corrige las claves indicadas en `message` y reintenta |
| 422 | `screening_required` | La cuenta no tiene un screening AML de verificación para derivar el PEP del titular; completa el screening y reintenta el mismo request de tarjeta |
| 422 | `cardholder_name_too_long` | `first_name` y `last_name` de la persona superan juntos el límite de 22 caracteres impresos por el emisor; acorta el nombre y reintenta |
| 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/SILVER/PLATINUM); 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/SILVER/PLATINUM); usa USDT/USDC o reintenta más tarde |
| 422 | `guard_diverged` | La referencia de precio BTC divergió más allá del price guard pre-trade; espera convergencia o divide la operación y reintenta con la misma clave de idempotencia |
| 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 |
| 409 | `already_paid` | El [link de cobro universal](/es/guides/checkout) ya se pagó por otro método |
| 409 | `checkout_not_pending` | Un intento de collect del checkout ya no acepta datos porque otro intento decidió el payin; lee el estado y no crees otro link |
| 410 | `checkout_expired` | El link de cobro universal venció sin pago |
| 422 | `method_unavailable` | El método elegido en el link de cobro no está disponible para ese link o país |
| 400 | `country_required` | Materialización fiat del link de cobro sin `?country=XX` |
| 400 | `currency_required` | El país ofrece el método del link de cobro en varias monedas; falta `?currency=YYY` |
| 422 | `country_unavailable` | Ese país no tiene métodos de pago disponibles en el link de cobro |
| 422 | `collect_otp_failed` | El rail rechazó el envío de la clave OTP del cobro pull del link |
| 422 | `collect_rejected` | El rail rechazó el cobro pull del link (OTP inválida o datos incorrectos); el link sigue pendiente |
| 422 | `provider_rejected` | Para `VE/VES/c2p`, no llames a `/collect/otp`; el pagador entrega la `claveDinamica` generada por su banco en el campo `otp` del collect. La señal interna del adapter es `collect_otp_unsupported`. |
| 422 | `settlement_asset_disabled` | El `settlement_asset` del link de cobro está deshabilitado para tu organización |
| 422 | `checkout_amount_mismatch` | La transferencia CBPay no cubre el monto vigente del link de cobro; el mensaje trae el monto actualizado |
| 503 | `checkout_recovery_pending` | La opción de pago elegida en el checkout está en reconciliación. Reintenta la misma solicitud de materialización; no crees una segunda opción de pago |
| 422 | `stored_card_revoked` | La [tarjeta guardada](/es/guides/stored-cards-subscriptions) está revocada; no acepta más cobros |
| 422 | `verification_required` | [QR Crypto POS](/es/guides/qr-pos): registra al merchant con el `verification_id` de su KYC/KYB de terceros aprobado |
| 422 | `merchant_disabled` | El merchant [QR Crypto POS](/es/guides/qr-pos) está deshabilitado; reactívalo antes de generar cobros |
| 422 | `nothing_received` | El cobro [QR Crypto POS](/es/guides/qr-pos) no ha recibido ningún pago on-chain: no hay nada que devolver |
| 422 | `refund_exceeds_received` | La devolución supera lo recibido menos lo ya devuelto del cobro [QR Crypto POS](/es/guides/qr-pos) |
| 503 | `refund_recovery_pending` | El vínculo de la devolución está temporalmente no disponible; reintenta con la misma clave de idempotencia y no origines otro retiro |
| 400 | `to_address_required` | La devolución [QR Crypto POS](/es/guides/qr-pos) (y el retiro crypto) exige la dirección destino explícita |
| 422 | `deposit_account_limit_reached` | Las cuentas persona y los corredores no soportados o existentes conservan una cuenta por corredor; una empresa admite hasta 50 cuentas banking por corredor habilitado |
| 400 | `invalid_label` | El apodo local de la cuenta supera 60 caracteres; acórtalo y reintenta |
| 409 | `alias_pending` | La CVU banking empresa AR/ARS aún no tiene alias; asígnalo con el PUT de bank-alias antes de leer saldo o movimientos |
| 422 | `purpose_not_supported` | Saldo y movimientos solo están disponibles para instrumentos con `purpose: "banking"` |
| 422 | `ambiguous_source_instrument` | Hay varias fuentes banking BO/BOB activas; envía el UUID elegido en `source_instrument_id` |
| 422 | `invalid_source_instrument` | La fuente elegida no es un instrumento banking BO/BOB activo; selecciona un destino válido |
| 409 | `intent_abandoned` | La verificación del tercero fue rechazada; crea un nuevo vínculo de verificación antes de pedir banking |
| 409 | `deposit_account_conflict` | El core devolvió un destino de depósito ya asignado a otra organización — reconcilia antes de reintentar |

\| 422 | `deposit_account_not_recoverable` | El destino pendiente no es un instrumento MX/MXN/bank\_transfer recuperable |
\| 422 | `export_rejected` | El procesador rechazó el export de la llave de la wallet segregada |
\| 422 | `stored_card_corridor_mismatch` | La [tarjeta guardada](/es/guides/stored-cards-subscriptions) pertenece a otro corredor país/moneda distinto del cobro |
\| 409 | `subscription_state` | La [suscripción](/es/guides/stored-cards-subscriptions) no está en un estado que permita esa acción (ej. pausar un plan cancelado) |
\| 409 | `email_required` | El login no tiene un email real; configura uno con `POST /v1/me/email/change` |
\| 422 | `payin_not_refundable` | El cobro no está acreditado o no tiene credencial del procesador; no se puede [devolver](/es/guides/refunds) |
\| 422 | `refund_not_supported` | Ese rail no admite [devoluciones](/es/guides/refunds) (QR, transferencia anunciada, cuenta dedicada, collect). Los cobros POS se devuelven por el riel crypto |
\| 422 | `refund_exceeds_payin` | La [devolución](/es/guides/refunds) supera lo que queda por devolver del cobro; tu saldo no se tocó |
\| 422 | `settlement_pending` | El saldo del cobro sigue programado para [settlement](/es/concepts/fees#settlement-de-payins-con-tarjeta) y no se puede [devolver](/es/guides/refunds) hasta liberarse (a `settle_at` o por liberación del admin de la organización) |
\| 400 | `invalid_amount` | El `amount` de la [devolución](/es/guides/refunds) debe ser un decimal positivo en la moneda del cobro |
\| 503 | `refund_event_pending` | El resultado de la devolución quedó guardado, pero todavía se está persistiendo su evento final. Reintenta con la **misma** clave de idempotencia; no envíes otra devolución |
\| 503 | `payin_event_pending` | El payin quedó acreditado, pero su evento final todavía se está reconciliando. Reintenta con la **misma** clave de idempotencia; no crees un nuevo payin |

### Cumplimiento (403 / 503)

| HTTP | `error` | Significado |
| - | - | - |
| 403 | `compliance_hold` | La operación fue retenida por los controles de cumplimiento de la plataforma. No es un error de tu request: contacta a soporte con el timestamp — por política no se informa la razón exacta |
| 403 | `geo_restricted` | El servicio o la operación no están disponibles para la jurisdicción de origen o de la contraparte |
| 503 | `compliance_check_unavailable` | No se pudo evaluar compliance y el payout no pudo guardarse en su cola técnica de screening; reintenta con la **misma** idempotency key. Cuando la cola está disponible, `POST /v1/payouts` devuelve `202` con `status: pending`. Los demás errores de validación y seguridad conservan sus códigos |
| 422 | `travel_rule_required` | Retiro on-chain sobre el umbral Travel Rule sin datos del beneficiario — agrega `travel_address` o `wallet_type: "self_hosted"` + `beneficiary_name` ([guía crypto](/es/guides/crypto)) |
| 422 | `travel_rule_beneficiary_required` | Falta `beneficiary_name` en un retiro sujeto a Travel Rule |
| 422 | `travel_rule_address_mismatch` | Tu `to_address` no coincide con la dirección de pago aprobada por la institución receptora — omítela o usa la del intercambio |
| 422 | `travel_rule_rejected` | La institución receptora rechazó la transferencia; verifica los datos del beneficiario |
| 422 | `travel_rule_pending` | La institución receptora aún no resuelve el intercambio; reintenta con la **misma** clave de idempotencia |
| 422 | `travel_rule_incomplete_approval` | La institución receptora aprobó sin entregar dirección de pago; contacta a soporte |
| 503 | `travel_rule_unavailable` | Intercambio Travel Rule temporalmente no disponible; reintenta con la **misma** clave de idempotencia |

### Qscore

Errores de los endpoints del buró de crédito ([guía](/es/guides/qscore)).

| HTTP | `error` | Significado |
| - | - | - |
| 400 | `purpose_required` | Falta `purpose` al crear un informe — la ley de protección de datos exige declararlo (`credit_evaluation`, `tenant_screening`, `hiring`, `supplier_onboarding` u `other`) |
| 400 | `invalid_purpose` | El `purpose` no es uno de los valores permitidos (`credit_evaluation`, `tenant_screening`, `hiring`, `supplier_onboarding`, `other`) — corrige el valor. `self_access` se rechaza aquí: tu propio informe solo va por `POST /v1/qscore/my-report` |
| 400 | `invalid_doc_id` | El `doc_id` no es válido para el `country` indicado (ej. dígito verificador de RUT incorrecto en Chile) — corrige el formato del documento |
| 400 | `invalid_subject_type` | `subject_type` no es `person`/`company` y no se pudo inferir del documento — envíalo explícito |
| 400 | `invalid_tax_id` | El `tax_id` verificado de la cuenta no es válido para su país (informe self) — contacta a soporte para corregir tus datos verificados |
| 403 | `kyc_required` | Generar tu propio informe exige un KYC/KYB aprobado — completa primero la verificación de identidad |
| 403 | `report_required` | Activar el monitoreo exige un informe comprado de ese sujeto — compra uno primero ([guía](/es/guides/qscore)); por privacidad, un sujeto inexistente recibe esta misma respuesta |
| 404 | `no_score` | El sujeto aún no tiene un score calculado — compra un informe primero |
| 404 | `pdf_not_ready` | El PDF del informe aún no está disponible — consulta el detalle hasta `status=ready` (el webhook `risk_report_ready` te avisa) |
| 409 | `no_tax_id` | La cuenta verificada no tiene `tax_id` registrado, así que el informe self no puede resolver su sujeto — completa primero tus datos verificados |
| 409 | `identity_mismatch` | El `tax_id` de la cuenta no calza con el documento de identidad verificado (informe self) — contacta a soporte; tus datos verificados deben ser consistentes |
| 422 | `unsupported_country` | Este flujo de fraude/identidad de Qscore no está disponible para el país seleccionado — usa un país compatible o contacta a soporte |
| 503 | `recovery_pending` | El informe de fraude todavía se está finalizando — consulta el informe con las mismas credenciales y reintenta el PDF cuando llegue a `ready` |
| 400 | `no_valid_items` | Todas las filas del lote fueron rechazadas (`invalid_doc_id` / `duplicate_in_batch`) y no se creó ningún lote — valida el archivo localmente (cada `doc_id` debe pasar el dígito verificador del país y ser único) y reenvía con una clave de idempotencia **nueva** |
| 400 | `too_many_items` | Un lote acepta a lo más 5.000 sujetos — parte la cartera en varios lotes, cada uno con su propia clave de idempotencia |
| 409 | `already_decided` | El link de consentimiento ya fue decidido (`granted`, `revoked` o `expired`) y no puede transicionar de nuevo — crea un link nuevo si necesitas otra autorización |
| 409 | `link_inactive` | La conexión bancaria detrás del consentimiento no está `active` (la sesión del widget expiró o el vínculo se revocó) — el titular debe reconectar desde el mismo link |
| 409 | `holder_mismatch` | El documento verificado por el banco no calza con el `doc_id` del sujeto — una cuenta de OTRO documento jamás otorga el consentimiento; verifica que creaste el link para el documento correcto |
| 409 | `seal_companies_only` | El sello público Qscore es solo para cuentas **empresa** — las cuentas persona no tienen sello ([guía](/es/guides/qscore-seal)) |
| 409 | `seal_not_eligible` | El Qscore de la cuenta no califica ahora para un sello público (banda A o B con una evaluación de no más de 90 días) — compra un informe fresco ([guía](/es/guides/qscore)) y, si la banda sigue bajo B, el sello no estará disponible hasta que el score mejore |
| 404 | `no_active_seal` | La cuenta no tiene un sello activo que revocar — activa uno primero con `POST /v1/qscore/my-seal`; un sello revocado queda revocado permanentemente (crea uno nuevo) |

### Firewall transaccional

Errores de la revisión de operaciones retenidas por el firewall transaccional ([guía](/es/guides/transaction-reviews)). La retención en sí **no es un error**: el create responde `202 Accepted` con `status: in_review` y `review_id`.

| HTTP | `error` | Significado |
| - | - | - |
| 400 | `invalid_status` | El filtro `status` no es válido — usa `in_review`, `info_requested`, `released`, `rejected` o `all` |
| 400 | `invalid_range` | Fechas `from`/`to` inválidas — formato `YYYY-MM-DD` (zona horaria de tu organización) |
| 400 | `invalid_name` | Falta el nombre del archivo en el query param `name` (máx. 200 caracteres, sin separadores de ruta) |
| 400 | `empty_file` | El body del archivo llegó vacío |
| 404 | `not_found` | La revisión o el archivo no pertenecen a tu cuenta — por diseño nunca se responde 403 |
| 409 | `not_awaiting_info` | La revisión no está esperando información (ya volvió a `in_review` o tiene decisión final) — consulta su estado actual |
| 413 | `file_too_large` | El archivo supera el límite de 50 MB — comprímelo o divídelo |
| 415 | `unsupported_file_type` | Tipo no permitido — usa PDF, PNG, JPEG, WEBP, TXT, CSV, DOC(X) o XLS(X) con el header `Content-Type` correcto |
| 422 | `file_limit_reached` | La revisión ya tiene 20 archivos |
| 503 | `firewall_unavailable` | No se pudo evaluar la política o persistir la revisión — reintenta con la **misma** clave de idempotencia |
| 503 | `storage_unavailable` | El almacenamiento de archivos no está disponible — reintenta en unos segundos |

### Eventos en tiempo real (SSE)

Códigos de [`GET /v1/events`](/es/realtime-events) y su historial.

| HTTP | `error` | Significado |
| - | - | - |
| 400 | `invalid_event_type` | Un valor de `?types=` no está en el catálogo — el `message` lista los válidos |
| 429 | `too_many_streams` | Se alcanzó el límite de streams simultáneos (por cuenta o por organización) — cierra uno antes de abrir otro |
| 429 | `rate_limited` | Demasiadas aperturas de stream desde esta IP (600 por hora) — la cuota cuenta *intentos*, no conexiones vivas: corta el bucle de reconexión y aplica backoff |
| 500 | `streaming_unsupported` | La conexión no soporta streaming (un proxy intermedio está bufferizando) — quita el buffering o usa webhooks |
| 503 | `stream_unavailable` | No se pudo abrir el stream; reintenta con backoff conservando tu `Last-Event-ID` |

### Firma de mensajes y vinculación de wallets

Códigos de la firma de mensajes con wallets (EIP-191 en EVM, TIP-191 en TRON): firma server-side con wallets segregadas (`POST /v1/segregated-wallets/{id}/signatures`, que emite un comprobante de firma verificable públicamente) y vinculación de wallets externas por desafío (`POST /v1/wallet-links/challenges` → firmas el desafío con tu wallet → `POST /v1/wallet-links/verify`).

| HTTP | `error` | Significado |
| - | - | - |
| 400 | `unsupported_chain` | La `chain` debe ser `tron` o `eth` |
| 400 | `invalid_statement` | El `statement` supera los 280 caracteres — acórtalo (ojo: el sobre firmado acepta máximo 140) |
| 409 | `proof_not_signable` | El comprobante ya no está en un estado firmable (ya firmado, expirado o cancelado) — solicita uno nuevo |
| 409 | `custody_transferred` | La custodia de la wallet fue transferida al cliente, así que la firma server-side ya no es posible — firma con tu propia clave |
| 409 | `challenge_consumed` | Este desafío ya fue usado o ya no es válido — solicita uno nuevo |
| 410 | `challenge_expired` | Este desafío expiró — solicita uno nuevo y fírmalo dentro de su ventana de validez |
| 422 | `signature_mismatch` | La firma no corresponde a la dirección del desafío — firma el mensaje exacto del desafío con la wallet que estás vinculando |
| 422 | `sign_rejected` | El firmante rechazó la solicitud — el `message` trae la razón saneada. Un `statement` de 141–280 caracteres también cae aquí (el sobre acepta máximo 140) |

### Servicio (5xx)

| HTTP | `error` | Significado |
| - | - | - |
| 500 | `internal_error` | Error inesperado; reintenta con la misma clave de idempotencia |
| 500 | `fee_config_invalid` | La configuración de comisiones de la cuenta es inválida; contacta al soporte (la operación no se ejecutó) |
| 502 | `rates_unavailable` | Tasas FX temporalmente no disponibles |
| 502 | `core_unavailable` | Procesador temporalmente no disponible |
| 502 | `core_invalid_response` | El procesador devolvió una respuesta inesperada; reintenta con la **misma** clave de idempotencia |
| 502 | `compliance_unavailable` | Screening AML temporalmente no disponible |
| 503 | `verifications_unavailable` | Verificación de identidad temporalmente no disponible |
| 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/SILVER/PLATINUM no disponible o desactualizado; reintenta más tarde o liquida en USDT/USDC |
| 503 | `channel_unavailable` | El canal de payout está temporalmente no disponible; reintenta más tarde con la **misma** clave de idempotencia |
| 503 | `webhook_processing_pending` | Un evento webhook entrante ya está siendo procesado; reintenta la misma entrega después de un backoff corto y no crees una operación nueva |
| 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á.

## Qscore fraude e Index

| HTTP | `code` | Significado y solución |
| -: | - | - |
| 400 | `invalid_period` | El período del Index no es `YYYY-Q1` a `YYYY-Q4` — usa un trimestre cerrado |
| 429 | `too_many_attempts` | La verificación pública de fraude está rate-limited — espera y reintenta |
| 502 | `generation_failed` | Falló la generación después del cargo — el fee se reembolsa; usa una nueva idempotency key |

## Ciclo de vida del corredor

| HTTP | Código | Significado y solución |
| - | - | - |
| 409 | `corridor_disabled` | El corredor seleccionado está deshabilitado administrativamente. Usa otro método habilitado o contacta al operador de la organización. |

## Banking creation recovery errors

| Code | HTTP | Meaning and action |
| - | -: | - |
| `idempotency_key_mismatch` | 400 | Header/body keys differ or the same key is reused with a different request hash. Correct the request or use a new key. |
| `idempotency_conflict` | 409 | The same key is already claimed with a conflicting payload. Do not resend with a new payload. |
| `idempotency_failed` | 409 | The claim is terminally failed. Start a new operation with a new key after reviewing the failure. |
| `claim_not_pending` | 409 | The requested human recovery is no longer pending. Read the claim and use the terminal result. |
| `claim_customer_owned_elsewhere` | 409 | The provider customer is already linked to another account. Stop and investigate ownership. |
| `banking_customer_creation_in_progress` | 409 | A lock is held by another banking operation. Retry the read/recovery flow later. |
| `banking_recovery_pending` | 503 | The result or refund remains ambiguous. Do not originate a second provider request; use the recovery endpoint. |
| `banking_invalid_response` | 422 | Provider account ownership did not match the claimed customer's `ProviderID`. |

## Errores de recuperación de creaciones banking

| Código | HTTP | Significado y acción |
| - | -: | - |
| `claim_identity_mismatch` | 422 | La identidad del cliente del proveedor no coincide con la solicitud banking original. Detén la recuperación y revisa el claim. |
| `claim_not_pending` | 409 | El claim ya no está pendiente. Lee el resultado terminal en vez de repetir la recuperación. |
| `claim_customer_owned_elsewhere` | 409 | El cliente del proveedor pertenece a otra cuenta. Detén el proceso y revisa la titularidad. |
| `banking_recovery_pending` | 503 | El resultado del proveedor sigue ambiguo. Usa la ruta administrativa; no origines otra creación. |

## Errores de Banking EUR y vIBAN

| Code | Meaning and action | | |
| - | - | - | - |
| `banking_customer_incomplete` | The banking customer lacks the provider identity required for the operation. Complete the customer/KYC/KYB data, then retry with the same idempotency key. | | |
| `registrant_incomplete` | 422 | Solicitud de vIBAN | Al perfil KYC/KYB verificado le faltan campos obligatorios del registrant; completa los datos de documento o incorporación indicados y reintenta la misma solicitud. |
| `provider_rejected` | The upstream banking operation was rejected. Do not create a new money operation automatically; inspect the resource and follow its retry/reconciliation state. | | |
| `customer_id_required` | A banking operation requires an explicit customer identifier. Provide the customer ID from the account-owned resource. | | |
| `iban_required` | The operation requires the IBAN associated with the owned virtual account. | | |
| `operation_in_progress` | The same banking operation is still being reconciled. Poll the resource and retry with the original idempotency key. | | |
| `ownership_required` | The resource belongs to another organization or account. Use only an owned resource; never guess or substitute an ID. | | |

## Códigos de mensajería masiva

Estos códigos provienen de los endpoints de administración de organización.
Se incluyen aquí para que los SDK compartidos mantengan un solo catálogo.

| HTTP | `error` | Causa | Solución |
| -: | - | - | - |
| 400 | `invalid_channels` | No se seleccionó un canal válido | Envía `email`, `notification` o ambos |
| 400 | `invalid_content` | Subject/body vacío o demasiado largo | Usa subject de 1–160 runas y body de 1–4000 runas |
| 400 | `invalid_reason` | Falta la razón de rechazo o es demasiado larga | Envía una razón auditada de 1–500 runas |
| 400 | `invalid_audience` | Modo, filtros o IDs inválidos | Usa `filters` o 1–5000 IDs de cuentas de la organización |
| 403 | `named_admin_required` | El actor no es un administrador nombrado | Usa un administrador nombrado `god` o `super_admin` |
| 403 | `partner_forbidden` | Un bank partner intentó usar un broadcast de organización | Usa un administrador permitido |
| 409 | `empty_audience` | Ninguna cuenta coincidió al enviar/aprobar | Corrige la audiencia y vuelve a enviar |
| 409 | `audience_too_large` | Los filtros coinciden con más de 20000 cuentas | Acota los filtros y vuelve a enviar |
| 409 | `second_approver_required` | El creador intentó aprobar su campaña | Pide aprobación a otro God nombrado |

## Errores de Banking EUR y wallets de empresa

| HTTP | Código | Qué hacer |
| -: | - | - |
| 409 | `virtual_iban_limit_reached` | La persona ya tiene un IBAN virtual para ese propósito. Reúsalo; el límite no aplica así a empresas. |
| 409 | `no_fee_charged` | Este IBAN virtual no tuvo cobro (el primer fondeo es gratis). Nada que devolver. |
| 409 | `wallet_not_ready` | La wallet sigue `pending`/`provisioning`. Consulta el detalle hasta que quede `active`. |
| 422 | `virtual_iban_limit_reached` | Se alcanzó el límite persona-por-propósito al reservar. No crees una segunda clave. |
| 422 | `registrant_incomplete` | Completa los datos KYC/KYB o de incorporación indicados en `message` y repite la misma solicitud durable. |
| 502 | `wallet_reservation_failed` | La reserva no devolvió una referencia utilizable. Concilia antes de reintentar. |
| 502 | `wallet_reservation_status_failed` | Falló la lectura de estado. Conserva `order_reference` o `client_order`. |
| 502 | `wallet_read_failed` | No se pudo leer la respuesta live o el monto EUR. Reintenta la lectura después de revisar el estado. |
| 502 | `wallet_statement_failed` | No se pudo leer el statement. Usa una ventana válida de días completos y reintenta la lectura. |

## Política de integraciones de cuenta

| HTTP | `error` | Significado y solución |
| -: | - | - |
| 400 | `email_domain_blocked` | El dominio está bloqueado para registro o cambio de email; usa otro dominio permitido. |
| 400 | `invalid_secret` | El secreto del webhook debe tener entre 16 y 256 caracteres. |
| 403 | `session_required` | Crear API keys y webhooks de cuenta exige JWT humano, no API key. |
| 403 | `integration_company_only` | Una persona necesita grant vigente; una empresa necesita KYB aprobado. |
| 403 | `integration_kyb_required` | Aprueba el KYC/KYB antes de crear o reactivar la integración. |
| 403 | `account_blocked` | La cuenta no está activa; solicita restaurarla. |
| 403 | `otp_required` | Completa el OTP `api_key_create` o `webhook_manage` y reintenta. |
| 403 | `integration_forbidden` | La cuenta no puede usar integraciones; consulta `GET /v1/integration-status` y resuelve el motivo. |

## Disputas y fraude

| HTTP | Código | Resolución |
| -: | - | - |
| 400 | `idempotency_key_required` | Envía la clave en body o header para una acción financiera. |
| 400 | `invalid_payload` | Corrige monto, fecha, UUID, archivo multipart o campo requerido. |
| 404 | `not_found` | El caso, importación, fila, evidencia o cuenta está fuera del scope. |
| 409 | `case_already_open` | Revisa el caso abierto del payin; no abras otra clave. |
| 409 | `payin_not_credited` | El payin no está `credited`, por lo que no hay saldo que retener; espera la acreditación antes de abrir un caso o vincular una fila importada. |
| 409 | `second_approver_required` | Otro admin nombrado debe cerrar el caso. |
| 409 | `invalid_state` | La transición solicitada no aplica al estado actual. |
| 409 | `bind_failed` | La fila ya no está unmatched o el payin no puede asociarse. |
| 409 | `import_in_progress` | Una importación con esta clave sigue en curso; espera y repite con la misma clave. |
| 503 | `pack_unavailable` | Reintenta cuando se recupere storage/render. |
| 503 | `storage_unavailable` | Recupera storage privado y reintenta la evidencia. |

## Errores de cuenta de fondeo SEPA

Estos errores se devuelven antes de debitar o despachar un payout cliente o
una operación Banking EUR:

| HTTP | `error` | Significado y solución |
| -: | - | - |
| 400 | `invalid_request` | `source_virtual_iban_id` debe ser un UUID válido. |
| 404 | `not_found` | El IBAN virtual explícito no pertenece a la cuenta. |
| 422 | `funding_account_required` | No existe un IBAN virtual asignado y activo para el propósito requerido. Usa una fuente activa para este flujo. |
| 422 | `ambiguous_source_viban` | Hay más de una cuenta EUR activa. Envía el `source_virtual_iban_id` elegido. |
| 503 | `funding_account_unavailable` | La consulta del origen está temporalmente no disponible. Reintenta con la misma clave de idempotencia; no se debitó ni despachó nada. |

### Errores de límites money-out

| HTTP | `error` | Significado |
| - | - | - |
| 422 | `amount_below_minimum` | El monto está bajo el mínimo efectivo; consulta `GET /v1/org/txn-firewall/limits/effective`. |
| 422 | `amount_above_maximum` | El monto supera el máximo efectivo; revisa la política antes de reintentar. |
| 422 | `velocity_limit_exceeded` | Una transferencia interna superó un tope móvil de monto o cantidad. Payouts y retiros entran a review con `reason=velocity_limit`. |
| 503 | `limits_unavailable` | No se pudieron evaluar los límites antes del débito; reintenta con la misma clave de idempotencia. |

## `dispute_fiat_unsupported`

No se puede abrir una controversia para un payin acreditado en BOB, MXN o ARS retenidos. Usa un crédito USDT soportado o espera soporte para controversias fiat.

## Aliases bancarios argentinos y alias del perfil

| HTTP | `error` | Significado / acción |
| - | - | - |
| 400 | `missing_alias` | La creación de una cuenta de depósito AR/ARS en la plataforma debe incluir el `bank_alias` elegido por el caller |
| 400 | `invalid_alias` | `bank_alias` debe tener 6–20 caracteres ASCII de `[A-Za-z0-9.-]` y al menos una letra |
| 400 | `invalid_account` | El core no pudo resolver el número de cuenta de depósito enviado |
| 400 | `invalid_request` | El valor del body del alias de perfil no coincide con un candidato derivado del nombre verificado, o se envió `bank_alias` a un corredor no compatible |
| 409 | `alias_already_set` | El alias del perfil es permanente y ya fue fijado |
| 409 | `alias_taken` | **Alias del perfil:** todos los candidatos derivados del nombre verificado están ocupados; contacta soporte. No se resuelve inventando un valor |
| 422 | `alias_taken` | **Alias bancario:** el sistema bancario argentino ya usa ese valor; elige otro |
| 422 | `alias_unsupported` | El riel de la cuenta de depósito no admite aliases bancarios |

## Ceremonia del Contrato Marco Grupo CB

Estos errores aplican a `POST /v1/me/contracts/envelopes/{envelopeID}/sign` y
a los recursos de sobres. El mensaje puede generalizarse por privacidad; el
código es el contrato estable para la integración.

| HTTP | Código | Causa y solución |
| -: | - | - |
| 400 | `idempotency_key_required` | La emisión admin exige key en body o `Idempotency-Key`; repite con una key estable. |
| 400 | `invalid_idempotency_key` | La key es inválida o demasiado larga; usa una key más corta. |
| 400 | `invalid_reason` | Void exige un motivo no vacío; envíalo en el body. |
| 400 | `invalid_content_type` | La biblioteca acepta `image/png`, `image/jpeg` o `image/jpg`. |
| 400 | `empty_file` | La carga no tiene bytes; envía el archivo original. |
| 400 | `invalid_image` | El archivo no es PNG/JPG válido; expórtalo nuevamente. |
| 403 | `contract_counsel_required` | La emisión en inglés exige aprobación de counsel; apruébala o usa español. |
| 403 | `human_session_required` | Firmar exige sesión humana; las API keys no pueden firmar. |
| 403 | `account_blocked` | La cuenta no está activa; pide al admin restaurarla. |
| 401 | `invalid_otp` | OTP inválido, vencido o ya usado; envía uno nuevo en `X-OTP-Token`. |
| 409 | `contract_invalid_state` | El sobre ya está completado o voided; no repitas la operación. |
| 422 | `contract_template_superseded` | El sobre se emitió con una versión anterior de la plantilla; pide reemisión antes de firmar. |
| 413 | `file_too_large` | La carga raw supera 5 MiB o la imagen normalizada supera 8 MiB. |
| 422 | `invalid_lang` | Usa solo `es` o `en`; los demás valores se rechazan. |
| 422 | `contract_account_ineligible` | El destino debe ser una empresa activa con KYB aprobado. |
| 422 | `contract_unfillable` | Revisa el array `missing` de la respuesta: puede listar datos de identidad, aprobación de counsel o `pricing:<flag>` cuando un servicio habilitado no tiene una fila de precio efectiva. |
| 422 | `invalid_consent` | El body debe llevar el booleano JSON `"consent": true`. |
| 422 | `invalid_signer` | Nombre/cargo vacíos o demasiado largos; usa owner/operator humano. |
| 422 | `invalid_signature_image` | `signature_png` no es un PNG válido o no está entintado, supera el límite de tamaño codificado/decodificado o no alcanza las dimensiones mínimas; envía una imagen válida y reintenta con un OTP nuevo. |
| 502 | `storage_failed` | Falló el storage privado; reconcilia el sobre antes de reintentar. |
| 502 | `email_failed` | Falló el email posterior a la firma; el sobre completado sigue siendo válido. |
| 503 | `storage_unavailable` | Storage privado no disponible; reintenta cuando se recupere. |

## Handoff de ambiente

| HTTP | `error` | Significado y solución |
| - | - | - |
| 401 | `invalid_handoff` | Falta el token de handoff, expiró, ya fue consumido, o se emitió para el otro ambiente. Emite un handoff fresco y canjéalo en el mismo ambiente. |
| 401 | `handoff_replayed` | Este token ya fue consumido (un solo uso). Emite un handoff fresco. |

## 2FA y passkeys

| HTTP | `error` | Significado y solución |
| - | - | - |
| 403 | `channel_locked_by_org` | La política de la org solo permite TOTP y bloquea el registro de passkeys en esta cuenta; también rechaza desafíos cuyo `channel` explícito es más débil que el piso de la org. Pide a un org admin que cambie la política. |
| 409 | `passkey_exists` | El credential ID ya está registrado en esta cuenta. Inicia sesión con la passkey existente. |
| 409 | `totp_already_enrolled` | El TOTP ya está activo en esta cuenta. Autentícate con el secret existente. |

## Documentos de checkout y de payouts

| HTTP | `error` | Significado y solución |
| - | - | - |
| 409 | `materializing` | Otra materialización de este link de checkout está en curso. Reintenta cuando termine. |
| 400 | `invalid_file_name` | El nombre de archivo está vacío, es muy largo o trae caracteres de path (`/`, `\`, `..`). Envía un nombre de archivo plano. |
| 415 | `unsupported_media_type` | El Content-Type no es un tipo de documento aceptado. Envía PDF, JPEG o PNG. |

## `destination_recently_rejected`

| HTTP | Código | Significado | Qué hacer |
| -: | - | - | - |
| 422 | `destination_recently_rejected` | El banco rechazó recientemente el mismo destino normalizado para esta cuenta. No se crea el payout, no se debita saldo y la clave de idempotencia no se consume. | Primero repite la solicitud original con la misma clave. Si quieres reintentar de forma deliberada, usa una clave de idempotencia nueva y envía `options.destination_retry_ack` con el ID del payout rechazado más reciente. |

El acuse solo es válido para el rechazo más reciente de ese destino. Reutilizar
la misma clave con `options` diferentes devuelve `idempotency_conflict`
(`409`). Los demás métodos y los destinos sin un rechazo reciente siguen su
flujo normal de validación y despacho.

## Errores de endurecimiento antiataques de autenticación

| HTTP | `error` | Significado y solución |
| -: | - | - |
| 400 | `invalid_code` | El código de registro o reset no tiene 4–10 dígitos. Envíalo con la forma esperada. |
| 401 | `invalid_code` | La identidad o el código de confirmación/reset es inválido o vencido. Usa el último código; la respuesta es deliberadamente uniforme. |
| 401 | `invalid_credentials` | Falló el login, la cuenta está pendiente de verificación o quedó bloqueada temporalmente después de 8 fallos en 15 minutos. Espera o confirma el registro. |
| 502 | `otp_check_failed` | No se pudo consultar el servicio de verificación. Mantén el mismo flujo y reintenta después; no crees otra cuenta ni otra solicitud. |

`POST /v1/auth/password/forgot` siempre responde `200` para evitar enumeración. Silencia envíos reales después de 5 por cuenta por hora; `POST /v1/auth/password/reset` permite 8 intentos de código por cuenta cada 15 minutos.

## Links de recuperación de members

| HTTP | `error` | Causa y acción |
| -: | - | - |
| 400 | `invalid_token` | El `token` no es el valor base64url de 43 caracteres del email de recuperación. Copia lo que sigue a `?token=` sin modificarlo. |
| 400 | `weak_password` | La nueva contraseña no cumple la política del servidor. Elige una más fuerte y usa el mismo link mientras siga vigente. |
| 401 | `invalid_token` | El link es inválido, venció, ya se usó o no corresponde a la org/member. Pide un link nuevo al admin de la organización. La respuesta es uniforme a propósito. |
| 502 | `recovery_send_failed` | No se pudo entregar el email de recuperación iniciado por el admin. Pide al admin revisar la configuración de email y disparar un link nuevo. |
| 429 | `too_many_attempts` | La IP superó el throttle de verificación. Espera un momento y reintenta; no automatices envíos repetidos. |

## Validación de payouts US/USD

Estos errores se devuelven antes del débito en payouts `ach`, `wire` y `swift`:

| HTTP | `error` | Significado y solución |
| -: | - | - |
| 400 | `invalid_beneficiary` | Falta un campo requerido o es inválido; revisa `message`, por ejemplo `beneficiary.account_type`. |
| 400 | `invalid_amount` | El monto no es un decimal positivo válido; corrígelo y crea una solicitud nueva. |
| 422 | `amount_below_minimum` | Un `wire` o `swift` está bajo USD 25; usa al menos `25.00`. |
| 422 | `prohibited_country` | El país del beneficiario o banco está en el Anexo B (`CU`, `IR`, `KP`, `SY`); usa un destino permitido. |
| 400 | `purpose_required` | `options.purpose` es obligatorio en todo payout bancario US/USD; elige un valor del catálogo. |
| 400 | `invalid_options` | Hay una opción desconocida o enviaste `purpose_detail` con un propósito distinto de `Other`; corrige `options`. |
| 400 | `supporting_document_required` | Sube y envía la factura o recibo antes de crear el payout bancario USD. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.