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

# Realtime Updates

> Stream document status changes with WebSocket or Server-Sent Events.

CapOut supports two realtime transports:

* WebSocket for long-lived interactive clients that may want to change subscriptions after connecting
* SSE for simpler browser-friendly one-way event delivery

Both transports start with `POST /ws/token`.

## 1. Mint a realtime token

```bash theme={null}
curl https://api.capout.ai/ws/token \
  -X POST \
  -H "capout-api-key: $CAPOUT_API_KEY"
```

Example response:

```json theme={null}
{
  "token": "rt_123",
  "organization_id": "org_123",
  "expires_at": "2026-04-14T14:45:00Z",
  "ws_url": "/ws/status",
  "sse_url": "/sse/status"
}
```

If the response contains relative paths, resolve them against `https://api.capout.ai` or `wss://api.capout.ai`.

## WebSocket example

```js theme={null}
const tokenResponse = await fetch("https://api.capout.ai/ws/token", {
  method: "POST",
  headers: {
    "capout-api-key": process.env.CAPOUT_API_KEY
  }
}).then((res) => res.json());

const wsUrl = new URL(tokenResponse.ws_url, "wss://api.capout.ai");
wsUrl.searchParams.set("token", tokenResponse.token);
wsUrl.searchParams.append("document_id", "doc_123");

const socket = new WebSocket(wsUrl);

socket.addEventListener("open", () => {
  socket.send(JSON.stringify({
    action: "subscribe",
    document_ids: ["doc_123"]
  }));
});

socket.addEventListener("message", (event) => {
  console.log(JSON.parse(event.data));
});
```

## SSE example

```js theme={null}
const tokenResponse = await fetch("https://api.capout.ai/ws/token", {
  method: "POST",
  headers: {
    "capout-api-key": process.env.CAPOUT_API_KEY
  }
}).then((res) => res.json());

const streamUrl = new URL(tokenResponse.sse_url, "https://api.capout.ai");
streamUrl.searchParams.set("token", tokenResponse.token);
streamUrl.searchParams.set("recent", "10");

const source = new EventSource(streamUrl);

source.onmessage = (event) => {
  console.log(JSON.parse(event.data));
};
```

## Scoping subscriptions

You can scope the initial stream in two ways:

* Repeat `document_id` to follow specific documents
* Send `recent=10` to follow the latest organization-owned documents

## Reconnect behavior

Handle disconnects with token expiry in mind:

1. Detect closure or error.
2. Mint a fresh realtime token.
3. Re-open the connection with the same document scope.
