Conventions
These apply to every endpoint. Read once.
Response envelope
Every response has the same envelope:
{
"code": 200,
"message": "Corridors fetched successfully",
"data": { },
"metadata": { }
}
| Field | Always present | Meaning |
|---|---|---|
code |
yes | HTTP status code, mirrored in the body for convenience. |
message |
yes | Human-readable summary of the result. |
data |
on success | The resource(s) — object or array. Omitted on errors. |
metadata |
on paginated lists | Pagination details (see below). |
Always branch on the HTTP status code; code mirrors it.
Pagination
List endpoints accept page (1-based) and return a metadata block:
{
"code": 200,
"message": "Transactions retrieved successfully",
"data": [ ],
"metadata": {
"page": 1,
"per_page": 20,
"total": 134,
"has_next": true,
"has_prev": false,
"total_pages": 7
}
}
To read the next page, add 1 to page. Continue until has_next is false.
Money & currency
-
All balances, amounts, and fees are in USD. There is one exception: a
field that names a different currency. Examples are the
currencyof a corridor, and the local amount that your customer pays or receives. - USD amounts are decimal (e.g.
100.50), not minor units.
Idempotency
Send an Idempotency-Key header on any request that moves money — a unique
value you generate per logical operation (a UUID is ideal):
Idempotency-Key: 7f3a1c9e-2b4d-4a6f-8e1c-9d2f5a7b3c4e
The first request runs and we keep its response. Every repeat of that key returns the same response, with the same status, instead of doing the work again. So when a payout times out, retry it with the same key: you either get the original result, or the payout is made — once.
Three cases are worth knowing:
| What you send | What you get |
|---|---|
| The same key, the same body | The first response, unchanged. |
| The same key, a different body | 422, idempotency_key_reused. Reusing a key for different work is a bug, and returning the first receipt would hide it from you. |
| The same key while the first is still running | 409, idempotency_in_progress. Wait a moment and ask again. |
If a request fails without answering, its key is released immediately so your retry runs. If the process handling it dies, the key frees itself after ninety seconds.
Errors
Errors return a non-2xx status and an envelope without data:
{
"code": 400,
"message": "account_name (sender name) is required for bank-transfer deposits"
}
| Status | Meaning |
|---|---|
400 Bad Request |
Invalid or missing parameters. The message says what. |
401 Unauthorized |
Missing, malformed, or revoked API key. |
403 Forbidden |
Authenticated but not allowed to access this resource. |
404 Not Found |
The resource does not exist, or it is not yours. |
422 Unprocessable |
The request is correct, but we cannot accept it. For example, the amount is less than the corridor minimum. |
5xx |
The error is on our side. You can send a read request again. For a write, use an Idempotency-Key. |
Branching on an error
message is written for a person, and we reword it whenever we find a clearer
sentence — so do not match on it. When a refusal is one you can do something
about, the envelope also carries an error_code, and that does not change:
{
"message": "This account cannot accept payments yet — its settlement account is still being set up.",
"error_code": "settlement_account_not_ready"
}
error_code |
What happened |
|---|---|
settlement_account_not_ready |
The account cannot be paid yet; its settlement account is still coming up. Try again shortly. |
corridor_unavailable |
We do not collect or pay out on this corridor right now. |
corridor_not_link_payable |
Payable through the API, but not through a payment link — its bank transfers arrive into a standing account, which cannot be matched to a single payment. |
amount_below_minimum |
Below the smallest amount the corridor will carry. |
insufficient_balance |
Not enough available balance to cover this. |
link_not_payable |
The payment link is expired, used up, or switched off. |
idempotency_key_reused |
The same Idempotency-Key was sent for different work. |
idempotency_in_progress |
A request with that key is still running. |
Errors we do not expect you to handle carry no error_code. That is deliberate:
a code is a promise that the meaning will not change, and we only make it where
we can keep it.