Errors & pagination
The cross-cutting rules every endpoint follows: status codes, cursor pagination, idempotency, and rate limits.
Status codes#
Errors use standard HTTP status codes with a JSON body of the shape { "error": "code", "message"?: "..." }. Validation errors also include a details field describing what failed.
| Field | Type | Required | Description |
|---|---|---|---|
| 400 | invalid_request | no | Malformed body or query (schema validation failed). |
| 401 | invalid_api_key | no | Missing or invalid Bearer token. |
| 404 | *_not_found | no | The referenced resource does not exist. |
| 422 | unprocessable | no | A valid request the business rules rejected (e.g. chain_not_enabled, unsupported_token, payout_failed). |
| 429 | rate_limited | no | Too many requests — see Rate limits below. |
| 500 | internal_error | no | Unexpected server error. |
{
"error": "unsupported_token",
"message": "Token USDT is not supported on ETH"
}Pagination#
All list endpoints (/v1/users, /v1/deposits, /v1/payouts, /v1/events) are cursor-based. Pass limit (1–200, default 50) and an optional cursor; each response returns next_cursor and has_more.
To page through results, pass the previous response's next_cursor as the next request's cursor, and stop when has_more is false.
let cursor = null;
do {
const res = await get("/v1/deposits", { limit: 100, cursor });
process(res.deposits);
cursor = res.next_cursor;
} while (res.has_more);Idempotency#
Every mutating operation is safe to retry:
- Users are keyed on your
external_ref— re-creating returns the same user and addresses. - Deposits are de-duplicated on
(txid, outputIndex), so an output is never credited twice. - Payouts accept an
Idempotency-Keyheader — a repeat with the same key returns the original settlement instead of creating a second one.
POST /v1/payouts
Idempotency-Key: 3f8c1e5a-9b2d-4c7e-8f1a-...
{ "chain": "ETH", "token": "USDC" }Rate limits#
A per-merchant rate limit applies across /v1. Exceeding it returns 429 rate_limited. Back off and retry — batching and cursor pagination keep you well under the limit for normal workloads.
If you expect sustained high throughput (bulk provisioning, backfills), spread the work out rather than bursting, and prefer webhooks over tight polling loops.