Skip to main content

Endpoint

Each connection is scoped to exactly one subaccount. To watch orders across multiple subaccounts, open one connection per subaccount. The server pushes a snapshot of currently-open orders on connect, then a frame every time one of that subaccount’s orders changes user-visible status.

Query parameters

ParameterRequiredDefaultDescription
subaccount_idyesUUID of the subaccount whose orders you want to stream. Must belong to the authenticated user.
extra_informationnofalseWhen true, every snapshot row and order frame additionally carries parent_iceberg_order_id, expiry_ts_utc, and complex_order_ids (TP/SL bucket). Useful for rendering full order rows without a REST round-trip.
key_id / ts / sigyes (programmatic)Ed25519 signed-handshake params. See Authentication.
access_tokenyes (browser)Supabase JWT for first-party web clients.

Authentication

Browsers can’t set custom headers on the WebSocket handshake, so the signed-request flow moves to query params. The signature covers WS\nPATH\nSORTED_QUERY (excluding key_id/ts/sig)\nTIMESTAMP.
The official SDKs sign the handshake for you. The raw recipe below is for integrations without an SDK.
Signed handshake
The authenticated user must own 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

  • connected is sent immediately after the handshake completes.
  • snapshot follows once, listing orders currently in pending, resting, or partially_filled for this subaccount. The client always sees state, then deltas — never the other way around.
  • order frames deliver every user-visible status transition. The internal processing status is never forwarded.
  • reconnect is sent during graceful pod shutdown. Reconnect with a small jitter.

Statuses

StatusMeaning
pendingAccepted by the API; not yet on the exchange book.
restingLive on the exchange book. Might have traded some quantity.
pending_amendA PATCH /v1/orders/{id} edit request is in flight on the exchange. Transitions back to resting (or a terminal state) once the exchange responds.
partially_filledSome quantity has traded; the rest is cancelled.
executedFully filled. Terminal.
cancelledCancelled before full execution. Terminal.
rejectedRejected by the exchange or risk checks. Terminal.
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):
When connected with ?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 (Python websockets, browser WebSocket) reply to server PINGs automatically — no application-level heartbeat is needed.

Minimal client

Using the SDK (handles signing automatically):
Or signing the handshake manually:

Errors

Recoverable problems arrive as an error frame followed by a close. code is stable and safe to branch on; message is human-readable.
codeWhen
unauthorizedAPI key / JWT was missing or invalid.
missing_subaccount_id?subaccount_id= was not supplied on the handshake.
invalid_subaccount_idsubaccount_id is not a valid UUID.
subaccount_forbiddenThe subaccount does not belong to the authenticated user.
snapshot_failedInitial open-orders snapshot could not be loaded; the connection stays open and live updates resume.

Close codes

CodeMeaning
1000Normal closure
1011Server-side overflow (client couldn’t keep up with the send rate)
4400Bad request (missing/invalid subaccount_id)
4401Missing or invalid authentication, or subaccount does not belong to the user
4503Server draining for deploy — reconnect with jitter