Core
Errors
Standard HTTP status codes plus a typed JSON envelope.
Error envelope
Toda respuesta de error tiene la misma forma: el campo error.code es la fuente de verdad — los message pueden cambiar, los códigos no. Usa el requestId al contactar soporte.
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
Mapeo de códigos HTTP que devuelve la API. El status de la respuesta es coherente con el tipo de error (4xx = problema del cliente, 5xx = problema nuestro).
| Status | Meaning | Description |
|---|---|---|
200 | OK | Everything worked. |
201 | Created | A new resource was created. |
400 | Bad request | Request is malformed or missing required parameters. |
401 | Unauthorized | Missing or invalid API key. |
402 | Payment failed | Provider declined the charge. |
403 | Forbidden | Key kind is not allowed for this endpoint. |
404 | Not found | Resource does not exist. |
409 | Conflict | Idempotency-Key reused with a different request body. |
415 | Unsupported media type | Content-Type must be application/json. |
422 | Unprocessable entity | No provider in the cascade can accept this charge. |
429 | Too many requests | Rate limit exceeded — back off and retry with jitter. |
500 | Server error | Internal failure. Safe to retry idempotently. |
503 | Service unavailable | Temporary maintenance / upstream outage. |
Error codes
Lista exhaustiva de cada error.code que puede aparecer en una respuesta /v1, agrupado por type. Estos códigos son estables — están versionados con la API. Si recibes uno que no está aquí, abre un issue.
Authentication errors · 6
| Code | HTTP | Cuándo se devuelve |
|---|---|---|
missing_credentials | 401 | El request no llegó con header Authorization (o lo enviaste vacío). |
invalid_api_key | 401 | La key no existe, está rotada, o pertenece a otro entorno (sandbox vs production). |
invalid_token | 401 | JWT corrupto, alterado o firmado con otro secreto. |
token_expired | 401 | El JWT expiró (vida útil 1h). Reinicia el flow llamando POST /v1/auth/token. |
production_key_refused | 400 | El endpoint sandbox rechaza credenciales live por seguridad. |
shop_required | 401 | El token no carga `shopId`. Probablemente vino de credenciales merchant-level legacy. |
Permission errors · 2
| Code | HTTP | Cuándo se devuelve |
|---|---|---|
key_kind_not_allowed | 403 | Estás usando una `pk_*` en una operación que requiere `sk_*`. |
forbidden | 403 | El recurso pertenece a otro merchant/shop o tu rol no lo permite. |
Invalid request errors · 21
| Code | HTTP | Cuándo se devuelve |
|---|---|---|
content_type_required | 415 | Mandaste form-urlencoded, multipart u otro tipo. Setea `Content-Type: application/json`. |
invalid_json | 400 | El cuerpo no se pudo parsear. Revisa comas y comillas. |
invalid_request | 400 | Uno o más campos no cumplen el schema. Mira el array `details.issues` para detalles. |
missing_field | 400 | Falta un campo obligatorio. El campo se indica en `param`. |
unsupported_method | 400 | El `method` solicitado no está en el allow-list del shop. Usa GET /v1/payment-methods para ver los disponibles. |
unsupported_country | 400 | El método elegido no aplica al país del usuario. |
unsupported_currency | 400 | Revisa `currencyLimits` del método para ver las monedas válidas. |
amount_invalid | 400 | El monto vino vacío, en cero, negativo o no numérico. |
amount_below_minimum | 400 | El monto es menor al mínimo que el provider acepta para este método. |
amount_above_maximum | 400 | El monto excede el máximo que el provider acepta para este método. |
idempotency_conflict | 409 | Reusaste una `Idempotency-Key` con un body distinto. Genera una nueva o repite el body original. |
card_declined | 402 | El emisor rechazó el cobro. `decline_code` indica la razón específica. |
insufficient_funds | 402 | Sub-caso de card_declined. |
fraud_blocked | 402 | El motor de riesgo (nuestro o del provider) marcó la operación. |
kyc_required | 402 | El monto/método requiere verificación. Inicia con POST /v1/kyc/init. |
three_ds_required | 402 | Redirige al usuario al `redirectUrl` que devuelve el response. |
three_ds_failed | 402 | El usuario no completó el challenge o falló. La transacción queda en estado failed. |
expired_card | 402 | Sub-caso de card_declined. |
balance_insufficient | 422 | Aplicado al solicitar withdrawals/refunds que exceden el available. |
settlement_already_paid | 409 | No se puede pagar dos veces el mismo settlement. |
claim_already_resolved | 409 | Los claims terminales no aceptan más evidencia ni cambios de estado. |
Rate limit errors · 1
| Code | HTTP | Cuándo se devuelve |
|---|---|---|
rate_limited | 429 | Excediste el rate limit del tier. El header `Retry-After` indica cuánto esperar. |
API / server errors · 11
| Code | HTTP | Cuándo se devuelve |
|---|---|---|
shop_not_found | 404 | El shop fue eliminado o nunca existió. Verifica el id en /admin/shops. |
merchant_not_found | 404 | El merchant referenciado no existe. |
transaction_not_found | 404 | El `id` de transacción no existe o pertenece a otro merchant. |
settlement_not_found | 404 | El settlement no existe o ya fue archivado. |
claim_not_found | 404 | El claim no existe o pertenece a otro merchant. |
wallet_not_found | 404 | La payout wallet no existe en este merchant. |
webhook_not_found | 404 | El webhook subscription no existe (o pertenece a otro merchant). Verifica el id contra GET /api/v1/webhooks. |
webhook_delivery_not_found | 404 | El delivery id no existe bajo este webhook subscription. Lista las entregas con GET /api/v1/webhooks/{id}/deliveries. |
cascade_exhausted | 422 | Todos los providers configurados rechazaron o están deshabilitados. Revisa `attempts` en el response. |
service_unavailable | 503 | Mantenimiento o un upstream caído. Reintenta con backoff exponencial. |
internal_error | 500 | Bug del servidor o un panic no controlado. Manda el `requestId` a soporte. |
Cómo manejar errores en tu integración: recomendamos hacer
switch sobre error.code para decidir la acción (retry, mostrar mensaje al usuario, escalar a soporte). Nunca hagas parsing del message: ese texto puede cambiar entre versiones.