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

# Session Pinning

> Route once per session and keep that model for the whole session.

Session pinning makes routing decisions per session instead of per turn. The first turn of a session is routed normally, and every later turn of that session runs on the same model and reasoning level without consulting the routing policy again.

```text theme={null}
Session start ──► routing policy ──► pin model + reasoning level
Later turns   ──► pinned model + reasoning level
Subagent start ─► routing policy ──► its own pin
```

While pinning is on, [Speculative Routing](https://docs.dari.dev/router/speculative-routing.md) and routing leases do not apply to pinned sessions.

## Sessions and Subagents

Each subagent is routed on its own when it starts, then keeps its model. Dari identifies sessions and subagents from the identity each client sends:

| Client | Session | Subagents |
| - | - | - |
| Claude Code | Claude Code session | Each `x-claude-code-agent-id` |
| Codex | Codex thread | Each subagent thread |
| OpenCode | `x-session-affinity` session | Each child session |
| Pi and other OpenAI-compatible clients | `x-session-affinity` or `prompt_cache_key` | Each distinct session identity |

Claude Code names its subagents only when `CLAUDE_CODE_GATEWAY_HINT_HEADERS=1` is set. `dari claude` and `dari claude --setup` (the Claude desktop app) set it for you; without it, Claude Code is pinned per conversation, so subagents still get their own decision but the main session is routed again after compaction.

Clients that send no session identity are pinned per conversation, using the same conversation grouping Dari uses for [leases](https://docs.dari.dev/router/send-chat-completions.md#conversations-and-leases).

Pins are keyed by session identity rather than by transcript content, so they survive compaction and idle gaps.

## What Is Not Pinned

* Background requests, such as conversation titles, compaction summaries, and side replies, keep their usual routing and never read or change a pin.
* Requests for an explicit model, such as `dari/model/<id>`, run on that model and leave the session's pin unchanged.

## When The Pinned Model Is Unavailable

If the pinned model and reasoning level can no longer serve a request—for example, the model was disabled, credits for it are exhausted, or the request includes an image and the pinned model is text-only—Dari routes that request normally and moves the pin to the new choice.

## Response Metadata

Every response served by a pin, including the turn that created it, includes the `X-Dari-Session-Pinned: true` header. Anthropic Messages responses also set `dari_routing.session_pinned` to `true`. Pinned responses do not include `X-Dari-Lease-Turns-Remaining`.

In **Activity**, turns served by a pin show a **Pinned** decision source.

## Configuration

Session pinning is off by default.

* **Dashboard:** open the router in the [Dari Dashboard](https://app.dari.dev), expand **Advanced Settings**, and turn on **Pin Model Per Session**.
* **CLI:** pass `--session-pinning` to `dari router create` or `dari router update`, or set `session_pinning: true` in `router.yml`. Pass `--session-pinning=false` on update to turn it off.
* **API:** set `session_pinning` to `true` when creating or updating a router.
* **Self-hosted gateway:** set `sessionPinning: true` on the router in `dari-gateway.yaml`. Pins are stored in the gateway's SQLite database.

```bash theme={null}
dari router update <router_id> --session-pinning
```


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