Orders
All order actions are signed by your trading key — see Authentication & Signing for the domain and signing rules. account_id and wallet are as defined in Account format.
Submit orders
The endpoint is a batch: 1–20 independently signed legs per request, all for the same account_id.
Order types
The schema also carries LIQUIDATION and ORPHAN_LIQUIDATION. They are server-generated, appear on read routes only, and must never be submitted — accept them when parsing.
What limit_price means changes with the type:
- On a
MARKETorder it is your slippage bound, which you compute yourself as mark × (1 ± tolerance). You can never be filled beyond it; whatever cannot fill within the bound is cancelled. - On a
LIMITorder it is the resting price. - On a
TRIGGERorder it is the price the order fires at — not the price that arms it. That istrigger.trigger_price.
TRIGGER orders omit time_in_force entirely; the server derives it from trigger.is_market.
Post-only
Post-only is not a separate flag: it is the time_in_force value ALO (add liquidity only). An order carries exactly one time_in_force — GTC, IOC, or ALO — so post-only cannot be combined with another time in force. A post-only order rests until filled or cancelled, like GTC, but it never takes: it rests or it is rejected.
The leg signs timeInForce: "ALO".
LIMITonly. AMARKETleg withALOis rejected withmarket_order_must_be_ioc. ATRIGGERleg’s time in force is always derived fromtrigger.is_market, so a trigger cannot be post-only.- The crossing check happens at the venue, not at validation. A post-only order whose price would match the book passes
POST /ordersasSUBMITTEDand then readsREJECTEDwithreason: post_only_would_takeonGET /orders/{orderId}. Keep a buy below the best ask and a sell above the best bid. - A post-only fill is always a maker fill, so it is charged the maker rate.
- A modify inherits
ALO, so the new order can be rejected withpost_only_would_takein the same way.
Trigger orders
trigger_price— the raw mark-price threshold that arms the fire (no slippage applied).is_market—truefires an IOC order atlimit_price. Onlyis_market: trueis supported today; trigger-limit (false) is rejected.tpsl—TPorSL.SLfires when the mark moves against the position,TPwhen it moves in favour. A trigger already past the mark on its fire side is rejected.size: "0"on a reduce-only trigger means close the full position at fire time.
Bracket orders
A bracket is an opening MARKET or LIMIT entry with a whole-position take-profit and/or stop-loss attached in the same batch. The TP/SL legs are accepted with the entry, wait while it works, and arm themselves once it fills — no polling for the fill, no window where the position is unprotected.
Attach a leg by naming the entry’s client_order_id in parent_client_order_id:
Shape rules, checked before anything is queued:
- On a
MARKETentry, check TP/SL against the mark at submit, not the entry’slimit_price— that is only your slippage bound. A TP placed just above a long’s slippage bound but at or below the mark is rejected withbracket_tp_wrong_side. parent_client_order_idis not signed — each leg still signs its own plainOrder.- The group is decided together: if any member fails validation, every member of the group comes back
REJECTEDwith the samereason. Other legs in the batch are unaffected. - Attached legs count toward
max_open_ordersfrom the moment they are accepted.
Lifecycle of an attached leg:
When a bracket leg arms, it replaces any armed whole-position trigger of the same tpsl kind on that market — the older one is cancelled with reason: bracket_replaced, so a position never carries two full-size stops.
While it is PENDING_PARENT the leg can be cancelled (order_type: "TRIGGER") but not modified (modify_not_modifiable). Modifying the entry re-checks the pending legs against the new limit_price and rejects the modify with bracket_tp_wrong_side / bracket_sl_wrong_side if a level would end up on the wrong side; otherwise the legs follow the modify to the new order_id.
Price and size rules
tick_size binds harder than px_decimals: BTC allows one decimal place but its tick is 1, so 60000.5 is rejected with price_off_tick.
Signing an order
Each leg signs Order(bytes32 account, …) — the struct is on Authentication & Signing. Every signed field must byte-match the body; a mismatch is a 403, not a validation error.
Response
data is an array index-aligned with your legs; top-level success is true only if every leg was SUBMITTED:
Legs are independent — one REJECTED (with reason) never cancels the others. The one exception is a bracket: an entry and its attached TP/SL are accepted or rejected as a group. SUBMITTED means validated and queued, not filled; the order then advances asynchronously.
Rejections
The list is not exhaustive.
Order lifecycle
The happy path is SUBMITTED → ACCEPTED → EXECUTING → FILLED.
Five statuses are terminal:
GET /orders/{orderId} can briefly return 404 right after submit — orders persist asynchronously. Retry with backoff. Statuses here are uppercase; list reads use a different vocabulary.
Cancel an order
Signed by your trading key as:
Note that account here is your wallet, not the account_id:
order_type is LIMIT (resting order) or TRIGGER (armed trigger, or a TP/SL still waiting for its entry); it is part of the signed payload, as is orderId — taken from the URL path, not the body. Returns status: SUBMITTED | REJECTED; 403 when the signature does not authorize the account. Cancellation is asynchronous — confirm with open-orders or position-triggers. Cancelling an entry that still has attached TP/SL cancels them too, with reason: bracket_parent_not_filled, unless the entry had already partly filled — then they arm against the position that fill opened.
Cancel in batch
1–20 legs, each signing its own Cancel. Per-leg status is SUBMITTED or FAILED (with reason) — FAILED, not REJECTED. A bad signature fails only its leg, as 200 with top-level success: false.
Modify an order
Signed by your trading key as:
- Every field is required on every call; there is no partial modify.
client_order_idmust be new — reusing the modified order’s id is rejected. It is signed here, unlike on an order leg.- Only price, size, and trigger price can change. Market, side, time in force (including
ALO), reduce-only, leverage, and order type are all inherited from the original order. - For a limit order send
trigger_price: "0"; for a trigger orderlimit_priceis the fire price. size: "0"(full-position close) is allowed only on TP/SL triggers.- Success returns the new order’s
order_id. If a trigger modify races the trigger firing (or an open-order-cap check at apply time), the modify is dropped and the original order is untouched. - A TP/SL in
PENDING_PARENTcannot be modified (modify_not_modifiable). Modifying its entry re-validates the attached levels against the newlimit_priceand moves them to the neworder_id— see Bracket orders.
Next: Position Lifecycle