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

# Session threads

> List, retrieve, archive, and observe persistent agent threads in one Session.

A Session always has one primary thread. A coordinator Session can add child threads from its published roster; customers observe them through this read-and-lifecycle surface and cannot create them directly.

## List threads

```http theme={"theme":{"light":"github-light","dark":"github-dark"}}
GET /v1/sessions/{session_id}/threads?limit=100&page=...
```

The response is `{ "data": SessionThread[], "next_page": string | null }`, ordered primary-first and then by child spawn. `limit` defaults to the service bound and is capped at 1,000; `page` is opaque.

## Retrieve a thread

```http theme={"theme":{"light":"github-light","dark":"github-dark"}}
GET /v1/sessions/{session_id}/threads/{thread_id}
```

The lookup is fenced by Session membership. A valid thread from another Session returns `404`, just like an unknown identifier.

## Archive a thread

```http theme={"theme":{"light":"github-light","dark":"github-dark"}}
POST /v1/sessions/{session_id}/threads/{thread_id}/archive
```

The operation accepts the ordinary mutation headers and returns the archived `SessionThread`. Only an idle child with no active action wait may be archived; target a parked ActionApproval or Custom-tool wait with `user.interrupt` first. Repeating an archive returns the same terminated resource; a running, rescheduling, or action-waiting child returns `409 runtime.invalid_transition`. The primary returns `409 runtime.primary_thread` while its Session exists.

## List thread events

```http theme={"theme":{"light":"github-light","dark":"github-dark"}}
GET /v1/sessions/{session_id}/threads/{thread_id}/events?limit=100&after=42
```

Use either `after` or opaque `page`, never both. The primary thread returns the Session's condensed event history. A child returns its complete history, including its local Run and lifecycle events.

## Stream thread events

```http theme={"theme":{"light":"github-light","dark":"github-dark"}}
GET /v1/sessions/{session_id}/threads/{thread_id}/stream
Last-Event-ID: 42
```

This is replay-then-live SSE with the same framing, sequence validation, cursor-gap behavior, reconnect discipline, and optional `event_deltas=agent.message|agent.thinking` previews as the [Session event stream](/reference/events). Preview frames are uncursored and stay local to this stream.

## Respond to a child action

There is no thread-specific response endpoint. Read the child's blocking
`agent.tool_use` from the primary Session stream, then post one of these to
`POST /v1/sessions/{session_id}/events`:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "type": "user.custom_tool_result",
  "payload": {
    "tool_use_id": "toolu_123",
    "result": { "ticket": "CFU-42" },
    "session_thread_id": "sthr_0123456789abcdef0123456789abcdef",
    "authored_by": "prin_0123456789abcdef0123456789abcdef",
    "caused_by": { "kind": "api" }
  }
}
```

Use `user.tool_confirmation` with `result: "allow" | "deny"` for a governed
tool confirmation. Echoing `session_thread_id` is required whenever more than
one child has the same pending `tool_use_id`; providing it unconditionally for
a child is the safe client rule. The response is accepted only for the exact
current wait generation, and exact replay returns the original event.

## `SessionThread`

| Field                      | Shape                                                                                                                      |      |              |              |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ---- | ------------ | ------------ |
| `id`                       | `SessionThreadId` (`sthr_…`)                                                                                               |      |              |              |
| `type`                     | `"session_thread"`                                                                                                         |      |              |              |
| `agent`                    | Frozen executable Agent snapshot: id, Workspace, version, source draft revision, and resolved draft without `multiagent`   |      |              |              |
| `session_id`               | Owning Session                                                                                                             |      |              |              |
| `parent_thread_id`         | `null` for primary; otherwise the primary thread id                                                                        |      |              |              |
| `status`                   | \`running                                                                                                                  | idle | rescheduling | terminated\` |
| `created_at`, `updated_at` | Server timestamps                                                                                                          |      |              |              |
| `archived_at`              | Archive timestamp or `null`                                                                                                |      |              |              |
| `stats`                    | Nullable timing fields: cumulative running time, live elapsed duration, and startup time; archive freezes the final values |      |              |              |
| `usage`                    | Nullable cumulative input, output, and cache token counts                                                                  |      |              |              |

A transient Runner failure emits `session.thread_status_rescheduled` while the same
thread retries. Exhausting that bounded retry leaves the thread addressable at `idle`
with `stop_reason: { "type": "retries_exhausted" }`; an unrecoverable terminal error
uses `terminated` instead.

See [Multiagent threads](/concepts/multiagent-threads) for lifecycle, capacity, interrupt, and webhook semantics.

## TypeScript SDK

The SDK keeps CMA's thread-first method shapes for thread-scoped operations:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
const page = await client.sessions.threads.list(sessionId, { limit: 100 })
const thread = await client.sessions.threads.retrieve(threadId, { session_id: sessionId })
const events = await client.sessions.threads.events.list(threadId, {
  session_id: sessionId,
  after: 42,
})

for await (const event of client.sessions.threads.events.stream(threadId, {
  session_id: sessionId,
  from: 42,
})) {
  console.log(event)
}

await client.sessions.threads.archive(threadId, { session_id: sessionId })
```

## CLI

The CLI exposes the same complete resource hierarchy:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
checkfu session threads list --session-id "$SESSION_ID"
checkfu session threads retrieve --session-id "$SESSION_ID" --thread-id "$THREAD_ID"
checkfu session threads events list --session-id "$SESSION_ID" --thread-id "$THREAD_ID" --after 42
checkfu session threads events stream --session-id "$SESSION_ID" --thread-id "$THREAD_ID" --from 42
checkfu session threads archive --session-id "$SESSION_ID" --thread-id "$THREAD_ID"
```
