> For the complete documentation index, see [llms.txt](https://docs.tread.fi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tread.fi/interacting-with-the-api/websocket-reference.md).

# Websocket Reference

Tread exposes WebSocket endpoints for real-time order updates and market data. All endpoints use the `wss://` scheme in production.

### Endpoints

| Endpoint                    | Purpose                   | Authentication |
| --------------------------- | ------------------------- | -------------- |
| `wss://<server>/ws/orders/` | User order updates        | Required       |
| `wss://<server>/ws/prices/` | Pair prices & order books | Not required   |

***

### `/ws/orders/` — Order Stream

Streams real-time updates for the authenticated user's orders, including standalone orders, multi-orders (TWAP, market maker, etc.), chained orders, and batch orders. Also surfaces orders the user can view via trading-group permissions.

#### Authentication

Two methods are supported. Pick one:

**1. Query parameter (recommended for API clients)**

```
wss://<server>/ws/orders/?token=<API_token>
```

If a token is provided but invalid, the server closes the connection with code `4001`.

**2. Post-connect message**

Connect without credentials, then send:

```json
{ "command": "authenticate", "token": "<DRF_token_or_JWT>" }
```

Both DRF tokens and JWT tokens (when `EXTERNAL_JWT` is enabled) are accepted.

On success:

```json
{ "type": "authenticated", "user_id": "<id>" }
```

On failure the server replies with an `error` message and closes with code `4001`. Unauthenticated connections cannot send any command other than `authenticate` and will be closed by the inactivity timer (\~30s).

#### Client → Server commands

**Subscribe**

```json
{ "command": "subscribe", "data_type": "user_orders" }
```

The user ID is taken from the authenticated session; no user ID is required in the message.

Immediately after subscribing, the server sends a one-time `order_snapshot` message containing all currently active orders (see below).

**Unsubscribe**

```json
{ "command": "unsubscribe", "data_type": "user_orders" }
```

**Keep-alive**

```json
{ "command": "keep_alive" }
```

Must be sent within the inactivity window (\~30 seconds) to keep the subscription alive. The server replies with:

```json
{ "type": "keep_alive" }
```

#### Server → Client messages

**`order_snapshot`**

Sent once, right after a successful `subscribe`. Contains the full set of currently active orders, partitioned by type:

```json
{
  "type": "order_snapshot",
  "data": {
    "orders": [ /* standalone orders */ ],
    "multi_orders": [ /* multi-orders with child orders */ ],
    "chained_orders": [ /* chained orders with children */ ],
    "batch_orders": [ /* batch orders with children */ ]
  }
}
```

**`order_update`**

Sent whenever an order changes state or receives a fill. Shape varies by order type.

**Standalone single order:**

```json
{
  "type": "order_update",
  "data": {
    "order_id": "<id>",
    "update_type": "order_created" | "order_activated" | "order_canceled" | "order_completed" | "order_fill_received" | "order_paused",
    "order": { /* order detail */ }
  }
}
```

**Multi-order (parent-level event):**

```json
{
  "type": "order_update",
  "data": {
    "order_id": "<multi_order_id>",
    "update_type": "order_created" | "order_activated" | "order_canceled" | "order_completed" | "order_paused",
    "order": { /* multi-order detail with child orders */ }
  }
}
```

**Multi-order (child event bubbled up to parent):**

```json
{
  "type": "order_update",
  "data": {
    "order_id": "<multi_order_id>",
    "update_type": "child_order_created" | "child_order_activated" | "child_order_canceled" | "child_order_completed" | "child_order_fill_received",
    "child_order_ids": ["<id>", "..."],
    "order": { /* multi-order detail with child orders */ }
  }
}
```

**Chained order (child event):**

```json
{
  "type": "order_update",
  "data": {
    "order_id": "<chained_order_id>",
    "update_type": "child_order_*",
    "order_type": "chained_order",
    "child_order_ids": ["<id>", "..."],
    "order": { /* chained-order detail */ }
  }
}
```

**Batch order (child event):**

```json
{
  "type": "order_update",
  "data": {
    "order_id": "<batch_order_id>",
    "update_type": "child_order_*",
    "order_type": "batch_order",
    "child_order_ids": ["<id>", "..."],
    "order": { /* batch-order detail */ }
  }
}
```

Notes:

* Fill broadcasts (`order_fill_received` / `child_order_fill_received`) are debounced server-side: rapid fills for the same order produce at most one broadcast per \~1 second.
* Status-change events (created, activated, canceled, completed, paused) are sent immediately.

**`order_error`**

Sent when an order encounters a runtime error (e.g., exchange rejection). Same envelope as `order_update`:

```json
{ "type": "order_error", "data": { /* error detail */ } }
```

**`error`**

Protocol-level errors:

```json
{ "type": "error", "error": "not_authenticated", "message": "Please authenticate first using the 'authenticate' command" }
```

```json
{ "type": "error", "error": "authentication_failed", "message": "<reason>" }
```

After an `authentication_failed` error the server closes with code `4001`.

#### Close codes

| Code   | Meaning                         |
| ------ | ------------------------------- |
| `1000` | Normal closure                  |
| `1011` | Internal server error           |
| `4001` | Authentication failure (custom) |
| `4002` | Inactivity timeout (custom)     |

***

### `/ws/prices/` — Price & Order Book Stream

Streams live pair prices and order book snapshots from supported exchanges. No authentication required.

#### Inactivity

The server requires a `keep_alive` message every \~10 seconds; otherwise the connection is closed.

```json
{ "command": "keep_alive" }
```

#### Client → Server commands

**Subscribe to a pair price**

```json
{
  "command": "subscribe",
  "data_type": "pair_price",
  "exchange": "<exchange_name>",
  "pair": "<pair_name>"
}
```

Only one `pair_price` subscription is active at a time per connection. Subscribing again replaces the previous subscription.

**Subscribe to an order book**

```json
{
  "command": "subscribe",
  "data_type": "order_book",
  "exchanges": ["<exchange1>", "<exchange2>"],
  "pair": "<pair_name>"
}
```

Note `exchanges` (plural, array) — you can subscribe to the same pair across multiple exchanges in one command.

**Unsubscribe**

Same shape as `subscribe` but with `"command": "unsubscribe"`.

#### Server → Client messages

**`price_update`**

Sent on every price change, and once immediately after subscribing (initial snapshot).

```json
{ "type": "price_update", "price": "<decimal_string>" }
```

**`order_book_update`**

```json
{
  "type": "order_book_update",
  "exchange": "<exchange_name>",
  "book": { /* chart-formatted order book */ }
}
```

An initial snapshot is sent per-exchange right after subscribing, followed by live updates.

#### Close codes

| Code   | Meaning                     |
| ------ | --------------------------- |
| `1000` | Normal closure              |
| `1011` | Internal server error       |
| `5000` | Inactivity timeout (custom) |

***
