Core concepts

Errors

Standard HTTP status codes plus a typed JSON envelope.

Error envelope

Every error response has the same shape: the error.code field is the source of truth — the messagemay change, the codes won't. Quote the requestId when contacting support.

json
{
  "error": {
    "code": "card_declined",
    "type": "invalid_request_error",
    "message": "The card was declined by the issuer.",
    "param": "source.number",
    "decline_code": "insufficient_funds",
    "requestId": "req_3kf91… "
  }
}

HTTP statuses

Mapping of the HTTP codes the API returns. The response status is consistent with the error type (4xx = client problem, 5xx = our problem).

StatusMeaningDescription
200OKEverything worked.
201CreatedA new resource was created.
400Bad requestRequest is malformed or missing required parameters.
401UnauthorizedMissing or invalid API key.
402Payment failedProvider declined the charge.
403ForbiddenKey kind is not allowed for this endpoint.
404Not foundResource does not exist.
409ConflictIdempotency-Key reused with a different request body.
415Unsupported media typeContent-Type must be application/json.
422Unprocessable entityNo provider in the cascade can accept this charge.
429Too many requestsRate limit exceeded — back off and retry with jitter.
500Server errorInternal failure. Safe to retry idempotently.
503Service unavailableTemporary maintenance / upstream outage.

Error codes

Exhaustive list of every error.code that can appear in a /v1 response, grouped by type. These codes are stable — they're versioned with the API. If you get one that isn't here, open an issue.

Authentication errors · 6
CodeHTTPWhen it's returned
missing_credentials401El request no llegó con header Authorization (o lo enviaste vacío).
invalid_api_key401La key no existe, está rotada, o pertenece a otro entorno (sandbox vs production).
invalid_token401JWT corrupto, alterado o firmado con otro secreto.
token_expired401El JWT expiró (vida útil 1h). Reinicia el flow llamando POST /v1/auth/token.
production_key_refused400El endpoint sandbox rechaza credenciales live por seguridad.
shop_required401El token no carga `shopId`. Probablemente vino de credenciales merchant-level legacy.
Permission errors · 4
CodeHTTPWhen it's returned
key_kind_not_allowed403Estás usando una `pk_*` en una operación que requiere `sk_*`.
forbidden403El recurso pertenece a otro merchant/shop o tu rol no lo permite.
permission_denied403Tu rol en el shop no incluye este permiso. Pedile a un usuario con 'Gestionar usuarios' que te lo otorgue.
method_not_enabled_for_shop403El método elegido en el checkout NO está en el set de métodos habilitados del shop (shop.paymentMethodIds). Cada shop solo puede cobrar con los rieles que tiene habilitados — la lista de GET /api/v1/payment-methods es la autoridad. Si el shop no tiene ningún método habilitado, NO puede iniciar cobros por checkout. Habilitá el rail en /admin/shops → el shop → Payment methods & fees.
Invalid request errors · 26
CodeHTTPWhen it's returned
content_type_required415Mandaste form-urlencoded, multipart u otro tipo. Setea `Content-Type: application/json`.
invalid_json400El cuerpo no se pudo parsear. Revisa comas y comillas.
invalid_request400Uno o más campos no cumplen el schema. Mira el array `details.issues` para detalles.
missing_field400Falta un campo obligatorio. El campo se indica en `param`.
unsupported_method400El `method` solicitado no está en el allow-list del shop. Usa GET /v1/payment-methods para ver los disponibles.
unsupported_country400El método elegido no aplica al país del usuario.
unsupported_currency400Revisa `currencyLimits` del método para ver las monedas válidas.
amount_invalid400El monto vino vacío, en cero, negativo o no numérico.
amount_below_minimum400El monto es menor al mínimo que el provider acepta para este método.
amount_above_maximum400El monto excede el máximo que el provider acepta para este método.
idempotency_conflict409Reusaste una `Idempotency-Key` con un body distinto. Genera una nueva o repite el body original.
card_declined402El emisor rechazó el cobro. `decline_code` indica la razón específica.
insufficient_funds402Sub-caso de card_declined.
fraud_blocked402El motor de riesgo (nuestro o del provider) marcó la operación.
kyc_required402El monto/método requiere verificación. Inicia con POST /v1/kyc/init.
three_ds_required402Redirige al usuario al `redirectUrl` que devuelve el response.
three_ds_failed402El usuario no completó el challenge o falló. La transacción queda en estado failed.
expired_card402Sub-caso de card_declined.
missing_required_fields422El método de pago necesita uno o más datos del comprador (documento, teléfono, etc.) que faltan o vienen mal formados. Revisa `details.missingFields` (o `details.malformedFields`): cada uno trae `key`, `type`, `label` y la regla de validación. Recolecta esos campos y reintenta el mismo cobro (puedes reusar la misma Idempotency-Key — los errores no se cachean).
amount_out_of_limits422The local-currency amount (after FX conversion) is below the minimum OR above the maximum the upstream rail accepts. Each method advertises its bounds in GET /api/v1/payment-methods → `currencyLimits[]`. The error response includes the exact limit (`details.limit.min`, `details.limit.max`) and which bound was violated (`details.boundViolated`). Adjust the `amount` (USD) so the converted local-currency value sits within the range, or pick a different method.
balance_insufficient422Aplicado al solicitar withdrawals/refunds/payouts que exceden el available.
currency_not_funded422El payout se debita del saldo de payout en la divisa del método. Si pedís un payout en MXN sin saldo MXN, hacé un swap USD→MXN primero. Verificá GET /api/v1/me/payout/balance.
payout_method_unavailable422El `methodId` no existe en el catálogo. Listá los métodos en GET /api/v1/me/payout/methods.
payout_not_found404No existe un payout con ese id para tu cuenta.
settlement_already_paid409No se puede pagar dos veces el mismo settlement.
claim_already_resolved409Los claims terminales no aceptan más evidencia ni cambios de estado.
Rate limit errors · 1
CodeHTTPWhen it's returned
rate_limited429Excediste el rate limit del tier. El header `Retry-After` indica cuánto esperar.
API / server errors · 14
CodeHTTPWhen it's returned
shop_not_found404El shop fue eliminado o nunca existió. Verifica el id en /admin/shops.
merchant_not_found404El merchant referenciado no existe.
transaction_not_found404El `id` de transacción no existe o pertenece a otro merchant.
checkout_session_not_found404El token del checkout no existe o expiró (TTL 24h). Crea una nueva sesión con POST /api/v1/checkout/sessions.
checkout_session_already_completed409La sesión ya fue usada para crear una transacción. Para reintentar con otro método, crea una nueva sesión.
settlement_not_found404El settlement no existe o ya fue archivado.
claim_not_found404El claim no existe o pertenece a otro merchant.
wallet_not_found404La payout wallet no existe en este merchant.
withdrawal_not_found404El retiro no existe o pertenece a otro merchant.
webhook_not_found404El webhook subscription no existe (o pertenece a otro merchant). Verifica el id contra GET /api/v1/webhooks.
webhook_delivery_not_found404El delivery id no existe bajo este webhook subscription. Lista las entregas con GET /api/v1/webhooks/{id}/deliveries.
cascade_exhausted422Todos los providers configurados rechazaron o están deshabilitados. Revisa `attempts` en el response.
service_unavailable503Mantenimiento o un upstream caído. Reintenta con backoff exponencial.
internal_error500Bug del servidor o un panic no controlado. Manda el `requestId` a soporte.
How to handle errors in your integration: we recommend a switch on error.code to decide the action (retry, show a message to the user, escalate to support). Never parse the message: that text can change between versions.