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

# watchChat

> Chat-scoped subscription: run lifecycle, cell snapshots, and heartbeats.

```typescript theme={null}
streaming.chats.watchChat(request, options?) → AsyncIterable<WatchChatEvent>
```

The state channel for a chat: announces `runStarted` immediately when a run is
live, delivers cell snapshots, signals `runComplete` / `runError`, and sends a
`heartbeat` every \~20 seconds. Stays open across runs — your loop decides when
to stop.

## Example

```typescript theme={null}
import { createStreamingClient } from "@textql/sdk/streaming";

const streaming = createStreamingClient({ apiKey: process.env.TEXTQL_API_KEY! });

for await (const event of streaming.chats.watchChat({
  chatId,
  latestCompleteCellId: lastSeenCellId, // omit to replay the whole chat
})) {
  switch (event.payload.case) {
    case "runStarted":
      showSpinner();
      break;
    case "cell":
      upsertCell(event.payload.value); // typed Cell — see the Cell reference
      break;
    case "runComplete":
      hideSpinner();
      console.log("final cell:", event.payload.value.finalCellId);
      break;
    case "runError":
      showError(event.payload.value.error);
      break;
    // "opened", "heartbeat", "handoffPending": safe to ignore
  }
}
```

## Request

| Field | Type | Notes |
| - | - | - |
| `chatId` | `string` | required |
| `latestCompleteCellId` | `string?` | replay starts *after* this cell — pass your last known cell id to catch up; omit to replay the entire chat |
| `resumeCursor` | `string?` | alternative resume point: the `cursor` from a previous event |

## Events

Every event is a `WatchChatEvent` with a discriminated `payload` and a
`cursor: string` you can store for reconnects.

| `event.payload.case` | `event.payload.value` | Meaning |
| - | - | - |
| `"opened"` | `{}` | subscription acknowledged |
| `"runStarted"` | `{}` | a run is executing (sent immediately on attach if one is live) |
| `"cell"` | [`Cell`](/api-reference/sdk/streaming/cells) | cell snapshot — upsert by `id` |
| `"runComplete"` | `{ finalCellId: string }` | run finished |
| `"runError"` | `{ error: string, code: string }` | run failed |
| `"handoffPending"` | `{ handoffMarker: string }` | run paused for human handoff |
| `"heartbeat"` | `{}` | keepalive, every \~20s |

To re-attach after a disconnect or page reload, pass the last cell id you
rendered as `latestCompleteCellId` — missed cells replay, then live events
continue. This is how the
[chat-demo](https://github.com/TextQLLabs/textql-ts-sdk/tree/main/examples/chat-demo)
survives mid-run reloads.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.