---
title: "Update a session"
method: PATCH
path: "/v1/projects/{project_id}/sessions/{session_id}"
tags: ["Sessions"]
---

# Update a session

`PATCH /v1/projects/{project_id}/sessions/{session_id}`

## Request

- Base URL: `https://api.naturali.ai`
- URL: `https://api.naturali.ai/v1/projects/{project_id}/sessions/{session_id}`
- 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`)

Updates the session name and/or status.

## Path parameters

- `session_id` string, required

## Request body

- UpdateSessionRequest
  - `name` string, nullable: Session name (set to null to clear)
  - `status` 'open' | 'closed' | 'expired': Session status
  - `auto_generate` boolean: Enable or disable automatic generation after user messages.
  - `tool_context` object, nullable: Key-value pairs forwarded as `X-Naturali-Context-<key>` headers on every `http` and `mcp` tool call in this session. The header name is the deployment's configured context prefix (`X-Naturali-Context-` by default) plus the key verbatim — no character is re-cased. Keys are stored exactly as sent and never case-converted. A key that is not a valid HTTP header name, or two keys that map to the same header, are rejected with `400 INVALID_TOOL_CONTEXT_KEY`. `session_id`, `actor_id` and `actor_external_id` are server-derived and dropped from the stored bag, in any casing. Write-only: no read of a session returns it. It also does not outlive the session: closing or expiring it clears the bag.
  - `inactivity_ttl_seconds` integer: Number of seconds of inactivity after which the session expires. 0 means the session never expires. Updates the stored TTL; the inactivity clock continues from the last activity timestamp.
  - `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. Set to null to disable the delay.
  - `max_context_messages` integer, nullable: Maximum number of recent conversation messages sent to the model on each generation, taking precedence over the agent's `max_context_messages`. Set to null to defer to the agent.

## Response `200`

Updated session

- 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.

## 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)
