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.
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
| Parameter | Type | Default | Description |
|---|---|---|---|
thread_identifier | string | None | The conversation the request belongs to. |
trace_unique_id | string | The request’s X-Respan-Gateway-Request-Id | The trace to put the request’s rows in. |
span_name | string | The call’s own name | The name of the request’s root row. Cut to 255 characters. |
trace_name | string | None | The 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:
| Row | What it is | Parent |
|---|---|---|
| Root | The request’s first model call. X-Respan-Log-Id names it. | None |
| Model call | Each later model call of the request | The 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:
| Header | What it names |
|---|---|
X-Respan-Log-Id | The request’s root row. Paste it into the Logs search to open the call. |
X-Respan-Gateway-Request-Id | The 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.
Link calls from your own tracing
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:
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.