Skip to main content
A session ties several traces to one conversation or workflow. Set a session id on your spans and Surface Area groups the matching traces into one view.

Sessions auto-populate

Every trace belongs to a session, even when you write no grouping code. At tracing.init() the SDK establishes a process-wide session id, and traces inherit it. The ambient session id resolves in this order: the GATEWAY_SESSION_ID environment variable if set (a stable pin, useful across restarts), otherwise an id generated once per process. Finer-grained boundaries — a tracing.session() block, a world session, an explicit attribute, a run rollout — override the ambient default per span. For one span, the first of these that applies wins:
  1. A run rollout’s session id.
  2. A session id set on the span when it is created.
  3. The innermost tracing.session() block or world session.
  4. GATEWAY_SESSION_ID, then the per-process id.
A trace belongs to one session, so give all of its spans the same one: open the block before the trace’s first span.
Set GATEWAY_SESSION_ID when you want every trace from a process to land in one named session, for example one session per deployment or per job. Leave it unset to get a fresh session id each time the process starts.

Group a block of traces with tracing.session()

tracing.session() names a session for a block. Every span created inside carries the session id: your own trace and span blocks, auto-instrumented LLM calls, and tool spans alike.
Grouping works through context propagation, so you name the session once for the whole block. Nested tracing.session() blocks use the innermost id.

Set the session id on a span

Set the tracing.semconv.SESSION_ID attribute to the same stable value on every turn, and Surface Area collects those traces under one session.
semconv.SESSION_ID maps to the attribute key gateway.session.id. Use a value that is stable for the conversation and unique across conversations, such as a chat thread id or a request id you already track.
Pair the session id with tracing.semconv.USER_ID (gateway.user.id) when you know who the conversation belongs to. Surface Area then filters sessions by user as well as by session. See Metadata & identity.

Trace two turns into one session

The example below traces two turns and groups them under conversation-42.
1

Set your Surface Area connection

2

Write the conversation

3

Open the Sessions dashboard

Run the script, then open your project in Surface Area and go to the Sessions view. Both turns appear under one conversation-42 session, in order.

Group traces with run and rollout context

A run and its rollouts assign session ids for you. Each rollout opened inside a run gets its own session id, and every trace created inside that rollout inherits it with no set_attribute call.
Read the active rollout’s session id from its session_id property when you need it elsewhere, for example when attaching a score to the same session.

Trace inside a world session

When your agent works in a live world session, its traces belong with that session. The platform already traces the session’s tool calls under the world session’s id, so trace your agent inside the session and both land in one place.
The with block does it for you. For a session you did not open with with, wrap the work in s.trace_context(). In TypeScript, use session.withTraceContext(fn). run_sessions and runSessions trace every agent call this way. Spans started inside take the world session’s id as their session id and carry world_session_id in their trace metadata. A tracing.session() block inside, or a run rollout, still sets the session id; the metadata stays. A tracing.session() block outside is overridden for the length of the world session. Open the world session before your agent’s first span: a span started earlier keeps the id it started with, and its trace can then land in that session instead.
Nothing is recorded until tracing.init() has run, and the session page shows the traces only when they are sent with keys for the project the world session belongs to. Threads your agent starts do not inherit the scope: enter s.trace_context() inside them, and leave the block on the thread that entered it.

Set the session id on auto-instrumented LLM calls

Auto-instrumented LLM calls produce spans where no tracing.trace block is in scope. Use tracing.tracing_context(session_id=...) to attach a session id to those spans for the duration of the block.
When no session id is set on a span (no tracing_context, no run rollout), the span falls back to the process-wide ambient session id.

Read sessions and traces in the dashboard

Open Sessions from the project navigation to see the sessions table. Each row is one session; the trace evidence view sits one level down and lists the individual traces inside it. Sessions table The Sessions table under Observe. Each row is one session, with its environment, user, trace count, duration, and cost. Both tables share a sidebar filter. Common session filters: Select any row to open its detail view: every trace inside the session in order, and from there the full call tree of any one trace, each LLM call and tool call with latency, token counts, cost, and the messages exchanged. Trace detail view A single trace: the Trace map on the left, and the Preview, Log View, and Scores tabs showing the captured input, output, tags, and metadata.
Filters live in the URL, so a filtered view is a shareable link. A blank score cell means no evaluator graded that run on that criterion: Surface Area never treats a missing score as zero.

Where to go next