Skip to main content
Pair OpenAI Agents instrumentation with OpenAI instrumentation so nested model calls are captured. Once setup() runs (pass both modules in TypeScript; Python auto-detects installed SDKs), the agent run, its tool calls, handoffs, and nested model calls are all captured automatically. Wrap the run in agentSpan() / agent_span() when you want stable identity and session grouping.
Three layers, in increasing order of effort:
  • setup() traces the whole run automatically (pass the SDK modules in TypeScript; Python auto-detects). Enough on its own.
  • agentSpan() / agent_span() adds a stable agentId, agentName, role, and sessionId so the Agents dashboard groups runs by conversation, plus your high-level input and output.
  • manualSpan() / manual_span() captures steps the SDK never sees: your own retrieval, routing, or sub-steps inside a tool.
For the full breakdown of when to reach for each, see How Tracing Fits Together.

Install

Configure Export

Set the Catalyst endpoint and token before your app starts. setup() reads these in every example below. Generate a token at API Keys.

Minimal Setup

Pass the SDK modules to setup() in TypeScript, or let it auto-detect in Python, then run your agent. The run, any tool calls, and the nested model calls are all captured with no further code. This is everything you need for a single one-shot agent.

Group Under An Agent

Wrap the run in agentSpan() to give it stable identity and session grouping. This is the recommended shape for anything beyond a one-shot script. Pass userId to record user.id so you can filter traces by user on the dashboard, just like sessionId. For arbitrary keys, attach any custom attributes you want to filter traces by inside the callback (organization.id, chat.id, order.id). See Adding Custom Attributes.

Multi-Agent Handoff

Wrap the triage request once. The trace tree groups the triage agent, specialist agent, nested model calls, and tools under the customer request.
Use one stable ID for the outer customer-facing run. If you also emit separate AGENT spans for handoff specialists, give each specialist its own stable agent.id, such as refunds-agent or billing-agent.

When To Add Manual Spans

The Agents SDK only captures the work it runs for you: the model calls, and the tools you register with tool(). Anything you do yourself inside the agentSpan callback is invisible to it. You do not wrap the SDK’s own tool calls in manualSpan(), those are already captured, and doing so would just produce a duplicate span. You reach for manualSpan() for the steps the SDK never sees. Two common cases:
  1. A step around the run. You fetch context from a vector store before the run, or validate and reshape the output after it. Neither is an SDK call, so author a RETRIEVER or CHAIN span yourself.
  2. Sub-steps inside a tool. The SDK emits one TOOL span per tool invocation. If that tool’s execute does something you want broken out (a vector search, a downstream API call, a transform), wrap it in a child span. It nests under the SDK’s TOOL span automatically.
The rule of thumb: if the Agents SDK runs the code (a model call, a registered tool), it is captured for you. If you run the code (retrieval, routing, validation, or a sub-step inside a tool), wrap it in manualSpan. See Manual Spans for the full set of span kinds and the handle API.

Flushing

Every example above ends with await tracing.shutdown(), which flushes batched spans before the process exits. Long-lived servers and serverless or edge runtimes need a different flush strategy, since the process does not exit after each run. See Flushing and process lifecycle for all three shapes.

Next Steps

Manual spans

Author TOOL, CHAIN, and RETRIEVER spans for work the SDK does not capture.

Agent identity

Pick stable agent.id and session.id values for the Agents dashboard.

Production agent example

A production-shaped agent with custom tool spans, end to end.

Attributes reference

Every Attr.* constant and SpanKindValues value the SDK emits.