Using the APIErrors

Errors

One error envelope everywhere, a coarse kind, a specific code, and a request id for support.

The envelope

Every failure, on every endpoint, returns the same shape:

{
  "error": {
    "kind": "invalid_request_error",
    "code": "verification_required",
    "message": "The customer must have an approved verification (KYC/KYB) before a wallet can be created.",
    "param": null,
    "request_id": "req_033xhsr1AZZxC8AOkK7y3W"
  }
}
  • kind is the coarse family. Branch on it for generic handling.
  • code is the specific condition. Branch on it for precise handling. Codes are stable API contract.
  • param names the offending field when one field is to blame.
  • request_id finds the exact request in our logs. Quote it to support. It is also on every response as the request-id header.

The message is ours. Nothing from behind the API reaches you: a failure anywhere downstream is translated into one of our codes, and the original wording goes to the logs where support needs it.

Kinds

kindHTTPWhen
authentication_error401Missing, unknown, revoked, or expired key. Wrong host for the key
permission_error403Suspended partner, missing partner terms or KYB, read-only key, sandbox-only endpoint
invalid_request_error400, 404, 409, 422Validation failures, state conflicts, unsupported corridors
idempotency_error409, 422Key reuse or a concurrent duplicate
not_found_error404No such resource in your partner and environment
rate_limit_error429Over the request rate. Honor Retry-After
api_error5xxOur fault. Safe to retry: 5xx responses are never stored for idempotent replay

Retry classes

Every code below carries one of three classes:

  • retryable: transient. Retry the same request, with backoff.
  • actionable: succeeds only after you change something.
  • terminal: a different request is required.

Authentication and access

codeHTTPClassMeaning
missing_api_key401actionableNo Api-Key header
invalid_api_key401terminalUnknown key
api_key_revoked401terminalThe key was revoked
api_key_expired401terminalThe rotation grace window has passed
environment_mismatch401actionablesk_test_ key on the live host, or the reverse
partner_suspended403terminalYour account is suspended
partner_tos_required403actionableYour partner terms are not on record
partner_kyb_required403actionableYour own business verification is not approved
read_only_key403actionableA read-only key attempted a mutation
sandbox_only403terminalA simulation endpoint called on the live host
unsupported_api_version400actionableApi-Version names a version we do not serve

Requests and idempotency

codeHTTPClassMeaning
missing_idempotency_key400actionableEvery POST /api/* needs the header
idempotency_key_reused422actionableSame key, different request. Mint a fresh key
request_in_progress409retryableThe first attempt with this key is still running
invalid_parameter400actionableA query parameter is missing, conflicting, or unknown
invalid_cursor400actionableThe pagination cursor is not one of ours
invalid_amount400actionableThe amount is finer than the asset's decimals
amount_out_of_range400actionableOutside the corridor's min or max
rate_limited429retryableHonor Retry-After, then retry unchanged

Terms and customers

codeHTTPClassMeaning
invalid_signed_tos_id400terminalThe agreement id does not exist or was not accepted
signed_tos_id_already_used409terminalOne acceptance binds one customer. Mint a new link
tos_already_accepted409terminalThe customer is already on the current terms version
partner_reference_id_exists409actionableYour reference id is already used in this environment
unknown_endorsement422actionableThe endorsement name is unknown or not enabled. The message lists the available ones
endorsement_profile_required422actionableRequesting endo_usd needs email, phone, economic_profile.tin, and .tax_residence_country in the same request

Verification

codeHTTPClassMeaning
customer_tos_required400actionableThe customer has no terms acceptance on record
verification_incomplete422actionableRequired inputs are still outstanding. The message names them
document_rejected422actionableA supplied document was rejected at intake. Supply a replacement, then submit again
verification_not_started400actionableAn operation needs a verification round that does not exist yet

Virtual accounts

codeHTTPClassMeaning
endorsement_required400actionableThe currency's endorsement was never requested. The message names the exact request
endorsement_not_approved400actionableIt was requested but is not approved. The customer's requirement buckets say why
verification_required400actionableApprove the customer's verification first
customer_name_required400actionableThe customer needs a name: it becomes the account holder name
unsupported_corridor400actionableThat currency and destination pair is not enabled. Check GET /api/corridors
unsupported_rail400actionableThat rail does not serve that currency
virtual_account_exists400actionableThis customer already has that rail and currency. PATCH its destination instead
no_rate_available400retryableNo price for the pair right now
no_fee_destination400terminalPartner fees need your balance account set up. Contact support

Wallets and operations

codeHTTPClassMeaning
public_keys_required400actionableRegister both public halves first. We never accept a private key
unsupported_chain400actionableThat chain is not enabled
wallet_not_owned400actionableaddress_wallet_id must name a wallet of the same customer
unsupported_address_share400actionableAddress sharing is EVM only, on both sides
address_exists409terminalThat address already has a wallet on that chain
activation_required400actionableThe Primary Signing Key is not activated yet
wallet_not_active400actionableThe wallet is frozen or closed
insufficient_balance400actionableThe chain balance is short. Balances are chain reads, not a ledger
operation_expired400actionableThe 30-second window elapsed. Create a new operation
operation_not_authorizable400terminalThe operation already reached a terminal state
operation_not_found404terminalNo such operation on that wallet
invalid_signature422actionableNot your registered key's raw ed25519 signature over the digest

Wallet key registration (dashboard)

codeHTTPClassMeaning
keys_required400actionableRegister both keys before preparing activation
keys_already_registered409terminalKeys are registered once. Contact support to change the identity
invalid_public_key422actionableBoth must be base58 ed25519 public keys, and they must differ
activation_rejected422actionableThe signed activation was not the one we prepared, or not signed by your Recovery Key

Not found

404 not_found_error codes are per resource: customer_not_found, virtual_account_not_found, wallet_not_found, tos_link_not_found, webhook_not_found, webhook_event_not_found, deposit_not_found, conversion_not_found, payout_not_found, transaction_not_found.

404s are ownership-scoped. Asking for another partner's customer returns not_found_error, not 403: resource existence is never leaked across tenants.

Server errors

api_error with a 5xx status is ours. Retry with the same Idempotency-Key: a 5xx is never stored for replay, so the retry executes fresh. A 503 means a dependency is unavailable and the request was refused rather than half-applied.