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:
{
"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 }
}| Field | Always present | Notes |
|---|---|---|
statusCode | Yes | Matches the HTTP status. |
timestamp | Yes | ISO 8601. |
path, method | Yes | The request that failed. |
message | Yes | Human-readable, and worth logging in full. On a money error it states what did and did not move. Do not branch on it. |
code | On every documented error | A stable SCREAMING_SNAKE identifier. Branch on this. |
details | When there is context | Your own data only. Amounts inside it are decimal strings. |
| promoted keys | When details is present | Each 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
| Status | Means | Generally |
|---|---|---|
400 | The request is wrong. | Fix it. Retrying unchanged cannot help. |
401 | The key is missing, malformed, unknown, revoked or expired. | Fix the credential. |
403 | The key is valid but not permitted — a missing scope, or an object that is not yours to touch. | Fix the key or the target. |
404 | No such object in your scope. A foreign object and a non-existent one answer identically, so an id cannot be probed. | Fix the id. |
409 | The request is well-formed but conflicts with the current state. | Depends on the code — see below. |
422 | The request is understood but cannot be carried out on this rail. | Use the right rail. |
429 | You exceeded your rate limit. | Back off and retry. |
500 | Something failed after the request was accepted. On a money route this may mean money moved. | Read the code before doing anything. |
503 | A 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 get | It means |
|---|---|
The original result, replayed: true | The first call succeeded. Money moved once. |
409 IDEMPOTENT_REQUEST_IN_PROGRESS | The first call is still running. Wait and resend the same key. |
A *_FAILED_PENDING_COMPENSATION or *_SETTLEMENT_NOT_RECORDED code | Money moved. Stop; TCB resolves it — automatically where the outcome can be proven, by an operator otherwise. Never use a new key. |
| A terminal failure code | Nothing 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, themessageand your idempotency key together. Those three resolve almost every support request.
Conventions
Idempotency, rate limits, amounts and versioning in detail.
The money lifecycle
What each state means and which webhook fires.