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

# Connect to WebSocket

> Subscribe to live market updates over the backend Socket.IO API or the direct CLOB WebSocket.

United Market offers two real-time interfaces. Most integrations should use the **backend Socket.IO** endpoint; server-side bots that want the matcher's native order-book stream can connect to the **CLOB WebSocket** directly.

| Interface             | URL                             | Protocol                                  |
| --------------------- | ------------------------------- | ----------------------------------------- |
| Backend (recommended) | `https://api.unitedmarket.ai`   | Socket.IO (secure `wss://` after upgrade) |
| CLOB (direct)         | `wss://clob.unitedmarket.ai/ws` | Raw WebSocket                             |

<Note>
  United Market is now live on BNB Smart Chain mainnet. We just launched — start trading on-chain prediction markets today.
</Note>

The Socket.IO endpoint adds market rooms, automatic reconnection, price snapshots, and normalized payloads. The [direct CLOB WebSocket](#direct-websocket-clob) is a lower-level feed intended for advanced, server-side consumers.

## Socket.IO (recommended)

Use a Socket.IO client — not a plain WebSocket client — to connect to `https://api.unitedmarket.ai`. The transport upgrades to a secure WebSocket (`wss://`) automatically.

### Install Client

```bash theme={null}
npm install socket.io-client
```

### Connect

```ts theme={null}
import { io } from "socket.io-client";

const socket = io("https://api.unitedmarket.ai", {
  transports: ["websocket", "polling"],
  reconnection: true,
});
```

### Subscribe to a Market

Join a market room with the market `conditionId`.

```ts theme={null}
const conditionId = "0xd19487c4038d0dce2edeb510a21d6f8534e09d8065d40cb9e10ad2652d86a276";

socket.on("connect", () => {
  socket.emit("join:market", conditionId);
});

socket.emit("leave:market", conditionId);
```

### Client Events

| Event            | Payload               | Description                             |
| ---------------- | --------------------- | --------------------------------------- |
| `join:market`    | `conditionId: string` | Subscribe to one market room.           |
| `leave:market`   | `conditionId: string` | Unsubscribe from one market room.       |
| `join:activity`  | none                  | Subscribe to global trade activity.     |
| `leave:activity` | none                  | Unsubscribe from global trade activity. |

### Server Events

| Event              | Description                                   |
| ------------------ | --------------------------------------------- |
| `orderbook:update` | Emitted when market orderbook state changes.  |
| `trade:new`        | Emitted when a trade is confirmed on-chain.   |
| `ob:snapshot`      | L2 orderbook snapshot for an outcome token.   |
| `price:snapshot`   | Latest bid, ask, and midpoint for an outcome. |

### `orderbook:update`

```ts theme={null}
type OrderbookUpdatePayload = {
  conditionId: string;
  orderbook: {
    type:
      | "order:new"
      | "order:cancelled"
      | "order:matched"
      | "order:filled"
      | "order:settlement";
    orderHash: string;
    tokenId: string;
    timestamp: number;
    data: Record<string, unknown>;
  };
};
```

After this event, clients can refetch `GET /markets/{id}/orderbook` or `GET /markets/{id}/trading`.

### `trade:new`

```ts theme={null}
type TradeNewPayload = {
  conditionId: string;
  trade: {
    orderHash: string;
    tokenId: string;
    timestamp: number;
    data: {
      maker: string;
      taker: string;
      makerAmountFilled: string;
      takerAmountFilled: string;
      fee: string;
      txHash: string;
    };
  };
};
```

### `ob:snapshot`

L2 levels are 18-decimal fixed-point strings — divide `price` and `size` by `1e18`. When `synthAsks` is `true`, asks already include complement synthetic asks, so do not re-derive them client-side.

```ts theme={null}
type L2Level = {
  price: string;
  size: string;
  numOrders: number;
};

type ObSnapshotPayload = {
  conditionId: string;
  tokenId: string;
  bids: L2Level[];
  asks: L2Level[];
  timestamp: number;
  // Monotonic matcher sequence number; drop a snapshot whose seq is not
  // greater than the last one you applied. Absent on older builds.
  seq?: number;
  synthAsks?: boolean;
};
```

### `price:snapshot`

```ts theme={null}
type PriceSnapshotPayload = {
  conditionId: string;
  outcomeId: string;
  tokenId: string;
  bid: number | null;
  ask: number | null;
  mid: number | null;
  timestamp: number;
};
```

### Reconnection

Socket.IO reconnects automatically when configured with `reconnection: true`. Re-emit `join:market` subscriptions after reconnecting if your client manages rooms outside a persistent component.

## Direct WebSocket (CLOB)

Advanced and server-side clients can connect straight to the CLOB matcher's raw WebSocket. This is a lower-level feed: it streams the matcher's native events without the backend's room management, price snapshots, or payload normalization.

| WebSocket URL                   |
| ------------------------------- |
| `wss://clob.unitedmarket.ai/ws` |

### Connect & Subscribe

Send a JSON `subscribe` message with a market `conditionId`. The server replies with a `subscribed` acknowledgement, then streams that market's events. Subscribe to multiple markets by sending multiple messages; remove one with `unsubscribe`.

```ts theme={null}
const ws = new WebSocket("wss://clob.unitedmarket.ai/ws");

ws.onopen = () => {
  ws.send(
    JSON.stringify({
      type: "subscribe",
      conditionId:
        "0xd19487c4038d0dce2edeb510a21d6f8534e09d8065d40cb9e10ad2652d86a276",
    }),
  );
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  // First an ack: { type: "subscribed", conditionId }
  // Then market events (see Event Format below).
  console.log(msg);
};

// Later: stop receiving events for a market.
// ws.send(JSON.stringify({ type: "unsubscribe", conditionId }));
```

<Note>
  If you never send a `subscribe` message, the socket receives events for **all** markets. Send at least one `subscribe` to scope the stream to the markets you care about.
</Note>

### Event Format

Every market event shares one envelope. The event name uses the matcher's native form (`ob_snapshot` / `ob_diff` with underscores, and `order:*`), and the per-event detail lives under `data`.

```ts theme={null}
type MatcherEvent = {
  type:
    | "order:new"
    | "order:cancelled"
    | "order:matched"
    | "order:filled"
    | "order:settlement"
    | "ob_snapshot"
    | "ob_diff";
  conditionId: string;
  orderHash: string;
  tokenId: string;
  timestamp: number;
  // Monotonic sequence number. Drop any event whose seq is not greater than
  // the last one you applied for that market.
  seq: number;
  data: Record<string, unknown>;
};
```

For `ob_snapshot`, `data` carries the full L2 book. As with the Socket.IO API, `price` and `size` are 18-decimal fixed-point strings, and `synthAsks: true` means asks already include complement synthetic asks.

```ts theme={null}
type ObSnapshotData = {
  bids: Array<{ price: string; size: string; numOrders: number }>;
  asks: Array<{ price: string; size: string; numOrders: number }>;
  synthAsks: true;
};
```

<Warning>
  This is the same data the backend relays, but in the matcher's raw form: event names use underscores (`ob_snapshot`, not `ob:snapshot`) and the payload is wrapped in the `MatcherEvent` envelope (fields under `data`) rather than flattened. Use the Socket.IO API if you want the normalized, room-scoped events.
</Warning>
