> For the complete documentation index, see [llms.txt](https://dev.strixlab.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dev.strixlab.io/typescript-sdk/websockets.md).

# WebSocket Streams

Everything real-time shares **one connection** to `/ws`, opened lazily the first time you touch `client.ws`. Reconnects, re-subscriptions and re-authentication are handled for you.

<table><thead><tr><th width="230">Channel</th><th width="120">Auth</th><th>Gives you</th></tr></thead><tbody><tr><td><code>client.ws.orderbook</code></td><td>Public</td><td>Snapshots, deltas, last trade price, market status, mid prices.</td></tr><tr><td><code>client.ws.trades</code></td><td>Public</td><td>Every executed trade on the tokens you subscribe to.</td></tr><tr><td><code>client.ws.user</code></td><td>HMAC</td><td>Your order updates, settled fills, position deltas, portfolio summary.</td></tr></tbody></table>

## The live order book

```ts
const unsubscribe = client.ws.orderbook.subscribe([yesTokenId, noTokenId], {
  depth: 20,

  onSnapshot: ({ marketId, levels, ts }) => {
    console.log('full book', levels.bids, levels.asks);
  },

  onDelta: ({ marketId, side, price, size }) => {
    // side: 'bid' | 'ask' · size === 0 means the level is gone
  },

  onMid: ({ yesMid, noMid, bestBid, bestAsk }) => {
    console.log(`YES ${yesMid} · NO ${noMid}`);
  },

  onLastPrice:    ({ price, size }) => console.log('last trade', price, size),
  onStatusUpdate: ({ status })      => console.log('market status', status),
});

// later
unsubscribe();
```

The server sends one **snapshot** on subscribe and **deltas** thereafter. The SDK applies deltas to its cached book for you, so `onSnapshot` always hands you a complete, current book — you don't have to maintain one yourself.

{% hint style="info" %}
**Delta timing.** The venue coalesces book changes and flushes level deltas roughly every 150 ms, hold-aware — you get the net change per price level, not one message per order. Expect batched movement rather than a message per event.
{% endhint %}

### Reading the cache synchronously

Inside a quoting loop you usually want the current book *now*, with no request and no callback:

```ts
const book = client.ws.orderbook.getBook(tokenId);   // null until the first snapshot
const { yesMid, noMid, bestBid, bestAsk } = client.ws.orderbook.getMid(tokenId);
```

Unlike `orderbook.midpoint()` over REST, `getMid()` returns `null` fields on a one-sided book rather than throwing.

### Subscription mechanics

* **Reference counted.** Subscribing twice to the same token opens one server subscription; the last `unsubscribe()` closes it.
* **Depth is per token, deepest wins.** One socket serves every subscriber, so a `depth: 200` subscriber and a `depth: 20` subscriber share the deeper stream. Depth is replayed verbatim on reconnect.
* **New subscribers get the cached snapshot immediately**, before the server responds.
* No practical limit on how many tokens you subscribe to on one connection.

## The public trade feed

```ts
const unsubscribe = client.ws.trades.subscribe([tokenId], (trade) => {
  console.log(`${trade.side} ${trade.size} @ ${trade.price}`);
});
```

`side` is the **taker's** side — a `BUY` consumed resting asks. This is the feed to watch if you want to know which way flow is going, rather than just where the book sits.

## Your orders and fills

Requires credentials. Connect once, then register handlers:

{% code title="user-channel.ts" %}

```ts
client.ws.user.connect();

client.ws.user.on('order.update', (o) => {
  // status: OPEN | MATCHED | PARTIALLY_FILLED | FILLED | CANCELLED | EXPIRED
  console.log(`order ${o.id} → ${o.status}  filled ${o.filledQuantity}, ${o.remaining} left`);
});

client.ws.user.on('trade.settled', (t) => {
  console.log(`✅ ${t.side} ${t.quantity} ${t.outcomeName} @ ${t.price} · tx ${t.txHash}`);
});

client.ws.user.on('trade.failed', (f) => {
  console.error(`❌ settlement failed: ${f.reason} (${f.side} ${f.quantity})`);
});

client.ws.user.on('position.update', (p) => {
  console.log(`position ${p.sharesDelta > 0 ? '+' : ''}${p.sharesDelta} @ ${p.averagePrice}`);
});

client.ws.user.on('portfolio.summary_update', (s) => {
  console.log(`equity ${s.totalValue} · free ${s.availableBalance} · pnl ${s.totalPnl}`);
});

client.ws.user.on('error', (e) => {
  console.error('server error frame:', e.message);   // e.raw has the whole message
});
```

{% endcode %}

`on()` returns an unsubscribe function; there's also `client.ws.user.off(event, handler)`. Read the live state with `client.ws.user.connectionState` (`'disconnected' | 'connecting' | 'connected' | 'authenticated'`).

### The lifecycle you'll observe

{% stepper %}
{% step %}

#### `order.update` → `OPEN`

Your order is on the book. This fires even for an order that will cross.
{% endstep %}

{% step %}

#### `order.update` → `MATCHED`

The engine matched it. **This status exists only on the socket** — you will never see it over REST, where the matched quantity shows up as `holdQuantity` instead. Nothing is final yet.
{% endstep %}

{% step %}

#### `trade.settled` + `order.update` → `FILLED` / `PARTIALLY_FILLED`

The on-chain settlement confirmed, \~15–20 s after the match. `trade.settled` carries the transaction hash. A `position.update` arrives alongside it as an optimistic delta, ahead of on-chain indexing.
{% endstep %}

{% step %}

#### `trade.failed` (rare)

The settlement transaction did not confirm. The order returns to the book or is cancelled; your balance is untouched. Log these — a run of them means something is wrong upstream.
{% endstep %}
{% endstepper %}

{% hint style="warning" %}
**Register an `error` handler.** Failed HMAC authentication arrives as an error frame on the socket, not as a thrown exception. Without a handler the SDK falls back to `console.error`, which is easy to miss in a long-running process — and every user event silently never arrives.
{% endhint %}

## Reconnects

The connection reconnects automatically with backoff. On reconnect the SDK re-sends every live order-book and trades subscription at its tracked depth, and re-authenticates the user channel.

{% hint style="info" %}
**Deltas can be missed across a gap.** The server sends a fresh snapshot on resubscribe, so the SDK's cache re-bases itself. If you keep your own derived state (a ladder, an inventory model), rebuild it from `onSnapshot` rather than assuming continuity.
{% endhint %}

## Shutting down

```ts
process.on('SIGINT', async () => {
  await client.orders.cancelAll();
  client.destroy();          // closes the socket, clears reconnect timers
  process.exit(0);
});
```

Without `destroy()` the open socket and its timers keep the Node process alive.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://dev.strixlab.io/typescript-sdk/websockets.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
