Session Stream
/v1/stream is a single multiplexed socket that does everything the dedicated
sockets do on one connection: place, cancel, and modify orders, and receive
order, fill, and tree-append pushes. You authenticate in-band with an
op: login frame (not a ?token= query param), then subscribe to the channels
you want.
When to use it
The dedicated sockets (trading, orders,
fills) each do one job and authenticate at the upgrade with
?token=. The session stream folds all of them into one connection, which is the
simpler choice for a full client that submits orders and consumes events at the
same time. The dedicated sockets remain available if you prefer one socket per
concern.
Connect and log in
wss://<gateway-host>/v1/stream
The socket upgrades unauthenticated. Every op except ping is rejected until
a successful login:
{ "op": "login", "token": "<access_token>", "cancel_on_disconnect": true }
| Field | Required | Description |
|---|---|---|
op | Yes | "login". |
token | Yes | A bearer token from POST /auth/token. |
cancel_on_disconnect | No | If true, the engine cancels all of this account's open orders when the socket drops. Recommended for market makers. |
To refresh an expiring token, send another login on the same socket without
dropping your subscriptions. The server emits an auth_expired reminder about
60 seconds before expiry.
Subscribe to channels
{ "op": "subscribe", "channels": ["orders", "fills", "tree"] }
| Channel | Scope | What it pushes |
|---|---|---|
orders | Per-account | Your order-lifecycle events (placed, partial, filled, cancelled, expired). Same payloads as the Orders Channel. |
fills | Per-account | Your continuation-fill memos. Same payloads, and the same verify-before-store rule, as the Fills Channel. |
tree | Global | Leaf-append events, for keeping an incremental view of the note tree (for example, live portfolio state). |
unsubscribe takes the same channels array. Each server push is tagged with a
top-level channel field so you can route it.
Submit orders
The order ops dispatch to the same intake and verification as
POST /orders and the trading socket, so
the bodies are identical and an order-level trading-key signature is still
required on every frame. Each carries a request_id the server echoes in its
reply; later state changes arrive on the orders channel.
{ "op": "order.place", "request_id": "r1", "params": { /* a Place Order body */ } }
{ "op": "order.cancel", "request_id": "r2", "order_id": "aa00…01" }
{ "op": "order.modify", "request_id": "r3", "order_id": "aa00…01",
"cancel_signature": "…", "cancel_nonce": 2, "replacement": { /* a Place Order body */ } }
order.modify is an atomic cancel-and-replace under one matcher lock, the same as
PUT /orders/{order_id}.
Heartbeat
Send { "op": "ping" } at least every 30 seconds; the server replies
{ "op": "pong" }. A session silent for more than 60 seconds is dropped.
What is not served here
account and settlement are not channels on this socket. Account state is
reconstructed client-side from the tree channel (or the /tree/*
endpoints) plus your keys, never delivered as a balance (see
Account Model); settlement status is read with
GET /settlement/status/{batch_id}.