Skip to main content
Failures always use the shared envelope: HTTP status gives you the class, error.code the specific condition, error.message a human-readable explanation, and error.details structured context where it exists:
Match on error.code, not message; messages are for humans and may be reworded. The two flat Sandbox probes are the only endpoints that don’t use this envelope.
Money movement (/fund, /mint, /transfer, /redeem) is asynchronous. The HTTP codes below cover what can be checked in the request (input, wallet, caps, public balances, configuration). Anything that goes wrong while the background worker executes the operation does not produce an HTTP error at all: the transaction becomes status: "FAILED" with error and errorCode set. That catalog is in Asynchronous failures below.

400 Bad request

401 Unauthenticated

402 Payment required

403 Forbidden

404 Not found

409 Conflict

410 Gone

415 Unsupported media type

429 Too many requests

500 Internal error

502 Upstream failure

Two kinds of endpoint can return 502: read endpoints that talk to the confidential-rail relayer inline, and the two Aro Mail calls that broadcast a transaction inline (POST /transfer/letter and POST /transfer/letter/claim). The queued money-movement endpoints (/fund, /mint, /transfer, /redeem) never do: on-chain and relayer problems during execution become status: "FAILED" on the transaction (see the next section), and transient ones are retried by the worker without surfacing.
For escrow_unsettled and claim_unsettled, error.details carries { letterId, txHash } so a client can follow the letter with GET /transfer/letter/{id} instead of creating or claiming it again. Both mean the transaction was broadcast; only the bookkeeping is pending and reconciliation finishes it.
For the Aro Mail *_failed codes the call also records a LETTER_* transaction row with status: "FAILED" and the revert context in error; check GET /transactions for the audit trail. The *_unsettled codes are different: the transaction was broadcast, so its row stays PENDING with a txHash until reconciliation verifies the receipt. Symptom-level fixes live in Troubleshooting.

503 Configuration

A 503 naming an environment variable means the deployment is missing required configuration. Platform-side; report the message text to your admin. On money-movement endpoints these are checked before anything is queued, so a 503 means no transaction row was created.

Asynchronous failures (transaction.errorCode)

Money-movement requests return 201 with a QUEUED transaction; a background worker then executes it. When execution cannot proceed, the worker marks the transaction FAILED and sets two fields:
error is human-readable and may be reworded; errorCode is the stable key. Both are null on every non-FAILED transaction.
FAILED always means nothing moved on-chain for that row. A transaction is failed only when a pre-flight check rejected it before anything was broadcast, or when the on-chain call reverted (a revert moves no funds). A broadcast transaction whose outcome is still unknown stays PENDING (or SETTLING for a redeem payout) and keeps being tracked; it is never failed underneath you.
Codes that no longer exist: approval_timeout, operator_approval_timeout, redeem_settlement_pending, and redeem_settlement_failed. A redeem payout that cannot be sent yet keeps the row in SETTLING and is retried.
Symptom-level fixes for each code live in Troubleshooting.