Skip to Content
Launching the open-source gateway

Threads and traces

Group a conversation's turns into a thread, and see every step of a request as one trace.

Every gateway request is one trace, with a row for each model call it makes. A thread groups the requests of one conversation, turn after turn. You control both with a few Respan params, and you can join gateway calls to the traces your own code already writes.

Terminal
curl -i https://gateway.respan.ai/v1/chat/completions \ -H "Authorization: Bearer $RESPAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.5", "messages": [{"role": "user", "content": "Where is my order?"}], "thread_identifier": "conv_5d21", "trace_unique_id": "order-help-7f3a", "span_name": "support-reply" }'

Parameters

ParameterTypeDefaultDescription
thread_identifierstringNoneThe conversation the request belongs to.
trace_unique_idstringThe request’s X-Respan-Gateway-Request-IdThe trace to put the request’s rows in.
span_namestringThe call’s own nameThe name of the request’s root row. Cut to 255 characters.
trace_namestringNoneThe trace’s name, used for the root row when span_name is not set.

Send them at the top level, in respan_params, or in the X-Data-Respan-Params header, like any Respan param.

Threads

Send the same thread_identifier on every turn of a conversation. Chat Completions doesn’t keep history for you, so still send the full conversation in messages each turn; the thread is how Respan groups the turns afterwards.

In Gateway › Logs, group by Threads to see each conversation as one item with its turns in order, or filter by Thread ID.

One trace per request

The gateway writes one row for each call it makes, and the rows of one request form a trace:

RowWhat it isParent
RootThe request’s first model call. X-Respan-Log-Id names it.None
Model callEach later model call of the requestThe root

Rows are typed by what they are: chat, embedding, image, speech, transcription, or video.

Every row of a request carries the same X-Respan-Gateway-Request-Id. The root row is published as soon as its call ends; the other rows when the request ends. Once every row of the trace is in, Respan fires the trace_completed webhook event.

Trace ID

Without trace_unique_id, the trace ID is the request’s X-Respan-Gateway-Request-Id, so each request is its own trace. Send the same trace_unique_id on several requests to put them all in one trace, for example every model call of one agent run.

Root row name

The root row is named by span_name. Without it, the gateway uses trace_name, then the call’s own name. Name the root after what the request does in your app, such as support-reply or classify-ticket, so traces are easy to tell apart.

What you see

In the response. Every answer carries two headers, set before any provider is called, so streamed and refused requests have them too:

HeaderWhat it names
X-Respan-Log-IdThe request’s root row. Paste it into the Logs search to open the call.
X-Respan-Gateway-Request-IdThe whole request. Every row it writes carries it.

In Logs. Open Gateway › Logs, paste the X-Respan-Log-Id, and open the request. Group by Traces to see the root with its model calls and tool calls as a tree, with each row’s latency, tokens, and cost. See Inspect spans and traces.

On your bill. Each model call row is charged like any call. The trace’s total is the sum of its rows.

If your app is traced with the Respan SDK or OpenTelemetry, you can put a gateway request in your app’s trace. Send your trace’s ID as trace_unique_id:

Python
from opentelemetry import trace ctx = trace.get_current_span().get_span_context() response = client.chat.completions.create( model="gpt-5.5", messages=[{"role": "user", "content": "Summarize this ticket."}], extra_body={ "trace_unique_id": format(ctx.trace_id, "032x"), "span_name": "summarize-ticket", }, )

The gateway’s rows join your trace, so one trace shows your code, the gateway call, and every step inside it.

Next steps