> ## Documentation Index
> Fetch the complete documentation index at: https://docs.noise.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Streams

> The WebSocket: authentication, topics, and sequence gaps.

The `/ws` endpoint carries live order, position, balance, and market updates. The socket can also place and cancel orders, using the same payloads and the same verification path as HTTP.

## Connecting

The handshake is challenge-response, so a captured auth frame cannot be replayed onto a different connection.

<Steps>
  <Step title="The server challenges">
    On connect the server sends `{ "type": "challenge", "nonce": "…" }`.
  </Step>

  <Step title="The client authenticates">
    Send an `auth` frame whose signature is the HMAC over `timestamp + GET + /ws + nonce`, where
    the challenge nonce takes the place of the request body and binds the signature to this
    connection.
  </Step>

  <Step title="The server acknowledges">
    The server replies `auth:ack` with `success`. Subscription follows.
  </Step>
</Steps>

```ts theme={null}
import { createHmac } from "node:crypto";

const ws = new WebSocket(`wss://<host>/ws`);

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  if (msg.type === "challenge") {
    const timestamp = String(Math.floor(Date.now() / 1000));
    const signature = createHmac("sha256", API_SECRET)
      .update(`${timestamp}GET/ws${msg.nonce}`)
      .digest("hex");

    ws.send(JSON.stringify({ type: "auth", apiKey: API_KEY, timestamp, signature }));
  }

  if (msg.type === "auth:ack" && msg.success) {
    ws.send(JSON.stringify({ type: "subscribe", topic: "orders" }));
  }
};
```

Where the key is IP-restricted, the allowlist applies here too. Revoking a key closes any open socket with code **4001**, and reconnecting with a revoked key fails authentication.

## Topics

Subscription is explicit, so nothing is streamed until it is requested. Send `{ "type": "subscribe", "topic": "…" }` and expect a `subscribe:ack`.

| Topic             | Scope   | Carries                               |
| ----------------- | ------- | ------------------------------------- |
| `orders`          | Private | The authenticated wallet's orders.    |
| `positions`       | Private | The authenticated wallet's positions. |
| `balances`        | Private | The authenticated wallet's balances.  |
| `asset:{assetId}` | Public  | One market.                           |
| `assets:general`  | Public  | All markets.                          |

Private topics are filtered to the wallet behind the credential; no other wallet's stream is reachable.

## Sequence gaps

Every `data` frame carries a `seq` that is monotonic **per connection, per topic**:

```json theme={null}
{ "type": "data", "topic": "orders", "seq": 128, "data": { } }
```

<Warning>
  A gap in `seq` means data was missed. Resubscribe and reconcile against the REST endpoints before
  quoting again. Do not interpolate, and do not continue trading on state known to be incomplete.
</Warning>

Track `seq` per topic rather than globally, and reset the expected value on reconnect.

## Liveness

`system:heartbeat` arrives about every 5 seconds with `serverTime` and the current trading `status`. A missed heartbeat means reconnect.

`system:status` fires on a transition between `active` and `paused`. Pair it with the [halt behaviour](/trader-api/orders#when-trading-is-halted): submissions fail while paused, cancels keep working.

## Trading over the socket

`place` and `cancel` frames accept the same payloads as their HTTP counterparts (up to 20 orders and 50 cancels respectively) and reply with per-item results.

```json theme={null}
{ "type": "place", "id": "my-batch-1", "orders": [ ] }
```

The optional `id` is echoed back on `place:result` and `cancel:result`, which allows replies to be correlated with in-flight requests. Orders must still be signed exactly as described in [Orders](/trader-api/orders); the socket saves a round trip, not a signature.

<Info>
  Rate limits are keyed to the credential rather than to the connection, so a socket does not carry
  a budget of its own. See [Errors and Limits](/trader-api/errors).
</Info>
