Errors
CashXChain uses standard HTTP status codes and a consistent structured JSON error response across all endpoints.
Error format
All errors return the same top-level shape:
{
"success": false,
"error": "insufficient_scope",
"message": "api key missing required scope: payments:write",
"details": "scope: payments:write"
}
| Field | Type | Description |
|---|---|---|
success | false | Always false on error responses. |
error | string | Stable machine-readable code. Safe to match programmatically. |
message | string | Human-readable description. Do not parse — may change. |
details | string | null | Optional extra context (e.g. field name, resource ID). |
HTTP status codes
| Status | Meaning |
|---|---|
400 Bad Request | Request body or parameters are invalid. |
401 Unauthorized | Authentication failed — missing, invalid, or expired credential. |
403 Forbidden | Credential valid but insufficient permissions for this operation. |
404 Not Found | Resource does not exist or is not accessible to the caller. |
409 Conflict | Duplicate resource or invalid state transition. |
422 Unprocessable Entity | Request is valid JSON but semantically cannot be processed. |
429 Too Many Requests | Rate limit exceeded. |
500 Internal Server Error | Unexpected server error. |
503 Service Unavailable | Backend or downstream partner temporarily unavailable. |
Authentication and authorization errors
| Code | HTTP | Meaning |
|---|---|---|
authentication_required | 401 | No Authorization header and route requires authentication. |
invalid_api_key | 401 | Key not found, revoked, or hash does not match. Identical response for non-existent and revoked keys to prevent enumeration. |
token_expired | 401 | JWT exp claim has passed. |
invalid_token | 401 | JWT signature invalid, malformed, or algorithm mismatch. |
insufficient_scope | 403 | Restricted key does not have the required scope for this operation. |
publishable_key_write_denied | 403 | Publishable key (pk_) attempted a write method. |
dashboard_access_denied | 403 | Regular API key (sk_/pk_/rk_) attempted a dashboard route that requires JWT or master key. |
environment_mismatch | 403 | Key prefix environment (sandbox/live) does not match the API base URL. |
Resource errors
| Code | HTTP | Meaning |
|---|---|---|
resource_not_found | 404 | Resource does not exist or is not accessible to the calling account. |
payment_not_found | 404 | Payment UUID not found. |
transfer_not_found | 404 | Transfer UUID not found. |
quote_not_found | 404 | FX quote UUID not found or expired. |
Payment and transfer errors
| Code | HTTP | Meaning |
|---|---|---|
insufficient_funds | 422 | Sender balance is below the requested transfer amount. |
sender_no_coinbase_wallet | 422 | Sender attempted a USDC/EURC transfer but has no Solana wallet provisioned. |
recipient_not_found | 422 | Recipient email is not registered on CashXChain. |
phantom_recipient | 422 | Recipient account exists but has no partner links (no wallet or bank provisioned). |
external_send_blocked | 400 | External bank send rejected in sandbox — bank details do not match a registered sandbox user. |
invalid_currency | 400 | Currency code is not supported by the selected partner. |
invalid_partner | 400 | Partner slug is not recognised or not enabled for this environment. |
customer_not_linked | 400 | Customer is not enrolled with the specified partner. |
FX errors
| Code | HTTP | Meaning |
|---|---|---|
quote_expired | 422 | FX quote TTL has passed (30-second window). |
quote_already_consumed | 409 | FX quote has already been executed. |
unsupported_pair | 400 | No rate available for the requested currency pair. Sandbox supports: USD/EUR, USD/GBP, EUR/GBP and inverses. |
corridor_unavailable | 503 | Currency or country corridor is not currently enabled. |
Idempotency errors
| Code | HTTP | Meaning |
|---|---|---|
idempotency_key_missing | 400 | Write route requires an Idempotency-Key header but none was provided. |
idempotency_conflict | 409 | The same Idempotency-Key was used with a different request body. |
Account and compliance errors
| Code | HTTP | Meaning |
|---|---|---|
account_not_active | 403 | Account KYB status is not active — cannot transact. |
requires_action | 403 | Additional information or approval is required before this operation can proceed. |
cross_account_access_denied | 403 | Non-admin caller attempted to access a resource belonging to another account. |
Partner errors
| Code | HTTP | Meaning |
|---|---|---|
partner_unavailable | 503 | Downstream payment partner is temporarily unavailable. Retry with exponential backoff. |
partner_error | 502 | Partner returned an unexpected error. The operation may or may not have been processed — check GET /v1/payments/{id} or GET /v1/transfers/{id}. |
Validation errors
| Code | HTTP | Meaning |
|---|---|---|
invalid_request | 400 | Request body or query parameters are missing required fields, wrong types, or failed validation rules. |
invalid_country_code | 400 | country_code is not a valid ISO 3166-1 alpha-2 code. |
invalid_currency | 400 | currency is not a valid ISO 4217 code or is not supported on this route. |
invalid_key_type | 400 | type must be secret, publishable, or restricted. |
Rate limit errors
| Code | HTTP | Meaning |
|---|---|---|
rate_limit_exceeded | 429 | Too many requests. Retry after the duration indicated in the Retry-After response header. |
Debugging
All requests are automatically logged. Reference a request by its log ID when contacting support:
GET /v1/request-logs/{id}
This returns the full request and response body for that request. List recent logs with:
GET /v1/request-logs?limit=100
Safe retries
All write routes are idempotent when an Idempotency-Key header is supplied. Repeating the same key with the same body returns the cached response without re-executing the operation — safe on network errors, timeouts, and 5xx responses.
Do not retry on 400, 401, 403, or 409 — these indicate a client-side error that will not resolve on retry.