An order moves from admission to a terminal status, and every interface reports the same lifecycle. This page explains what you can place, what each status means, how an order changes, and where to read it back. Field-by-field schemas are in the API reference.
Order types
Every order is a limit or market order with one time in force. Place it with CreateOrder or POST /v1/orders, which select both with one execution variant inside order; the SBE WebSocket sets orderType and timeInForce separately.
| Execution variant | SBE equivalent | Behavior |
|---|---|---|
limitGtc | LIMIT + GTC | Rests on the book until it fills or is canceled |
limitGtd | LIMIT + GTD | Rests until it fills, is canceled, or reaches its expiry time |
limitIoc | LIMIT + IOC | Fills what it can at the limit price or better; the remainder is canceled |
limitFok | LIMIT + FOK | Fills its whole quantity at once, or is rejected without filling anything |
marketIoc | MARKET + IOC | Takes available liquidity up to a protected price; the remainder is canceled |
A market order always executes as immediate-or-cancel; it never rests and cannot be post-only. Its price boundary is described in Market order price protection.
Post-only
Set postOnly on a limitGtc or limitGtd order to make sure it only adds liquidity. If the order
would trade on arrival, it is rejected with POST_ONLY_CROSS instead of filling. A Pair in post-only
mode accepts only post-only orders and refuses others with PAIR_POST_ONLY.
Good-till-date expiry
A limitGtd order carries expireAt, an exact UTC time at least 1 second and at most 30 days after
the request is validated. The order cannot execute at that instant. When it is reached, the order
ends CANCELED with cancel reason GTD_EXPIRED. SBE carries the same time as expireAtTsNs in
nanoseconds.
Sizing and quote-budget market buys
Each order sets exactly one size: an exact base quantity, or a maximum quote amount to spend. The
quote budget is available only for BUY market orders and BUY limit IOC orders. A quote-budget buy
ends FILLED once what is left of its budget buys no further quantity step at the next price it
could fill at; if the book or its price limit stops it first, its remainder is canceled. See Order sizing for both modes and Preview order to check an order before you send
it.
Other order parameters have their own pages: Client order IDs, Fee Assets, and Self-trade prevention.
Order statuses
A create response confirms admission only. From then on the order reports one of these statuses:
| Status | Meaning | Terminal |
|---|---|---|
PENDING | Accepted and sent for execution, not yet on the book | No |
WORKING | On the book or matching. A partly filled order stays WORKING | No |
PENDING_CANCEL | A cancel was requested. The order can still fill until the cancel completes | No |
FILLED | The whole quantity executed, or a quote-budget buy spent its budget | Yes |
CANCELED | Ended with unfilled quantity. Fills before the cancel stand | Yes |
REJECTED | Refused by the market after admission; it never worked | Yes |
There is no separate expired or partially filled status. An expired GTD order is CANCELED with GTD_EXPIRED, and a partial fill shows as a WORKING order whose filled quantity is above zero.
Transitions
The usual paths are:
PENDINGtoWORKINGwhen the order reaches the book, then toFILLEDorCANCELED- an IOC, FOK, or market order never rests: it ends
FILLEDorCANCELEDas soon as it has traded what it can PENDINGtoREJECTEDwhen the market refuses it, for example a post-only order that would cross or a FOK order that cannot fill in fullWORKINGtoPENDING_CANCELafter a cancel request, then toCANCELED, or toFILLEDif it fills first
An IOC order that fills in part reports a fill, then ends CANCELED with IOC_REMAINDER. A
quote-budget buy that spends its budget ends FILLED instead, with no cancel. An order
that is replaced ends CANCELED with REPLACED, and its successor starts its own lifecycle under a
new order ID.
Why an order ended
A CANCELED order carries cancelReason. A cancel is not an error: the order ended, its fills
stand, and its remaining reserved funds are released.
USER_REQUEST
You canceled it with a cancel, a batch cancel, or a cancel-all.IOC_REMAINDER
The unfilled remainder of an immediate-or-cancel limit order.MARKET_REMAINDER
The unfilled remainder of a market order: the book ran out, the next price was past its boundary, or it reached the market's maximum order quantity.STP_EXPIRE_MAKER
Self-trade prevention canceled this resting order.STP_EXPIRE_TAKER
Self-trade prevention canceled this incoming order.STP_EXPIRE_BOTH
Self-trade prevention canceled both orders.ATTACHED_EXIT_FIRED
An attached take-profit, stop-loss, or trailing stop fired while the order still rested, so its unfilled remainder was canceled before the exit.REPLACED
A replace ended this order and created its successor.GTD_EXPIRED
A good-till-date order reached its expiry time.QTY_AT_OR_BELOW_FILLED
A modify set the total quantity at or below the filled quantity, so the order ended filled so far.CANCEL_ALL_AFTER
The dead-man switch expired.PAIR_DRAIN
The market was disabled and its resting orders were canceled.MARKET_HALTED
The market was halted and every open order was canceled.ATTACHED_RISK_REFUSED
The market refused one of the order's attached take-profit, stop-loss, or trailing legs.TWAP_ROLLOVER
A TWAP slice was canceled at the next slice's due time; its remainder moves to later slices.STRATEGY_CANCEL
An order placed by a trigger was canceled because its trigger ended or another leg took over.ACCOUNT_REPAIR
The exchange canceled it while recovering the account.SYSTEM
The exchange canceled it for a reason it does not name.
ConnectRPC uses the full Protobuf names, such as CANCEL_REASON_GTD_EXPIRED. REST and SBE use the
short names above.
A refusal happens at one of two points:
- At admission. The create call itself fails with an error code, such as
INSUFFICIENT_FUNDSorPRICE_TICK_SIZE. No order exists, so there is nothing to read back. - After admission. The create succeeded, then the market refused the order. The order ends
REJECTED, andrejectReasoncarries the same code the API returns for that refusal, such asPOST_ONLY_CROSS,FOK_INSUFFICIENT_LIQUIDITY, orPRICE_BAND_VIOLATION.
Every code is listed in the error catalogue.
Versions
Every order carries version, a per-order counter of its execution state. Version 0 is an order
accepted but not yet on the book; version 1 is its first book state, and each later execution-state
change increases it. A higher version is always newer, across REST, ConnectRPC, and the private
WebSocket. Keep the copy with the higher version when you merge a read with a stream update.
version does not cover the fee total (feeTotal), which is recorded after the fills settle, or
details a read adds on request. Two copies with the same version can differ in those fields, so
refresh them from a later read rather than by version.
See Order versions.
version counts changes of one order. A replacement starts a new order with its own versions; the
link between the two is the lineage described below.
Modify an order
ModifyOrder (POST /v1/orders/modify) changes an open order's price, total
quantity, client order ID, or attached take-profit, stop-loss, and trailing legs. Side, type, time in
force, and Pair cannot change; cancel the order and place a new one instead.
Name the order by orderId or clientOrderId, name its Pair, and send at least one change. The new
quantity is the order's new total quantity, including what has already filled, not the
remaining quantity.
Amend or replace
Polyester applies a modify in one of two ways:
- An amend changes the order in place. It keeps its
orderIdand its place in the queue. A modify is an amend when it keeps the price, does not raise the total quantity, and keeps the client order ID. - A replace cancels the order and creates a successor with a new
orderIdand reset queue priority. A new price, a larger total quantity, or a new client order ID needs a replace.
behavior chooses which ways are allowed:
behavior | Result |
|---|---|
AMEND_OR_REPLACE (default) | Amends when it can, otherwise replaces |
AMEND_ONLY | Amends, or rejects a change that needs a replace with MODIFICATION_REQUIRES_REPLACE |
REPLACE_ONLY | Always replaces |
A successor keeps the order's side, type, time in force, GTD expiry, post-only flag, and self-trade prevention mode, and takes over its attached legs. If the market refuses the successor, for example because a post-only order would now cross, the modify fails and the original order is unchanged.
A new total quantity at or below the filled quantity ends the order at its filled quantity. The
modify answers QTY_AT_OR_BELOW_FILLED and no successor is created.
An order that has ended can no longer be modified, including one whose remainder an attached exit
canceled (ATTACHED_EXIT_FIRED); the modify answers ORDER_ALREADY_TERMINAL.
Lineage
A replaced order and its successors form one logical order. Each order's lineage.id is the order ID
of the first generation, and lineage.generation counts from 1. A successor's cumQty includes
the fills of its predecessors, and inheritedCumQty shows how much it inherited. Use lineageId on
user trades to read the fills of the whole logical order.
Retries
requestId is required and identifies the modification, 1 to 64 characters from ASCII letters,
numbers, ., _, :, /, and -. Retry with the same requestId and the same body: Polyester
returns the original result for 60 seconds after it answered. The same requestId with a different
body is rejected with CONFLICT_IDEMPOTENCY_KEY_REUSE. Use a new requestId for every new
modification.
The response reports actionTaken (AMENDED or REPLACED), oldOrderId, and finalOrderId, the
order to track from now on. For an amend, both IDs are the same.
REST takes the new price as a decimal string in newPrice. The new quantity is newQtyScaled, a
scaled integer even over REST. A newPrice with a nonzero digit beyond 9 fractional digits answers 400 PRICE_TICK_SIZE with a newPrice violation; trailing zeros are accepted.
{
"orderId": "2NEpo7TZRRrLZSi2U",
"symbol": "BTC-USDT",
"newPrice": "64250.5",
"requestId": "mod-7f3c-1"
}Call /orders.v1.OrdersService/ModifyOrder with the order ID as a decimal string and scaled
integers (see Scaled integers):
{
"orderId": "1234567890123456789",
"symbolId": 1,
"newPriceTicks": "64250500000000",
"behavior": "AMEND_OR_REPLACE",
"requestId": "mod-7f3c-1"
}| Error | What to do |
|---|---|
ORDER_PLACEMENT_PENDING | The order or its replacement is not on the book yet. Retry shortly |
ORDER_MODIFY_IN_PROGRESS | An earlier modify of the order has not completed. Retry once it completes |
MODIFICATION_REQUIRES_REPLACE | The change needs a replace and behavior is AMEND_ONLY |
ORDER_MODIFY_OUTCOME_UNKNOWN | The modify may still apply. Read the order, then retry with the same requestId if needed |
A Pair in cancel-only mode still accepts amends but refuses replaces. To modify many orders of one Pair at once, use batch replace.
Cancel an order
CancelOrder (POST /v1/orders/cancel) names one order by orderId or clientOrderId. The answer ACCEPTED means cancellation was requested: the order moves to PENDING_CANCEL and can still fill
until it ends CANCELED or FILLED. A cancel never applies twice, so repeating it is safe: a repeat
while the first cancel is in progress answers ACCEPTED again, and a cancel of an order that has
already ended answers ORDER_ALREADY_TERMINAL. A client order ID keeps naming its order for at least
a minute after the order ends; later, a cancel by client order ID may answer ORDER_UNKNOWN, so
retry by orderId or read the order. ORDER_CANCEL_OUTCOME_UNKNOWN means the cancel may still apply;
read the order before retrying.
To cancel many orders, use batch cancel, Cancel all orders, or the dead-man switch.
Read orders and fills
- Pending and working orders:
GET /v1/orders/openorGetOpenOrders - Filled, canceled, and rejected orders:
GET /v1/orders/historyorGetOrderHistory - One order with its recent fills:
GET /v1/spot/orders/{order_id}orGetOrder - Your fills:
GET /v1/spot/tradesorGetUserTrades
All lists return newest first and page with an opaque pageToken; limit is 1 to 1000, default 100.
A history page can hold fewer orders than requested, or none, while more remain, so keep paging while nextPageToken is not empty.
GetOrderover ConnectRPC also acceptsclientOrderIdand returns the latest order with that ID. REST looks up an order byorderIdonly.- Right after a create,
GetOrderwaits briefly for the order to become readable. If it still is not, it answers a retryable unavailable error instead of not found; retry the same lookup. - An order shows a fill as soon as it executes, and its trades are recorded shortly after. In
GetOrder,executionsCompleteistrueonce the order's trades through its generation, across every page of execution history, add up exactly to its filled quantity. While it isfalse, the trades list the executions recorded so far and a later read returns the rest. It is alwaysfalsewhen execution history is not included. avgPxis the exact average execution price of the logical order.cumQuoteis the quote amount of its fills before fees. History reads andGetOrderwith execution history (the default) also carryfeeTotal, the net fee so far; it can trail the fills until itsqtyequalscumQty.- User trades filter by
orderIdfor one physical order, or bylineageIdfor the whole logical order across replacements. - Attached take-profit, stop-loss, and trailing legs are included only when you set
includeAttachedRiskorincludeAttachedRiskState.
The lifecycle on each interface
Every interface answers a command at admission and reports the rest of the lifecycle separately:
| Interface | Command answer | Lifecycle updates |
|---|---|---|
| REST | HTTP response | Private order updates, or the read endpoints above |
| ConnectRPC | RPC response | The same private WebSocket in Protobuf or JSON, or OrdersReadService |
| SBE WebSocket | CommandAck, batch acks, or CommandReject | OrderEvent and ExecutionEvent on the same connection; see Responses and events |
The private WebSocket sends each changed order complete, with events such as ACCEPTED, FILLED, CANCELED, REJECTED, and REPLACED. SBE OrderEvent carries the status, quantities, rejectReason, and cancelReason of each change. Order entry over SBE is described in Order entry.
orderId through the private stream or the read endpoints.