Error handling

API Documentation · Zypher Global

Generate credentials

Every API failure returns a standardized JSON body with a human-readable message and a stable error_code. The HTTP status also reflects the error (not always 200). Handle errors by error_code, it does NOT change across languages or versions.

Error format
HTTP 422
{
  "ok": false,
  "error": "Chave PIX não encontrada.",
  "error_code": "pix_key_not_found"
}
  • ok — always false on error.
  • error — human-readable message (localized). Good to display/log; do NOT use for logic.
  • error_code — stable slug to handle in code (switch/if). This is the source of truth.

HTTP Status

  • 400 — invalid request (data/fields).
  • 401 — missing or invalid credentials.
  • 403 — no permission, IP not allowed, or resource disabled.
  • 422 — data rejected during processing (e.g., key not found, insufficient balance).
  • 429 — too many attempts. Wait and try again.
  • 500 / 503 — internal error or service temporarily unavailable.
The status is also sent in the X-App-Error-Code header (for clients that only read headers).

Error codes

The same error_code can appear on different routes. Always handle by the code.

Authentication and access

  • missing_credentials401— Client ID/Secret missing in the header.
  • invalid_credentials401— Invalid Client ID/Secret.
  • key_inactive403— API key is inactive.
  • permission_denied403— The key lacks the required permission (cashin/cashout/balance).
  • ip_not_configured403— Cash-out without an IP in the key allowlist.
  • ip_not_allowed403— Source IP not in the allowlist (or not IPv4).
  • invalid_account403— Account linked to the key is invalid.
  • rate_limited429— Too many requests in a short time.

Key / recipient (PIX)

  • pix_key_not_found422— The PIX key does not exist in DICT.
  • pix_key_invalid422— PIX key has an invalid format.
  • pix_key_type_invalid422— Key type not accepted for this destination.
  • invalid_document422— CPF/CNPJ invalid or does not match the key.
  • name_mismatch422— Recipient name/document does not match the key.
  • recipient_cannot_receive422— Destination account cannot receive (closed/inactive).
  • destination_blocked422— Destination key or account blocked/suspended.

Amount and balance

  • insufficient_balance400— Insufficient balance for the amount + fee.
  • amount_below_min422— Amount below the minimum allowed.
  • amount_above_limit422— Amount above the allowed limit.
  • amount_unavailable422— No rail covers this amount right now, try a lower amount.
  • invalid_amount422— Invalid or zero amount.

Processing / provider

  • duplicate422— Duplicate operation (check if it was already processed).
  • unauthorized403— Operation not authorized by the provider.
  • maintenance503— Service under maintenance.
  • provider_unavailable422— Service unavailable or timeout, you can retry.
  • cashin_failed422— Generic failure creating the charge.
  • cashout_failed422— Generic withdrawal failure (contact support).

Account / permissions

  • kyc_required403— Account without document verification (KYC).
  • currency_not_supported400— Currency not supported (use BRL, MXN, ARS, COP or USDT).
  • currency_not_enabled403— Currency not enabled for this account, contact support.
  • phone_required422— Buyer phone required for COP cash-in (NEQUI/BRE-B).

Conversion

  • invalid_request400— Fields amount/from/to missing or invalid.
  • conversion_failed500— Failed to execute the conversion (nothing was debited).
  • conversion_in_progress409— A conversion with that external_id is already in progress, wait, do not resend.

Lookup / status

  • missing_reference400— Missing identifier (request_number / e2e / transaction_id).
  • deposit_not_found404— No deposit for the given identifier.
  • withdrawal_not_found404— No withdrawal for the given identifier.
Retryable: provider_unavailable and rate_limited are transient, you can repeat the same request (with the same external_id to avoid duplicates). The rest are final: fix the data before resending.

Handling example

JavaScript
const r = await fetch(url, opts);
const data = await r.json();
if (!data.ok) {
  switch (data.error_code) {
    case 'pix_key_not_found':
    case 'pix_key_invalid':
      // pedir outra chave ao usuário
      break;
    case 'insufficient_balance':
      // avisar saldo
      break;
    case 'provider_unavailable':
    case 'rate_limited':
      // retentar com backoff (mesmo external_id)
      break;
    default:
      // mostrar data.error e logar
  }
}
Was this page helpful?