---
title: "List a session's forks"
method: GET
path: "/v1/projects/{project_id}/sessions/{session_id}/forks"
tags: ["Sessions"]
---

# List a session's forks

`GET /v1/projects/{project_id}/sessions/{session_id}/forks`

## Request

- Base URL: `https://api.naturali.ai`
- URL: `https://api.naturali.ai/v1/projects/{project_id}/sessions/{session_id}/forks`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://api.naturali.ai/authorize`, token URL `https://api.naturali.ai/token`, scopes: `mcp:access`)

Returns the sessions forked directly from this one. One level of lineage: a fork of a fork is listed under its own parent.

## Path parameters

- `session_id` string, required

## Query parameters

- `limit` integer
- `offset` integer

## Response `200`

Paginated list of forks

- object
  - `data` SessionRecord[]
    - `id` string: Session public ID
    - `agent_id` string: Agent public ID, kept after a shared agent is deleted by its owner.
    - `conversation_id` string: Underlying conversation public ID
    - `status` 'open' | 'closed' | 'expired'
    - `name` string, nullable
    - `actor_id` string, nullable: Public ID of the user actor, or null when the session was created without one
    - `tags` TagBag: Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter and knowledge search. Keys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag. The bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.
    - `auto_generate` boolean: When true, automatically triggers generation after each user message (if no generation is in progress).
    - `generating_at` string, date-time, nullable: Timestamp when the current generation started, or null if not generating.
    - `created_at` string, date-time
    - `updated_at` string, date-time
    - `inactivity_ttl_seconds` integer: Number of seconds of inactivity after which the session expires. 0 means the session never expires.
    - `message_delay_seconds` integer, nullable: Number of seconds to wait after the last user message before sending to the LLM. Acts as a debounce: each new message resets the timer. null or absent means no delay (immediate processing).
    - `max_context_messages` integer, nullable: Maximum number of recent conversation messages sent to the model on each generation. Takes precedence over the agent's `max_context_messages`; null defers to it. A fork keeps its parent's value.
    - `last_activity_at` string, date-time, nullable: Timestamp of the last activity on the session (message added or response generated).
    - `forked_from_session_id` string, nullable: Public ID of the session this one was forked from, or null when it was not forked. Also null once that parent is deleted — a fork survives its parent and keeps its own history.
    - `forked_from_position` integer, nullable: The parent conversation position this session branched after, or null when it is not a fork or was forked at the tip.
    - `usage` UsageTotals: Token counts and cost for one slice of the meter. Tokens and cost only: a `compute_second` or `gb_day` meter is reported by the aggregate's `components` array. The three input dimensions partition the prompt — `input_tokens` = `uncached_input_tokens` + `cached_tokens` + `cache_write_tokens` — and are separate because they are separately priced. Note that the `input_tokens` **component** on a usage event is the uncached figure alone; here `input_tokens` is the whole prompt.
      - `cost_usd` number, nullable: Sum of priced component costs; null when nothing in the slice was priced. Always null at step altitude — the ledger prices one event per generation at write time, and a price read back per step would disagree with it after any price change.
      - `input_tokens` integer: Full prompt tokens, reconstructed from the components.
      - `uncached_input_tokens` integer: Prompt tokens priced at the plain input rate — the prompt minus cache reads and cache writes.
      - `output_tokens` integer
      - `cached_tokens` integer: Prompt tokens served from the provider's prompt cache.
      - `cache_write_tokens` integer: Prompt tokens written into the provider's prompt cache.
      - `reasoning_tokens` integer: A non-billable subset of `output_tokens`, reported where the provider breaks it out.
  - `total` integer
  - `limit` integer
  - `offset` integer

## Other responses

- `401`: Missing or invalid credentials.
- `403`: The credential is scoped to a different project, or the caller's role in the project does not carry this action.
- `404`: The resource does not exist (existence is not leaked).

---

[API](https://skmtc.dev/naturali/apis/naturali-ai-api.md) · [All operations](https://skmtc.dev/naturali/apis/naturali-ai-api/llms.txt) · [OpenAPI document](https://skmtc.dev/naturali/apis/naturali-ai-api/revisions/e4905b78f422?raw)
