Order struct, signs it with the trading wallet, and submits the signed values. The server recovers the signer, recomputes the order hash, enforces the market’s trading rules, and hands the order to the matching engine.
The API signs nothing on a trader’s behalf, and no endpoint can amend a signed order, because the flags and prices are inside the signature.
Before signing
ReadGET /assets for the target market. The signature covers marketId while the request body carries assetId, so both are required:
marketId is nullable. A market that has never published a snapshot reports null, and no valid order can be signed for it, because the signature covers that field.
tickSize, lotSize, and maxLeverage are also nullable, and here null means the market publishes no such constraint rather than that the market is unusable. Where a value is present, prices must be a multiple of tickSize, sizes a multiple of lotSize, and leverage within maxLeverage; violations are rejected as TICK_SIZE, LOT_SIZE, or MAX_LEVERAGE. Read the fields per market rather than assuming a constraint exists.
A universal 0.000001 precision floor applies regardless.
The Order struct
The domain, fromGET /status, using exchange.chainId and exchange.clearingHouseAddress:
The four monetary fields are signed as 1e6 fixed-point integers but submitted as decimal strings. Scale for the signature, then send the human-readable form.
Submitting
INVALID_SIGNATURE.
Response:
idempotent: true. There is no client order id, but the order hash can be computed locally before submitting, so the identifier is known in advance.
Order types
A limit order executes at its price or better, and rests on the book until it fills, expires, or is cancelled. A market order still carries alimitPrice, which acts as a slippage bound, because it is submitted as an aggressive limit order and so cannot execute at an unbounded price. The matching engine rejects a market order into an empty book, so limit orders are the safer choice for quoting rather than taking.
Batching
POST /orders/batch takes up to 20 orders and POST /orders/batch-cancel up to 50 hashes. Both return per-item results rather than failing as a unit:
Accepted is not filled
A200 means the engine took the order. It can still reject it afterwards, and that surfaces on the order rather than on the submit response.
Poll GET /account/orders/{orderHash} or watch the orders stream. A rejected or cancelled order carries rejectCode, a small integer from the engine, and rejectReason, its decoded description. Both are null while the order is live.
For a rejected order, branch on rejectCode and treat rejectReason as display text. These are the codes a trading client meets most often; the engine’s map is larger:
For a cancelled order the codes are unrelated to the table above:
Cancelling
Dead man’s switch
POST /orders/cancel-all-after arms a countdown. If it lapses, every open order for the key is cancelled, so a bot that crashes or loses connectivity does not leave exposure resting on the book.
{ "timeout": 0 } to disarm.
Treat this as mandatory for any unattended strategy. It is the only mechanism that withdraws
resting orders when the client process itself is what failed.
When trading is halted
While trading is paused, order submission returns503 TRADING_DISABLED and the system:status stream fires.
Cancels remain accepted, deliberately, so exposure can always be pulled during a halt. Error handling should let a halt stop submissions without also blocking cancels.
GET /assets also reports a tickerState per market: AUCTION means orders accrue without continuous matching, CONTINUOUS is normal matching, DELISTED markets stay listed so old positions still resolve, and UNKNOWN means the book has never published a snapshot.