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

# WebSocket

> Subscribe to live prices, positions, orders, and AI decisions over a single WebSocket connection.

The Hyperoru WebSocket API streams the same data the web app uses in real time. Open one connection, subscribe to the channels you care about, and receive updates as they happen -- no polling required.

<Snippet file="base-url.mdx" />

## Connect

Production endpoint:

```
wss://api.production.hyperoru.com/ws
```

You must authenticate with a session token. Currently the server accepts the token as a query parameter (browsers do not let you set custom headers on WebSocket upgrades):

```
wss://api.production.hyperoru.com/ws?session_token=YOUR_SESSION_TOKEN
```

<CodeGroup>
  ```javascript JavaScript theme={null}
  const ws = new WebSocket(
    `wss://api.production.hyperoru.com/ws?session_token=${token}`
  );

  ws.addEventListener("open", () => {
    ws.send(JSON.stringify({
      type: "subscribe",
      request_id: "1",
      data: { channels: ["trades", "positions", "asset_curve"] },
    }));
  });

  ws.addEventListener("message", (e) => {
    const msg = JSON.parse(e.data);
    console.log(msg.type, msg.data);
  });
  ```

  ```python Python theme={null}
  import asyncio, json, websockets

  async def main(token: str):
      url = f"wss://api.production.hyperoru.com/ws?session_token={token}"
      async with websockets.connect(url) as ws:
          await ws.send(json.dumps({
              "type": "subscribe",
              "request_id": "1",
              "data": {"channels": ["trades", "positions", "asset_curve"]},
          }))
          async for raw in ws:
              msg = json.loads(raw)
              print(msg["type"], msg.get("data"))

  asyncio.run(main("YOUR_SESSION_TOKEN"))
  ```
</CodeGroup>

## Message protocol

Every message -- client or server -- is a JSON object with a `type` field.

```json theme={null}
{
  "type": "subscribe",
  "request_id": "optional-correlation-id",
  "data": { }
}
```

| Field        | Direction        | Purpose                                                             |
| ------------ | ---------------- | ------------------------------------------------------------------- |
| `type`       | both             | Identifies the kind of message.                                     |
| `request_id` | client -> server | Optional. Echoed back by the server so you can correlate responses. |
| `data`       | both             | Payload. Shape depends on `type`.                                   |

## Client messages

<AccordionGroup>
  <Accordion title="subscribe - join one or more channels" defaultOpen>
    Subscribe to any combination of channels. You can subscribe more than once; new channels are added to the existing set.

    ```json theme={null}
    {
      "type": "subscribe",
      "request_id": "1",
      "data": { "channels": ["trades", "positions", "model_chat", "asset_curve"] }
    }
    ```

    Available channels:

    | Channel       | Broadcasts                                    |
    | ------------- | --------------------------------------------- |
    | `trades`      | Order fills and cancellations on your traders |
    | `positions`   | Opened, updated, and closed positions         |
    | `model_chat`  | AI trader decisions and reasoning             |
    | `asset_curve` | Equity and daily PnL updates (every \~60s)    |
  </Accordion>

  <Accordion title="bootstrap - fetch initial state">
    Ask the server for the current accounts, positions, and active orders so you can render a UI without a separate HTTP round trip.

    ```json theme={null}
    { "type": "bootstrap", "request_id": "2" }
    ```

    The server replies with a single `bootstrap` message containing the full snapshot, then continues streaming updates on the channels you subscribed to.
  </Accordion>

  <Accordion title="switch_account - change the context">
    Point the connection at a different AI trader. Subsequent broadcast events will apply to this account.

    ```json theme={null}
    {
      "type": "switch_account",
      "request_id": "3",
      "data": { "account_id": 1 }
    }
    ```
  </Accordion>

  <Accordion title="get_snapshot - point-in-time state">
    Request a one-off snapshot of an account's equity and positions without subscribing.

    ```json theme={null}
    {
      "type": "get_snapshot",
      "request_id": "4",
      "data": { "account_id": 1 }
    }
    ```
  </Accordion>

  <Accordion title="get_asset_curve - historical equity series">
    Fetch the asset curve for charting.

    ```json theme={null}
    {
      "type": "get_asset_curve",
      "request_id": "5",
      "data": { "account_id": 1, "timeframe": "7d" }
    }
    ```

    Valid timeframes: `1d`, `7d`, `30d`, `90d`, `all`.
  </Accordion>

  <Accordion title="place_order - execute a trade">
    You can place orders over the WebSocket to avoid a round-trip to the REST API. The response is pushed as a `trade_update` event on the `trades` channel.

    ```json theme={null}
    {
      "type": "place_order",
      "request_id": "6",
      "data": {
        "account_id": 1,
        "symbol": "BTC",
        "side": "buy",
        "size": 0.01,
        "order_type": "market"
      }
    }
    ```

    See `POST /api/orders/create` in the [Orders reference](/api-reference/introduction) for the full request schema.
  </Accordion>

  <Accordion title="ping - keep-alive">
    Send `{ "type": "ping" }` periodically to keep the connection open through any proxy. The server replies with `{ "type": "pong" }`.
  </Accordion>
</AccordionGroup>

## Server events

The server pushes messages of these types to subscribed clients:

### `trade_update`

Fired when an order changes state -- accepted, filled, partially filled, cancelled, rejected.

```json theme={null}
{
  "type": "trade_update",
  "data": {
    "trade_id": 42,
    "account_id": 1,
    "symbol": "BTC",
    "side": "buy",
    "status": "filled",
    "price": 68500.0,
    "size": 0.01,
    "pnl": null,
    "timestamp": "2026-04-22T12:00:00Z"
  }
}
```

### `position_update`

Fired when a position opens, updates, or closes.

```json theme={null}
{
  "type": "position_update",
  "data": {
    "account_id": 1,
    "symbol": "BTC",
    "side": "long",
    "size": 0.05,
    "entry_price": 67000.0,
    "mark_price": 68500.0,
    "unrealized_pnl": 75.0,
    "timestamp": "2026-04-22T12:00:05Z"
  }
}
```

### `model_chat_update`

Fired when an AI trader produces or updates a decision, including streamed reasoning chunks.

```json theme={null}
{
  "type": "model_chat_update",
  "data": {
    "decision_id": "dec-001",
    "account_id": 1,
    "symbol": "BTC",
    "decision": "buy",
    "reasoning": "RSI oversold at 28, strong support at 67000...",
    "timestamp": "2026-04-22T12:00:00Z"
  }
}
```

### `asset_curve_update`

Fired roughly every minute with updated equity and PnL figures.

```json theme={null}
{
  "type": "asset_curve_update",
  "data": {
    "account_id": 1,
    "timestamp": "2026-04-22T12:00:00Z",
    "equity": 10075.0,
    "daily_pnl": 75.0,
    "daily_return_pct": 0.75
  }
}
```

### `error`

Sent when a client message fails to parse or the server cannot fulfil the request.

```json theme={null}
{
  "type": "error",
  "request_id": "6",
  "data": {
    "error": "Invalid symbol",
    "code": "VALIDATION_ERROR"
  }
}
```

## Connection lifecycle

<Steps>
  <Step title="Open the socket with your token">
    Use `wss://api.production.hyperoru.com/ws?session_token=...`. The server closes the connection immediately if the token is missing or invalid.
  </Step>

  <Step title="Optionally bootstrap">
    Send `bootstrap` if you need initial state before subscribing.
  </Step>

  <Step title="Subscribe to channels">
    One `subscribe` call per connection is usually enough.
  </Step>

  <Step title="Handle reconnects">
    Networks drop. On unexpected close, wait 1-5 seconds with jitter, reconnect, and resubscribe. The server will deliver fresh state on reconnect -- there is no replay buffer.
  </Step>

  <Step title="Keep-alive">
    Send `ping` every 30 seconds to keep the connection open through intermediate proxies.
  </Step>
</Steps>

## Related

<Columns cols={2}>
  <Card title="REST introduction" icon="book" href="/api-reference/introduction">
    Browse every HTTP endpoint with interactive examples.
  </Card>

  <Card title="Market intelligence stream" icon="broadcast-tower" href="/features/market-data">
    Server-sent events for market commentary and regime updates.
  </Card>
</Columns>
