Skip to main content

API Conventions

These rules hold across the API and are not repeated on the endpoint pages.

What is documented here

These rules hold across every endpoint, so they are stated once here rather than repeated on each page. The API is also browsable as a Swagger UI if you want to try a call.

Base URL and paths

All requests go to https://api.example.com, and every public path carries the /api prefix:

https://api.example.com/api/withdrawals

Authentication

Every request carries the merchant's token in the Api-Token header:

Api-Token: YOUR_API_TOKEN

One endpoint is public and needs no token: GET /api/health. Everything else requires it. See Authentication.

Status codes

MethodSuccess status
GET200
POST201
DELETE200

A create therefore answers 201, not 200 — branch accordingly.

Content type

Request bodies are JSON and must be sent with Content-Type: application/json.

Lists and pagination

Every list endpoint is paginated through two query parameters:

ParameterDefaultRange
page1from 1
per_page2011000

A list does not return a bare array. It returns an envelope with the page metadata beside the rows:

LIST ENVELOPE
{
"total_count": 137,
"per_page": 20,
"total_pages": 7,
"page": 1,
"prev_page": null,
"next_page": 2,
"items": []
}

prev_page and next_page are null at the ends of the range. Read the rows from items.

Paginated endpoints are the eleven collection routes: assets, markets, members, deposit addresses, orders, trades, deposits, withdrawals, auto-conversions, transfers and webhooks.

Two kinds of response are not paginated and come back as plain values:

  • single-object reads — GET /api/…/{id};
  • GET /api/members/{id}/accounts, which returns a bare array of accounts.
Lists have no defined order, and paging is therefore unreliable

No list endpoint applies an explicit sort. Row order is not guaranteed — not by id, not by created_at, not between two identical requests. sort and sort_by are not supported and are silently ignored.

Because the underlying selection is unordered, paging through it can return the same row twice and skip others entirely. Page 2 is not "the rows after page 1".

What to do instead:

  • For a bounded set, ask for it in one page — per_page goes up to 1000 — and sort it yourself.
  • For anything larger, page through it, deduplicate by id, and accept that a single pass may be incomplete; repeat until the set stops changing.
  • Never build "the newest N" by reading page 1.

Data types

ValueWire formatNotes
IdentifiersJSON number90210. Used in paths and in query filters.
Client-supplied idsJSON stringclient_order_id and client_withdrawal_id are strings, not numbers.
Amounts, in a responseJSON stringmin_withdrawal, min_deposit and every fee field come back as decimal strings such as "1.000000".
Amounts, in a requeststring or numberamount, price and volume accept either. Sending a string avoids float rounding.
Booleansreal JSON booleanactive_deposit, active_withdrawal, aml_active.
precisionJSON numberDecimal places of a network's asset.
TimestampsISO 8601 string, UTC, milliseconds2026-08-28T13:25:11.098Z. Same format on the way in — expired_at is sent this way.
Send amounts as strings

Every amount field accepts a string, and every amount comes back as a string. Keeping money in strings end to end removes the whole class of float-rounding bugs — "0.1" stays "0.1".

Idempotency

No endpoint enforces an idempotency key. client_withdrawal_id on a payout and client_order_id on an order are optional, and the transfer body has no client-id field at all.

A retried request can act twice

Because nothing is enforced, a payout or a transfer sent twice — a timeout, a retry, a double click — may move the money twice. Guard against it on your side: create the operation once, record the id you get back, and use the list filters (client_withdrawal_id on payouts, client_order_id on orders) to check whether a request landed before retrying it.

Whether the service deduplicates internally when the same client_withdrawal_id is reused is not documented — do not rely on it either way until confirmed.

Field naming

The same value can be spelled differently on the way in and on the way out. Two cases to know:

OperationIn the requestIn the response
Transferassetasset_id
Auto-conversiondchain_from, asset_todchain_from_id, asset_to_id, plus asset_from_id

Inside an object an identifier is plain id — a member is id, not member_id, and an account is id, not account_id. The member_id and account_id spellings appear only where one object points at another.

Permissions

There is no scope or permission concept: one token does everything. See Authentication.

Errors

A failure answers with a numeric code and a message. Branch on the code; the messages are for humans and some are composed at runtime. The full catalogue is in Errors.