Conventions
Money as decimal strings, party hints, timestamps, the instrument catalog
Money is a decimal string
Every amount and price on the wire is a ledger Decimal string matching
^\d+(\.\d{1,10})?$ — positive, dot-separated, at most 10 fractional digits.
Never a float. Parse with a decimal/bigint library, not parseFloat, if
you do arithmetic on it.
{ "price": "97300.0", "qty": "5.0", "fee": "121.625" }Parties are hints
Party fields (maker, taker, invitedMakers, …) carry short party
hints — never full Canton party ids. Treat them as opaque identifiers
scoped to the desk. Yours is assigned at registration, derived from the
displayName you sent plus a uniquifying suffix:
{ "maker": "acme-mm-4f2a91" }Full party ids appear only where you need a real one: your own in the
registration responses and GET /maker/status, the key-recovery calls
(/maker/challenge, /maker/api-key/rotate), and the receiver of a
withdrawal.
Time
Timestamps are ISO-8601 UTC strings. validUntil in quote submissions also
accepts a zone offset (RFC 3339), not only the Z suffix — and may be omitted
entirely, in which case the desk defaults it (see Quoting).
The instrument catalog
GET /instruments returns the tradable catalog:
[{ "symbol": "cbtc", "displaySymbol": "cBTC", "decimals": 10 }]RFQs reference instruments by catalog symbol (base, quote); symbols you
send ignore case, so the display form (cBTC) works too — unless two
instruments share it, when only the catalog symbol names one. Use decimals to
render amounts; displaySymbol for humans. Holdings and pending deposits carry
both fields, so their symbol goes straight into a withdrawal. A holding in an
instrument that has left the catalog carries its raw registry id in both symbol
fields.
GET /desk returns the base fee tier and the makers a taker can invite right
now (the ones whose stream is open). The fee that applies to a trade is the one
stamped on the RFQ (rfq.feeBps), sized by the ticket's USD notional when the
RFQ is created.
200 vs 202
Wallet calls (/wallet/incoming/:cid/accept, /wallet/withdraw) answer
202 when the effect waits on your signature and 200 when it does not —
both with the same {actions} body. POST /maker/quotes always answers 200
and an accept always 202, each with actions inside. Treat the body as the
source of truth — sign whatever actions contains — and the status difference
never matters.
Errors
Error bodies are {error, code?}:
error— human-readable, not a contract: the text may change.code— machine-readable and stable (RETRY_LATER,INSUFFICIENT_FUNDS,DUST_TRADE,BELOW_MIN_NOTIONAL); present only where defined. See Error handling for the full matrix.