The server sends two kinds of messages: answers, exactly one per command you send, and private events, which you receive only after subscribing. Both arrive on the same connection.
Answers
Ping· successPong(22) · failureCommandReject(21)Subscribe· successSubscriptionAck(23) · failureCommandReject(21)CreateOrder,CancelOrder,ModifyOrder,CancelAllOrders· successCommandAck(20) · failureCommandReject(21)BatchCreateOrders· successBatchCreateAck(26) · failureCommandReject(21)BatchReplaceOrders· successBatchReplaceAck(27) · failureCommandReject(21)BatchCancelOrders· successBatchCancelAck(28) · failureCommandReject(21)
Every answer echoes the command's seq and correlationId. A CommandReject for a frame that
could not be decoded carries 0 in both. CommandAck.commandType names the command it answers.
A batch acknowledgement can contain rejected items; a CommandReject for a batch means no item
result is available. Order entry describes what
each acknowledgement contains, and Errors and rate limits what each reject
code means.
Subscribe to private events
Subscribe sets the complete list of event channels for the connection. Each Subscribe replaces
the previous one, and 0 turns all events off.
| Channel and bit | Delivers |
|---|---|
executions · bit 0 | ExecutionEvent for each fill of your orders |
orders · bit 1 | OrderEvent for each state change of your orders |
marketResync · bit 2 | MarketResync instead of a 1012 close when a Pair's events may be missing |
marketResync takes effect only together with executions or orders.
SubscriptionAck.channels confirms the set now active. The change is atomic: events written before
the acknowledgement follow the previous set, events after it follow the new one.
Events cover every order of the account the session is bound to, whichever interface placed it,
including orders placed by triggers. They start when you subscribe: there is no snapshot or replay
of earlier events. Load current state first, for example with GET /v1/orders/open, then apply events.
ExecutionEvent
One ExecutionEvent reports one fill of one of your orders. The same execution can be sent twice:
first with settlementStatus PENDING as soon as it matches, then with SETTLED once financial
settlement is final.
| Field | Use |
|---|---|
symbolIdorderId | The Pair and your order |
matchId | Trade ID, increasing within the Pair. Deduplicate on symbolId, matchId, and orderId |
matchSeq | Execution sequence within the Pair. Gaps are expected: you see only your own executions |
sideliquidityRole | Your side, and whether you were MAKER or TAKER |
fillQtyScaledfillPriceTicks | Quantity and price of this fill |
remainingQtyScaled | Your order's remaining quantity right after this fill; null on SETTLED notifications |
feeAsset | The fee Asset. The fee amount is not included; read it from trade history after settlement |
matchTsNs | Execution time |
gatewayTsNs | When this message was encoded. Subtract matchTsNs from it to estimate server-side delivery latency |
Act on PENDING for trading decisions. Use SETTLED, or trade history, for accounting. A SETTLED execution can reach trade history shortly afterward, so read again when it is not there yet. See Wait for history to catch up.
OrderEvent
One OrderEvent reports one state change of one of your orders.
| Field | Use |
|---|---|
symbolIdorderId | The Pair and your order |
version | Starts at 1 and increases with each change. Ignore an event whose version is not newer |
status | State after this change |
remainingQtyScaled | Unexecuted quantity; 0 in a terminal state |
cumulativeQtyScaled | Total executed quantity across the order's lineage, including fills of the orders it replaced |
rejectReason | For REJECTED: the same code the Orders API returns for that refusal; 0 otherwise |
cancelReason | For CANCELED: why the order ended, for example USER_REQUEST, IOC_REMAINDER, REPLACED, or GTD_EXPIRED; 0 otherwise |
timeInForceexpireAtTsNs | The order's time in force and, for GTD, its expiry |
eventTsNsgatewayTsNs | When the change happened and when this message was encoded |
This interface sends the statuses PENDING, PENDING_CANCEL, WORKING, FILLED, CANCELED, and REJECTED. The schema's PENDING_REPLACE status is never sent here. FILLED, CANCELED, and REJECTED are terminal. An order still executes while it is PENDING_CANCEL. A replaced order ends CANCELED with cancelReason REPLACED, and its successor
reports its own events under its new orderId.
A gap in an order's versions means you missed a change of that order. Read the order with GET /v1/spot/orders/{order_id} (ConnectRPC GetOrder).
Ordering
The server keeps three guarantees on each connection:
- Answers arrive in the order you sent the commands.
- The answer to a command arrives before any event that command caused. After you read a create's
CommandAck, you know itsorderIdbefore its firstOrderEvent. - Events arrive in the order the server received them.
Between an answer and an event the command did not cause, there is no guaranteed order. An event of an existing order can arrive while a later command is still unanswered.
Recovery
Events are a live feed, not the durable record. The server tells you when you may have missed some, and you recover by reading the durable state over REST or ConnectRPC.
MarketResync or a 1012 close
When the server detects that private events of a Pair may have been missed, of any account, it acts on every connection that subscribes to events:
- With
marketResyncenabled, it sendsMarketResyncfor that Pair and keeps the connection open. Events of every other Pair are unaffected. - Without it, it closes the connection with
1012.
MarketResync can arrive for a Pair you do not trade, and one interruption can send it more than
once. Its reason is INTERRUPTED or DISCONTINUITY; the recovery is the same for both. When
only your account's events may be missing, or event delivery is unavailable, the connection closes
with 1012 even with marketResync enabled.
Mark the Pair as uncertain
On MarketResync, keep applying events of other Pairs. Hold decisions that depend on your state in
the named Pair.
Read the Pair's state
Read your open orders and recent executions on that Pair over REST or ConnectRPC.
Apply later events
Apply the Pair's events that arrive after the MarketResync, in order. They may repeat state your
read already returned: skip order events whose version is not newer, and duplicate executions.
After a 1012 close, follow Reconnect and
reconcile every Pair you trade.
Market halts
Halts are exceptional: the exchange halts a market only to protect its integrity. A halt cancels
every open order of the Pair with cancel reason MARKET_HALTED (17) and ends its triggers. Fills
before the cancel stand, and the market refuses new orders until it trades again.
The halt's individual cancels are not pushed. At the halt, a session receives MarketResync with
reason INTERRUPTED for the Pair, or a 1012 close without marketResync; reconcile the Pair as
above. An order the halted market refuses after admission arrives as an OrderEvent with status REJECTED and rejectReason MARKET_HALTED (56), even though its CreateOrder was acknowledged.
Slow readers
Each connection holds a bounded queue of events waiting to be written to you. It absorbs bursts,
such as the order events of a large cancel-all. If you stop reading, the queue fills and the server
closes the connection with 1008 (private event queue overflow). Read the socket on a dedicated
thread or task and hand messages to your strategy through your own queue.