Pilot environment: testnet, simulated yield, mocked fiat rails — read the disclosure
Using the API

Errors

The error envelope, the HTTP statuses, and every code the API can return with what to do about it and whether it is safe to retry.

The error shape

An error is not wrapped in the success envelope. It has its own shape:

Error response
{
  "statusCode": 400,
  "timestamp": "2026-09-02T10:17:34.221Z",
  "path": "/api/partner/v1/deposits",
  "method": "POST",
  "message": "amount carries more fractional digits than USD settles at",
  "code": "AMOUNT_PRECISION_EXCEEDED",
  "allowedDecimals": 2,
  "details": { "allowedDecimals": 2 }
}
FieldAlways presentNotes
statusCodeYesMatches the HTTP status.
timestampYesISO 8601.
path, methodYesThe request that failed.
messageYesHuman-readable, and worth logging in full. On a money error it states what did and did not move. Do not branch on it.
codeOn every documented errorA stable SCREAMING_SNAKE identifier. Branch on this.
detailsWhen there is contextYour own data only. Amounts inside it are decimal strings.
promoted keysWhen details is presentEach key of details is also copied to the top level, so body.allowedDecimals and body.details.allowedDecimals are the same value. Read whichever you prefer. Use details if you want one place to look.

An unexpected server fault has no code and reads "message": "An unexpected error occurred". Treat it as a network error. See Handling 5xx.

HTTP statuses

StatusMeansGenerally
400The request is wrong.Fix it. Retrying unchanged cannot help.
401The key is missing, malformed, unknown, revoked or expired.Fix the credential.
403The key is valid but not permitted — a missing scope, or an object that is not yours to touch.Fix the key or the target.
404No such object in your scope. A foreign object and a non-existent one answer identically, so an id cannot be probed.Fix the id.
409The request is well-formed but conflicts with the current state.Depends on the code — see below.
422The request is understood but cannot be carried out on this rail.Use the right rail.
429You exceeded your rate limit.Back off and retry.
500Something failed after the request was accepted. On a money route this may mean money moved.Read the code before doing anything.
503A dependency was unavailable. Nothing was attempted.Retry with the same key.

The code catalogue

Read the Retry column first.

  • Same key — safe to resend the identical request with the same Idempotency-Key.
  • New key — nothing moved; a fresh request under a fresh key is safe.
  • Poll — money may have moved or already completed. Re-read the intent under the original key. Never resubmit.
  • Fix first — a retry cannot succeed until you change something.

Handling 5xx and network errors

A timeout, a dropped connection or a 500 on a money route tells you the request may have been carried out. It never tells you it was not.

Resend the identical request with the same Idempotency-Key. The mechanics are in Conventions. One of four things comes back.

You getIt means
The original result, replayed: trueThe first call succeeded. Money moved once.
409 IDEMPOTENT_REQUEST_IN_PROGRESSThe first call is still running. Wait and resend the same key.
A *_FAILED_PENDING_COMPENSATION or *_SETTLEMENT_NOT_RECORDED codeMoney moved. Stop; TCB resolves it — automatically where the outcome can be proven, by an operator otherwise. Never use a new key.
A terminal failure codeNothing moved. You may now start a genuinely new request under a new key.

Practical advice:

  • Retry only idempotent-safe conditions: 429, 503, connection errors, and timeouts. Exponential backoff, with a ceiling.
  • Never generate a new idempotency key on a retry path. Mint the key when you first decide to move money, persist it beside your own record, and reuse it for every attempt at that movement.
  • Log the code, the message and your idempotency key together. Those three resolve almost every support request.

Pilot disclosure

On this page