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

# WebSocket protocol

> Connection lifecycle, authentication, heartbeat, and reconnection patterns for XOXNO WebSocket APIs.

Connection lifecycle, authentication, heartbeat, and reconnection patterns for XOXNO WebSocket APIs.

## Endpoints

The Relayer exposes two WebSocket endpoints:

| Endpoint                           | Mode        | Description                                                         |
| ---------------------------------- | ----------- | ------------------------------------------------------------------- |
| `wss://relayer.xoxno.com/ws`       | Full-duplex | Subscribe to topics, broadcast transactions, relay transactions     |
| `wss://relayer.xoxno.com/ws/stats` | Read-only   | Pushes `networkStats` at adaptive intervals. No subscribe required. |

The `/ws/stats` endpoint operates independently. Connect to both when your application needs subscription-based topics alongside the network stats stream.

## Connection lifecycle

```
Client                              Server
  |                                   |
  |──── WebSocket handshake ─────────>|
  |<──── 101 Switching Protocols ─────|
  |                                   |
  |──── subscribe { gasStats } ──────>|
  |<──── { status: "ok" } ───────────|
  |                                   |
  |<──── gasStats event ─────────────|
  |<──── gasStats event ─────────────|
  |                                   |
  |──── relay { tx } ────────────────>|
  |<──── { hashes: [...] } ──────────|
  |                                   |
  |──── unsubscribe { gasStats } ────>|
  |<──── { status: "ok" } ───────────|
  |                                   |
  |──── close ───────────────────────>|
  |<──── close ──────────────────────|
```

1. **Connect:** Open a WebSocket connection to either endpoint.
2. **Subscribe:** Send `subscribe` actions to register for topics (`/ws/stats` subscribes automatically).
3. **Receive events:** The server pushes JSON events for each subscribed topic.
4. **Send actions:** Submit `broadcast` or `relay` actions to process transactions.
5. **Unsubscribe:** Remove subscriptions you no longer need.
6. **Disconnect:** Close the connection cleanly.

## Authentication

Neither WebSocket endpoint requires authentication. The server treats all connections equally.

## Message format

All messages use JSON encoding. Text frames are the standard transport. The server also accepts binary frames for legacy JSON payloads and protobuf batch broadcast.

* **Maximum message size:** 4 MB
* **Encoding:** UTF-8 JSON

Client-to-server messages include an `action` field. Server-to-client messages include either a `type` field (for events) or an `action` + `status` field (for action responses).

## Heartbeat

The server sends a WebSocket **ping** frame every **30 seconds**. Your client must respond with a **pong** frame. Most WebSocket libraries handle this automatically.

If the server receives no pong within the next ping interval, it closes the connection.

## Connection limits

| Parameter                     | Value                  |
| ----------------------------- | ---------------------- |
| Max topics per connection     | 64                     |
| Rate limit (control messages) | 64 per 5-second window |
| Max message size              | 4 MB                   |
| Ping/pong heartbeat interval  | 30 seconds             |

<Warning>
  Sending more than 64 control messages within a 5-second window triggers a rate-limit error. Both `subscribe` and `unsubscribe` actions count toward this limit.
</Warning>

## Reconnection strategy

Use exponential backoff with a cap:

| Attempt | Delay            |
| ------- | ---------------- |
| 1       | 1 second         |
| 2       | 2 seconds        |
| 3       | 4 seconds        |
| 4       | 8 seconds        |
| 5       | 16 seconds       |
| 6+      | 30 seconds (cap) |

After reconnecting, re-subscribe to all topics. The server does not persist subscriptions across connections.

## Connection examples

<CodeGroup>
  ```ts JavaScript theme={"system"}
  const ws = new WebSocket('wss://relayer.xoxno.com/ws');

  ws.onopen = () => {
    console.log('Connected');
    ws.send(JSON.stringify({ action: 'subscribe', topic: 'gasStats' }));
  };

  ws.onmessage = (event) => {
    const msg = JSON.parse(event.data);
    console.log('Received:', msg.type || msg.action);
  };

  ws.onclose = () => {
    console.log('Disconnected: implement reconnection here');
  };
  ```

  ```python Python theme={"system"}
  import asyncio
  import json
  import websockets

  async def connect():
      uri = "wss://relayer.xoxno.com/ws"
      async with websockets.connect(uri) as ws:
          await ws.send(json.dumps({
              "action": "subscribe",
              "topic": "gasStats"
          }))

          async for message in ws:
              msg = json.loads(message)
              print(f"Received: {msg.get('type') or msg.get('action')}")

  asyncio.run(connect())
  ```

  ```rust Rust theme={"system"}
  use futures_util::{SinkExt, StreamExt};
  use serde_json::json;
  use tokio_tungstenite::connect_async;

  #[tokio::main]
  async fn main() {
      let (mut ws, _) = connect_async("wss://relayer.xoxno.com/ws")
          .await
          .expect("failed to connect");

      let subscribe = json!({
          "action": "subscribe",
          "topic": "gasStats"
      });

      ws.send(subscribe.to_string().into()).await.unwrap();

      while let Some(Ok(msg)) = ws.next().await {
          if let Ok(text) = msg.into_text() {
              println!("Received: {text}");
          }
      }
  }
  ```

  ```bash websocat theme={"system"}
  # Install: cargo install websocat
  # Connect and subscribe:
  echo '{"action":"subscribe","topic":"gasStats"}' | \
    websocat wss://relayer.xoxno.com/ws
  ```
</CodeGroup>

## Read-only stats stream

The `/ws/stats` endpoint requires no subscribe action. Data arrives as soon as you connect:

```ts theme={"system"}
const ws = new WebSocket('wss://relayer.xoxno.com/ws/stats');

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  // msg.type === 'networkStats'
  const { network, mempool } = msg.data;
  console.log(`TPS: ${network.currentTps} | Mempool: ${mempool.estimatedQueue}`);
};
```

## Related pages

<CardGroup cols={2}>
  <Card title="subscribe" icon="bell" href="/api-reference/ws/subscribe">
    Subscribe to real-time topics: gas stats, network stats, account deltas, and tx status.
  </Card>

  <Card title="broadcast" icon="radio" href="/api-reference/ws/broadcast">
    Submit pre-signed transactions for P2P broadcast via WebSocket.
  </Card>

  <Card title="gasStats" icon="chart-bar" href="/api-reference/events/gas-stats">
    Per-shard gas price and PPU statistics, every 50ms.
  </Card>

  <Card title="networkStats" icon="globe" href="/api-reference/events/network-stats">
    Full network snapshot: TPS, blocks, mempool, validators, health.
  </Card>
</CardGroup>
