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

# WebSocket

> Real-time messaging sessions with typed envelopes, reconnection, and resume.

For live chat UIs, the messaging client opens a bidirectional WebSocket
session: send message frames, receive the reply as a token stream, and recover
missed events after a reconnect. Authentication is handled for you, and it
works in both Node and the browser.

## Open a session

```ts theme={"dark"}
import { ApolloMessagingClient } from '@aui.io/aui-client';

const client = new ApolloMessagingClient({ publishableKey: 'pk_network_...' });

const socket = await client.connect();
await socket.waitForOpen();
```

`connect()` accepts optional arguments:

```ts theme={"dark"}
const socket = await client.connect({
  headers: { 'x-request-tag': 'support-widget' }, // extra connect headers
  debug: false,                                   // log socket activity
  reconnectAttempts: 30,                          // default: 30
});
```

## Send a message

The socket sends typed frames. A `message` frame submits a turn — note that on
the WebSocket the target agent is explicit; use the id resolved from your key:

```ts theme={"dark"}
const { agentId } = await client.getContext();

socket.sendMessage({
  type: 'message',
  agent_id: agentId!,
  user_id: 'end-user-123',
  text: 'I am looking for a built-in microwave',
  // thread_id: existingThreadId,      // omit to auto-create a thread
  // context: { url: location.href },  // originating page URL
  // agent_variables: { ... },
});
```

## Receive envelopes

Every server frame is a sequenced envelope. Register handlers with
`on(event, handler)` — events are `open`, `message`, `error`, and `close`:

```ts theme={"dark"}
let lastSeq = -1;
let threadId: string | undefined;

socket.on('message', (envelope) => {
  if (envelope.seq > 0) lastSeq = envelope.seq;

  switch (envelope.type) {
    case 'thread':      // announced once per thread: the resolved id
      threadId = envelope.data?.thread_id;
      break;

    case 'event': {     // streaming events while the agent works
      const name = envelope.data?.channel?.event_name;
      if (name === 'thread-message-text-content-updated') {
        process.stdout.write((envelope.data?.data as any)?.text ?? '');
      }
      break;
    }

    case 'message':     // the completed reply (plus trace_info when enabled)
      console.log('\ncomplete:', envelope.data?.text);
      break;

    case 'error':       // session- or turn-level error; the socket stays open
      console.error(envelope.data);
      break;
  }
});

socket.on('close', (event) => console.log('closed', event.code));
socket.on('error', (error) => console.error(error));
```

| Envelope `type` | Meaning                                                                                                            | `data`                                   |
| --------------- | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------- |
| `thread`        | The resolved thread id (once per thread)                                                                           | `{ thread_id }`                          |
| `event`         | Streaming event; token deltas arrive as `thread-message-text-content-updated` with the text under `data.data.text` | `Apollo.StreamEvent`                     |
| `message`       | Turn complete — the full reply                                                                                     | `Apollo.Message` + optional `trace_info` |
| `error`         | Session error (`{ error, code }`) or turn error (`{ message, status_code }`)                                       | varies                                   |

<Info>
  `on()` registers a **single handler per event** — calling it again for the
  same event replaces the previous handler.
</Info>

## Resume after a reconnect

Envelopes carry a monotonic `seq` (locally generated errors use `-1`). Track
the highest value you've processed; after a reconnect, ask the server to
replay what you missed instead of re-running the turn:

```ts theme={"dark"}
socket.sendResume({ type: 'resume', resume_after: lastSeq });
```

The socket reconnects automatically (up to `reconnectAttempts`), so a typical
pattern is to send a `resume` frame from the `open` handler when `lastSeq > -1`.

## Close the session

```ts theme={"dark"}
socket.close();
```

The socket also exposes `readyState` for connection-state checks.

## Full example

```ts theme={"dark"}
import { ApolloMessagingClient } from '@aui.io/aui-client';

const client = new ApolloMessagingClient({ publishableKey: process.env.AUI_PK! });
const { agentId } = await client.getContext();

const socket = await client.connect();
let lastSeq = -1;

socket.on('open', () => {
  if (lastSeq > -1) socket.sendResume({ type: 'resume', resume_after: lastSeq });
});

socket.on('message', (envelope) => {
  if (envelope.seq > 0) lastSeq = envelope.seq;
  if (envelope.type === 'message') {
    console.log('agent:', envelope.data?.text);
    socket.close();
  }
});

await socket.waitForOpen();

socket.sendMessage({
  type: 'message',
  agent_id: agentId!,
  user_id: 'end-user-123',
  text: 'Hello over WebSocket',
});
```

<Tip>
  The wire protocol underneath (URL, query parameters like `include_trace` and
  `events`, close codes) is documented in [API → WebSocket](/api/messaging/websocket),
  with the machine-readable contract published as an AsyncAPI document.
</Tip>
