Enrich span attributes

Attach metadata, customer identifiers, and other parameters to your spans.
  1. Sign up — Create an account at platform.respan.ai
  2. Create an API key — Generate one on the API keys page
  3. Add credits or a provider key — Add credits on the Credits page or connect your own provider key on the Integrations page

Add the Docs MCP to your AI coding tool to get help building with Respan. No API key needed.

1{
2 "mcpServers": {
3 "respan-docs": {
4 "url": "https://mcp.respan.ai/mcp/docs"
5 }
6 }
7}

Every span is a log with hierarchical context. All fields available on log fields & parameters are also available on spans. This page covers the most important trace-specific fields. For the complete list, see the Span fields reference.


Span types

Every span has a log_type that determines how Respan processes it.

TypeDescriptionInputOutput
chatChat completion (default)Messages arrayAssistant message
textText completionPrompt stringCompletion string
embeddingEmbedding generationText or arrayEmbedding vectors
transcriptionSpeech-to-textAudio metadataTranscribed text
speechText-to-speechText inputAudio output
workflowTop-level orchestrationAny structureAny structure
taskSub-operationAny structureAny structure
agentAgent spanAny structureAny structure
toolTool executionTool inputTool output
guardrailSafety checkInput checkedPass/fail
handoffAgent handoffSource agentTarget agent

All types use universal input and output fields. For chat-type spans, prompt_messages and completion_message are also extracted for backward compatibility.


How to setup

Pass fields to spans using respan_span_attributes (Respan OTel), withTrace metadata (OpenAI Agents SDK), or experimental_telemetry (Vercel AI SDK). Here’s a quick example:

1from respan.decorators import workflow
2from respan.contexts.span import respan_span_attributes
3from openai import OpenAI
4
5client = OpenAI()
6
7@workflow(name="my_workflow")
8def my_workflow():
9 with respan_span_attributes(
10 respan_params={
11 "customer_identifier": "user_123",
12 "metadata": {"env": "production"},
13 "thread_identifier": "thread_001",
14 "trace_group_identifier": "pipeline_run_001"
15 }
16 ):
17 response = client.chat.completions.create(
18 model="gpt-5.4",
19 messages=[{"role": "user", "content": "Hello!"}],
20 )
21 return response
FrameworkMethodSupported params
Respan (OTel)respan_span_attributes(...)All parameters
OpenAI Agents SDKwithTrace("...", fn, { metadata })metadata only (JS/TS)
Vercel AI SDKexperimental_telemetry.metadatacustomer_identifier, metadata, custom pricing

Key fields

customer_identifier

string — Unique user or customer ID. Applies to all spans in the trace. Use this to track per-user metrics, set budgets, and apply rate limits.

1@workflow(name="my_workflow")
2def my_workflow():
3 with respan_span_attributes(
4 respan_params={
5 "customer_params": {
6 "customer_identifier": "user_123",
7 "name": "John Doe",
8 "email": "john@example.com"
9 }
10 }
11 ):
12 # your code here
13 pass

See Customer identifier for more.

metadata

object — Custom key-value pairs for tagging, analytics, and filtering. Metadata appears as custom properties on the trace and its spans.

1@workflow(name="my_workflow")
2def my_workflow():
3 with respan_span_attributes(
4 respan_params={
5 "metadata": {
6 "env": "production",
7 "language": "en",
8 "feature": "chat_support"
9 }
10 }
11 ):
12 # your code here
13 pass

trace_group_identifier

string — Groups related traces together, even across different sessions or systems. Useful for complex workflows that span multiple traces.

Group traces

Add the same trace_group_identifier to multiple workflows:

1@workflow(name="workflow_a")
2def workflow_a():
3 with respan_span_attributes(
4 respan_params={
5 "trace_group_identifier": "pipeline_run_001"
6 }
7 ):
8 pass # First workflow
9
10@workflow(name="workflow_b")
11def workflow_b():
12 with respan_span_attributes(
13 respan_params={
14 "trace_group_identifier": "pipeline_run_001" # same ID groups them
15 }
16 ):
17 pass # Second workflow

thread_identifier

string — Conversation thread identifier. All spans with the same thread_identifier are grouped into the same thread.

1@workflow(name="my_workflow")
2def my_workflow():
3 with respan_span_attributes(
4 respan_params={
5 "thread_identifier": "thread_001"
6 }
7 ):
8 # your code here
9 pass

group_identifier

string — Group identifier for related spans/logs. Use this to batch related requests together for analysis.

1@workflow(name="my_workflow")
2def my_workflow():
3 with respan_span_attributes(
4 respan_params={
5 "group_identifier": "group_123"
6 }
7 ):
8 # your code here
9 pass

custom_identifier

string — An indexed string for fast querying. Use for your own internal IDs.

1extra_body={"custom_identifier": "req_abc123"}

environment

Separate test and production data using different API keys. Create a test key and a production key in API Keys.

You can also set the environment field explicitly:

1extra_body={"environment": "staging"}

Token & cost tracking

Respan auto-calculates tokens and cost for gateway requests. For manual logging:

FieldDescription
prompt_tokensInput tokens
completion_tokensOutput tokens
reasoning_tokensReasoning tokens (o3, gpt-5)
prompt_cache_hit_tokensCached tokens read
prompt_cache_creation_tokensCached tokens created
costTotal cost in USD
latencyDuration in seconds
time_to_first_tokenTTFT for streaming
tokens_per_secondGeneration throughput

Override pricing for custom/fine-tuned models:

1payload = {
2 "prompt_unit_price": 2.50, # per 1M prompt tokens
3 "completion_unit_price": 10.00, # per 1M completion tokens
4}

Feedback & annotations

User feedback

1payload = {"positive_feedback": True} # or False

Notes

Attach free-text annotations to any span via the UI or API.

Evaluation scores

Evaluators automatically attach scores. Filter and sort by scores:

$?sort_by=-scores__evaluator-uuid

See Evaluators for setup.


Other fields

Beyond the key fields above, every span also supports all span fields:

CategoryFields
Trace hierarchytrace_unique_id, span_unique_id, span_parent_id, span_name, span_workflow_name, span_path
Contentinput, output, model, log_type
Metricslatency, cost, usage, start_time, timestamp
Identitycustom_identifier, environment, provider_id

See the full Span fields reference for descriptions of every field.


Complete example

A full tracing setup with all key parameters:

1from respan.decorators import workflow, task
2from respan.contexts.span import respan_span_attributes
3from respan import Respan
4from openai import OpenAI
5
6Respan()
7client = OpenAI()
8
9@task(name="joke_creation")
10def create_joke():
11 completion = client.chat.completions.create(
12 model="gpt-5.4",
13 messages=[{"role": "user", "content": "Tell me a joke about opentelemetry"}],
14 )
15 return completion.choices[0].message.content
16
17@workflow(name="joke_workflow")
18def joke_workflow():
19 with respan_span_attributes(
20 respan_params={
21 "customer_params": {
22 "customer_identifier": "user_123",
23 "name": "John Doe",
24 "email": "john@example.com"
25 },
26 "metadata": {
27 "env": "production",
28 "language": "en",
29 "feature": "chat_support"
30 },
31 "custom_identifier": "ticket_789",
32 "thread_identifier": "thread_abc",
33 "group_identifier": "group_001",
34 "trace_group_identifier": "workflow_group_456"
35 }
36 ):
37 joke = create_joke()
38 return joke
39
40result = joke_workflow()
41print(result)