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
kind
HTTP
When
authentication_error
401
Missing, unknown, revoked, or expired key. Wrong host for the key
Validation failures, state conflicts, unsupported corridors
idempotency_error
409, 422
Key reuse or a concurrent duplicate
not_found_error
404
No such resource in your partner and environment
rate_limit_error
429
Over the request rate. Honor Retry-After
api_error
5xx
Our 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
code
HTTP
Class
Meaning
missing_api_key
401
actionable
No Api-Key header
invalid_api_key
401
terminal
Unknown key
api_key_revoked
401
terminal
The key was revoked
api_key_expired
401
terminal
The rotation grace window has passed
environment_mismatch
401
actionable
sk_test_ key on the live host, or the reverse
partner_suspended
403
terminal
Your account is suspended
partner_tos_required
403
actionable
Your partner terms are not on record
partner_kyb_required
403
actionable
Your own business verification is not approved
read_only_key
403
actionable
A read-only key attempted a mutation
sandbox_only
403
terminal
A simulation endpoint called on the live host
unsupported_api_version
400
actionable
Api-Version names a version we do not serve
Requests and idempotency
code
HTTP
Class
Meaning
missing_idempotency_key
400
actionable
Every POST /api/* needs the header
idempotency_key_reused
422
actionable
Same key, different request. Mint a fresh key
request_in_progress
409
retryable
The first attempt with this key is still running
invalid_parameter
400
actionable
A query parameter is missing, conflicting, or unknown
invalid_cursor
400
actionable
The pagination cursor is not one of ours
invalid_amount
400
actionable
The amount is finer than the asset's decimals
amount_out_of_range
400
actionable
Outside the corridor's min or max
rate_limited
429
retryable
Honor Retry-After, then retry unchanged
Terms and customers
code
HTTP
Class
Meaning
invalid_signed_tos_id
400
terminal
The agreement id does not exist or was not accepted
signed_tos_id_already_used
409
terminal
One acceptance binds one customer. Mint a new link
tos_already_accepted
409
terminal
The customer is already on the current terms version
partner_reference_id_exists
409
actionable
Your reference id is already used in this environment
unknown_endorsement
422
actionable
The endorsement name is unknown or not enabled. The message lists the available ones
endorsement_profile_required
422
actionable
Requesting endo_usd needs email, phone, economic_profile.tin, and .tax_residence_country in the same request
Verification
code
HTTP
Class
Meaning
customer_tos_required
400
actionable
The customer has no terms acceptance on record
verification_incomplete
422
actionable
Required inputs are still outstanding. The message names them
document_rejected
422
actionable
A supplied document was rejected at intake. Supply a replacement, then submit again
verification_not_started
400
actionable
An operation needs a verification round that does not exist yet
Virtual accounts
code
HTTP
Class
Meaning
endorsement_required
400
actionable
The currency's endorsement was never requested. The message names the exact request
endorsement_not_approved
400
actionable
It was requested but is not approved. The customer's requirement buckets say why
verification_required
400
actionable
Approve the customer's verification first
customer_name_required
400
actionable
The customer needs a name: it becomes the account holder name
unsupported_corridor
400
actionable
That currency and destination pair is not enabled. Check GET /api/corridors
unsupported_rail
400
actionable
That rail does not serve that currency
virtual_account_exists
400
actionable
This customer already has that rail and currency. PATCH its destination instead
no_rate_available
400
retryable
No price for the pair right now
no_fee_destination
400
terminal
Partner fees need your balance account set up. Contact support
Wallets and operations
code
HTTP
Class
Meaning
public_keys_required
400
actionable
Register both public halves first. We never accept a private key
unsupported_chain
400
actionable
That chain is not enabled
wallet_not_owned
400
actionable
address_wallet_id must name a wallet of the same customer
unsupported_address_share
400
actionable
Address sharing is EVM only, on both sides
address_exists
409
terminal
That address already has a wallet on that chain
activation_required
400
actionable
The Primary Signing Key is not activated yet
wallet_not_active
400
actionable
The wallet is frozen or closed
insufficient_balance
400
actionable
The chain balance is short. Balances are chain reads, not a ledger
operation_expired
400
actionable
The 30-second window elapsed. Create a new operation
operation_not_authorizable
400
terminal
The operation already reached a terminal state
operation_not_found
404
terminal
No such operation on that wallet
invalid_signature
422
actionable
Not your registered key's raw ed25519 signature over the digest
Wallet key registration (dashboard)
code
HTTP
Class
Meaning
keys_required
400
actionable
Register both keys before preparing activation
keys_already_registered
409
terminal
Keys are registered once. Contact support to change the identity
invalid_public_key
422
actionable
Both must be base58 ed25519 public keys, and they must differ
activation_rejected
422
actionable
The 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 sameIdempotency-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.