Endpoint
Query parameters
Authentication
Browsers can’t set custom headers on the WebSocket handshake, so the signed-request flow moves to query params. The signature coversWS\nPATH\nSORTED_QUERY (excluding key_id/ts/sig)\nTIMESTAMP.
Signed handshake
subaccount_id — otherwise the connection is closed with code 4401. A missing or malformed subaccount_id is closed with 4400.
Protocol
All frames are JSON. Server frames are bytes (orjson-serialized UTF-8). Client frames are accepted but ignored — there is no subscribe/unsubscribe action; the subaccount is fixed at handshake time.Server → client
connectedis sent immediately after the handshake completes.snapshotfollows once, listing orders currently inpending,resting, orpartially_filledfor this subaccount. The client always sees state, then deltas — never the other way around.orderframes deliver every user-visible status transition. The internalprocessingstatus is never forwarded.reconnectis sent during graceful pod shutdown. Reconnect with a small jitter.
Statuses
Iceberg child tranches arrive as ordinary
order frames — clients can’t tell them apart from user-placed simple orders.
Order payload
Default fields (always sent):?extra_information=true, every row additionally carries:
average_price is null until the first fill. traded_qty and fees_paid default to 0. complex_order_ids lists attached take-profit / stop-loss conditional-order ids by side; both buckets are empty when nothing is attached. order_type and time_in_force are the canonical enum names (LIMIT, MARKET, GTC, IOC, FOK, …).
Keepalive
Liveness is handled at the WebSocket protocol layer (RFC 6455 PING/PONG control frames). Standard clients (Pythonwebsockets, browser WebSocket) reply to server PINGs automatically — no application-level heartbeat is needed.
Minimal client
Using the SDK (handles signing automatically):Errors
Recoverable problems arrive as anerror frame followed by a close. code is stable and safe to branch on; message is human-readable.

