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

# Sessions, conversations & turns

> Group gateway calls for tracing — from a single call to multi-agent.

Every gateway call the SDK helps you make carries grouping headers so related calls line up in your traces. Three objects nest, and each level defaults from the one above, so you only reach for the level your app needs.

| Object       | Created by                                                | Groups                                              | Header it introduces     |
| ------------ | --------------------------------------------------------- | --------------------------------------------------- | ------------------------ |
| Session      | `to11.session(id?)`                                       | A whole unit of work, across conversations          | `x-to11-session-id`      |
| Conversation | `to11.conversation(id?)` or `session.conversation(id?)`   | One agent thread                                    | `x-to11-conversation-id` |
| Turn         | `to11.turn()`, `session.turn()`, or `conversation.turn()` | One turn — a fresh trace grouping that turn's calls | `traceparent`            |

A turn's `headers(prompt?)` returns the full bag: the tenant auth (`x-to11-authorization`, `x-to11-project-id`, and `x-to11-env` when the client has an `env` — otherwise the gateway routes on the project's fallback env), the `traceparent`, the session and conversation ids, and — when you pass a rendered prompt — its provenance. It is a pure read of the turn's fixed ids, so reuse it across a tool loop or call it per request; the result is the same. Apply it only to gateway requests; it carries the to11 key and must not go to a provider endpoint directly.

Ids are generated and stable per object; pass your own to group by a business id (a user session, a chat thread). You view the grouped traces in the dashboard under **Projects → Traces**.

The examples below assume:

```ts theme={null}
import { createClient } from "@to11ai/sdk";
import OpenAI from "openai";

const to11 = createClient({ env: "production", format: "openai" });
const openai = new OpenAI(to11.openaiOptions());
const prompt = await to11.prompts.render("weather-concierge", { variables: { city: "NYC" } });
```

## One call

For a single call, create the turn inline. It uses the client's default session and conversation.

```ts theme={null}
const res = await openai.chat.completions.create(
  { ...prompt.config, messages: prompt.messages },
  { headers: to11.turn().headers(prompt) },
);
```

Here `to11.turn()` mints a fresh `traceparent` — a brand-new trace for this one call — and borrows the client's default session and conversation ids. `headers(prompt)` then stamps the request with the whole bag at once: the auth headers (`x-to11-authorization`, `x-to11-project-id`, and `x-to11-env` when set), the three grouping ids (`x-to11-session-id`, `x-to11-conversation-id`, `traceparent`), and the prompt's provenance (`x-to11-prompt-id`, `x-to11-prompt-version`, plus release, variant, label, and slug when present). That provenance is what lets the trace in your dashboard show which prompt version produced the call.

## A tool-use loop

When one turn makes several model calls (a tool loop), **hoist** the turn so every call reuses its headers — and therefore its `traceparent` — so the whole loop is one trace.

```ts theme={null}
import { tools, TOOL_IMPLS } from "./tools"; // your own tool schemas + implementations

const turn = to11.turn();
const headers = turn.headers(prompt);
const messages = [...prompt.messages];

while (true) {
  const res = await openai.chat.completions.create(
    { ...prompt.config, messages, tools, tool_choice: "auto" },
    { headers },
  );
  const msg = res.choices[0].message;
  messages.push(msg);
  if (!msg.tool_calls?.length) {
    console.log(msg.content);
    break;
  }
  for (const call of msg.tool_calls) {
    const result = await TOOL_IMPLS[call.function.name](JSON.parse(call.function.arguments));
    messages.push({ role: "tool", tool_call_id: call.id, content: JSON.stringify(result) });
  }
}
```

The turn is created once, so its `traceparent` is fixed for the whole loop. Reusing the same `headers` on every call keeps the first model call, each tool-result round-trip, and the final answer under one `traceparent` — they line up as a single turn in your traces. Create a turn *inside* the loop instead and every call would get its own `traceparent`, scattering one logical turn across unrelated traces.

## A multi-turn chat

Call `session.turn()` once per user input. Every turn shares the session's implicit conversation id, so the whole chat groups under one conversation, each turn its own trace.

```ts theme={null}
const session = to11.session();
const messages = [...prompt.messages];

for await (const input of inputs) {
  messages.push({ role: "user", content: input });
  const turn = session.turn();
  const res = await openai.chat.completions.create(
    { ...prompt.config, messages },
    { headers: turn.headers(prompt) },
  );
  messages.push(res.choices[0].message);
}
```

Each `session.turn()` mints a fresh `traceparent` — one trace per user input — but hands back the session's single implicit conversation id every time. So `x-to11-conversation-id` stays constant across the whole chat while `traceparent` changes each turn: the dashboard shows one conversation containing a trace per turn.

## Multiple agents

Run several agents under one session by creating a conversation per agent. Each conversation has its own id and message thread; all share the session.

```ts theme={null}
const session  = to11.session();
const main     = session.conversation();
const research = session.conversation();

await openai.chat.completions.create(
  { ...mainPrompt.config, messages: mainMessages },
  { headers: main.turn().headers(mainPrompt) },
);
await openai.chat.completions.create(
  { ...researchPrompt.config, messages: researchMessages },
  { headers: research.turn().headers(researchPrompt) },
);
```

Each `session.conversation()` generates its own `x-to11-conversation-id`, and both carry the same `x-to11-session-id`. Every turn beneath them still gets its own `traceparent`. The result is one session holding two parallel conversations, each with its own traces — so you can tell the main agent's work from the research agent's while still seeing they belong to the same unit of work.

## Without a managed prompt

You do not need a rendered prompt to use the gateway. Call `turn.headers()` with no argument to get auth, session, conversation, and trace headers, and supply your own model and messages.

```ts theme={null}
const to11 = createClient({ env: "production" }); // no format — not rendering prompts
const openai = new OpenAI(to11.openaiOptions());

const res = await openai.chat.completions.create(
  { model: "gpt-4o", messages: [{ role: "user", content: "…" }] },
  { headers: to11.turn().headers() },
);
```

Calling `headers()` with no argument injects the same auth headers and the same three grouping ids — just without the `x-to11-prompt-*` provenance, since there is no rendered prompt to attribute. You supply `model` and `messages` yourself, and the call is still authenticated, grouped, and traced exactly like the others.
