Repository Public
1# Observability and usage23> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.45Track live agent activity, inspect completed work, and review detailed turn traces:671. You can view the session logs in the Platform dashboard.82. You can follow the session through its events and saved history.93. You can inspect turns and identify delegated command execution.104. You can inspect recorded token usage for root-agent and subagent turns.1112## View the session in the dashboard1314Go to [platform.openai.com/logs?api=agents](https://platform.openai.com/logs?api=agents) and open the **Agents** tab.1516Search for a session by ID to inspect its turns, tool calls, and subagents.1718Use the [Tracing guide](https://developers.openai.com/api/docs/guides/agents-api/tracing) to inspect recorded model responses, tool calls, and subagent activity in the dashboard, or [export session traces](https://developers.openai.com/api/docs/guides/agents-api/tracing#export-session-traces) as OTLP JSON through the public API.1920## Follow events and inspect session history2122Every session exposes an event stream that shows what the agent is doing in real time. Set `OPENAI_API_KEY` and replace the illustrative session ID in these examples with your saved session ID:2324Follow live session events2526```javascript27// Replace the illustrative IDs and URLs below with your own resource values.28import OpenAI from "openai";2930const client = new OpenAI();31const events = await client.beta.agents.sessions.events.stream("sess_123");32try {33 for await (const event of events) {34 if (35 [36 "agent.session.turn.failed",37 "agent.session.turn.cancelled",38 "agent.session.failed",39 "agent.session.environment.failed",40 "error",41 ].includes(event.type)42 ) {43 throw new Error(`Agent lifecycle failure: ${event.type}`);44 }45 console.log(JSON.stringify(event));46 }47} finally {48 events.controller.abort();49}50```5152```python53# Replace the illustrative IDs and URLs below with your own resource values.54from openai import OpenAI5556client = OpenAI()57session_id = "sess_123"58with client.beta.agents.sessions.events.stream(session_id) as events:59 for event in events:60 if event.type in {61 "agent.session.turn.failed",62 "agent.session.turn.cancelled",63 "agent.session.failed",64 "agent.session.environment.failed",65 "error",66 }:67 raise RuntimeError(f"Agent lifecycle failure: {event.type}")68 print(event.to_json(indent=None))69```7071```go72// Replace the illustrative IDs and URLs below with your own resource values.73import (74 "context"75 "fmt"7677 "github.com/openai/openai-go/v3"78)7980ctx := context.Background()81client := openai.NewClient()82events := client.Beta.Agents.Sessions.Events.StreamStreaming(ctx, "sess_123")83defer events.Close()84if events.Err() != nil {85 panic(events.Err())86}87for events.Next() {88 event := events.Current()89 switch event.Type {90 case "agent.session.turn.failed", "agent.session.turn.cancelled", "agent.session.failed", "agent.session.environment.failed", "error":91 panic(event.RawJSON())92 }93 fmt.Println(event.RawJSON())94}95if err := events.Err(); err != nil {96 panic(err)97}98```99100```java101// Replace the illustrative IDs and URLs below with your own resource values.102import com.fasterxml.jackson.databind.json.JsonMapper;103import com.openai.client.OpenAIClient;104import com.openai.client.okhttp.OpenAIOkHttpClient;105import com.openai.core.http.StreamResponse;106import com.openai.models.beta.agents.AgentSessionEvent;107108OpenAIClient client = OpenAIOkHttpClient.fromEnv();109var json = new JsonMapper();110try (StreamResponse<AgentSessionEvent> events =111 client.beta().agents().sessions().events().streamStreaming("sess_123")) {112 var iterator = events.stream().iterator();113 while (iterator.hasNext()) {114 var event = iterator.next();115 if (event.turnFailed().isPresent()116 || event.turnCancelled().isPresent()117 || event.failed().isPresent()118 || event.environmentFailed().isPresent()119 || event.error().isPresent()) {120 throw new IllegalStateException("Agent failed: " + event);121 }122 System.out.println(json.writeValueAsString(event));123 }124}125```126127```ruby128# Replace the illustrative IDs and URLs below with your own resource values.129require "openai"130require "json"131132client = OpenAI::Client.new133events = client.beta.agents.sessions.events.stream_streaming("sess_123")134begin135 events.each do |event|136 case event.type.to_s137 when "agent.session.turn.failed", "agent.session.turn.cancelled", "agent.session.failed", "agent.session.environment.failed", "error"138 raise "Agent failed: #{event.to_h}"139 end140 puts JSON.generate(event.to_h)141 end142ensure143 events.close144end145```146147```bash148curl -N \149 -H "OpenAI-Beta: agents=v1" \150 -H "Authorization: Bearer $OPENAI_API_KEY" \151 -H "Accept: text/event-stream" \152 "https://api.openai.com/v1/agents/sessions/sess_123/events?stream=true"153```154155156The stream stays open across idle events so you don't miss queued work. Press **Ctrl+C** to stop watching.157158As the session runs, you’ll see events such as:159160```text161agent.session.environment.connected162agent.session.turn.created163agent.session.turn.in_progress164agent.session.turn.item.added165agent.session.turn.output_text.delta166agent.session.turn.completed167agent.session.idle168```169170To inspect work that has already happened, retrieve the session’s saved items:171172Inspect saved session items173174```javascript175// Replace the illustrative IDs and URLs below with your own resource values.176import OpenAI from "openai";177const client = new OpenAI();178179const sessionId = "sess_123";180const items = await client.beta.agents.sessions.items.list(sessionId, {181 order: "asc",182 limit: 100,183});184console.log(items.data);185```186187```python188# Replace the illustrative IDs and URLs below with your own resource values.189from openai import OpenAI190191client = OpenAI()192193session_id = "sess_123"194items = client.beta.agents.sessions.items.list(session_id, order="asc", limit=100)195print(items.to_json())196```197198```go199// Replace the illustrative IDs and URLs below with your own resource values.200import (201 "context"202 "fmt"203204 "github.com/openai/openai-go/v3"205)206207ctx := context.Background()208client := openai.NewClient()209result, err := client.Beta.Agents.Sessions.Items.List(ctx,210 "sess_123",211 openai.BetaAgentSessionItemListParams{212 Order: "asc",213 Limit: openai.Int(100),214 })215if err != nil {216 panic(err)217}218fmt.Println(result.Data)219```220221```java222// Replace the illustrative IDs and URLs below with your own resource values.223import com.openai.client.OpenAIClient;224import com.openai.client.okhttp.OpenAIOkHttpClient;225import com.openai.models.beta.agents.sessions.items.ItemListParams;226227OpenAIClient client = OpenAIOkHttpClient.fromEnv();228var result =229 client230 .beta()231 .agents()232 .sessions()233 .items()234 .list(235 ItemListParams.builder()236 .sessionId("sess_123")237 .order(ItemListParams.Order.of("asc"))238 .limit(100L)239 .build());240System.out.println(result.items());241```242243```ruby244# Replace the illustrative IDs and URLs below with your own resource values.245require "openai"246247client = OpenAI::Client.new248result = client.beta.agents.sessions.items.list(249 "sess_123",250 order: "asc",251 limit: 100252)253puts result.data254```255256```bash257curl \258 -H "OpenAI-Beta: agents=v1" \259 -H "Authorization: Bearer $OPENAI_API_KEY" \260 "https://api.openai.com/v1/agents/sessions/sess_123/items?order=asc&limit=100"261```262263264## Inspect turns and identify delegated commands265266Session turns are available through the public API. Use the `turn_id` from a command item with your saved session ID. The cURL example requires `jq`:267268Identify delegated command execution269270```javascript271// Replace the illustrative IDs and URLs below with your own resource values.272import OpenAI from "openai";273const client = new OpenAI();274275const sessionId = "sess_123";276const turns = await client.beta.agents.sessions.turns.list(sessionId, {277 limit: 20,278 order: "desc",279});280console.log(turns.data);281const turnId = "turn_123";282const turn = await client.beta.agents.sessions.turns.retrieve(turnId, {283 session_id: sessionId,284});285console.log(turn.subagent_id);286```287288```python289# Replace the illustrative IDs and URLs below with your own resource values.290from openai import OpenAI291292client = OpenAI()293294session_id = "sess_123"295turns = client.beta.agents.sessions.turns.list(session_id, limit=20, order="desc")296print(turns.to_json())297turn_id = "turn_123"298turn = client.beta.agents.sessions.turns.retrieve(turn_id, session_id=session_id)299print(turn.subagent_id)300```301302```go303// Replace the illustrative IDs and URLs below with your own resource values.304import (305 "context"306 "fmt"307308 "github.com/openai/openai-go/v3"309)310311ctx := context.Background()312client := openai.NewClient()313result, err := client.Beta.Agents.Sessions.Turns.List(ctx,314 "sess_123",315 openai.BetaAgentSessionTurnListParams{316 Limit: openai.Int(20),317 Order: "desc",318 })319if err != nil {320 panic(err)321}322fmt.Println(result.Data)323turn, err := client.Beta.Agents.Sessions.Turns.Get(ctx,324 "sess_123",325 "turn_123")326if err != nil {327 panic(err)328}329fmt.Println(turn.SubagentID)330```331332```java333// Replace the illustrative IDs and URLs below with your own resource values.334import com.openai.client.OpenAIClient;335import com.openai.client.okhttp.OpenAIOkHttpClient;336import com.openai.models.beta.agents.sessions.turns.TurnListParams;337import com.openai.models.beta.agents.sessions.turns.TurnRetrieveParams;338339OpenAIClient client = OpenAIOkHttpClient.fromEnv();340var result =341 client342 .beta()343 .agents()344 .sessions()345 .turns()346 .list(347 TurnListParams.builder()348 .sessionId("sess_123")349 .limit(20L)350 .order(TurnListParams.Order.of("desc"))351 .build());352System.out.println(result.items());353var turn =354 client355 .beta()356 .agents()357 .sessions()358 .turns()359 .retrieve(360 TurnRetrieveParams.builder().turnId("turn_123").sessionId("sess_123").build());361System.out.println(turn.subagentId());362```363364```ruby365# Replace the illustrative IDs and URLs below with your own resource values.366require "openai"367368client = OpenAI::Client.new369result = client.beta.agents.sessions.turns.list(370 "sess_123",371 limit: 20,372 order: "desc"373)374puts result.data375turn = client.beta.agents.sessions.turns.retrieve(376 "turn_123",377 session_id: "sess_123"378)379puts turn.subagent_id380```381382```bash383curl "https://api.openai.com/v1/agents/sessions/sess_123/turns?limit=20&order=desc" \384 -H "OpenAI-Beta: agents=v1" \385 -H "Authorization: Bearer $OPENAI_API_KEY"386387curl "https://api.openai.com/v1/agents/sessions/sess_123/turns/turn_123" \388 -H "OpenAI-Beta: agents=v1" \389 -H "Authorization: Bearer $OPENAI_API_KEY" | jq '.subagent_id'390```391392393Use the returned `last_id` as the next page's `after` value when `has_more` is `true`.394395Command items contain `turn_id`. Retrieve that turn and read `subagent_id` to identify the delegated agent that ran the command. A `null` subagent ID identifies root-agent work. Command-output truncation is not reported.396397## Inspect a turn trace398399Use the Platform dashboard to inspect a completed turn and its agent activity.400To retrieve recorded traces through the public API, use the [session trace export endpoint](https://developers.openai.com/api/docs/guides/agents-api/tracing#export-session-traces) with a project API key. Dashboard trace endpoints remain separate from the supported customer API.401402Turn resources include best-effort `usage` and a `subagent_id` that identifies delegated work. Usage can be `null` when unknown and may change. See [Inspect subagent token usage](#inspect-subagent-token-usage).403404To attribute a shell command, retrieve the turn identified by its command item's405`turn_id`, then inspect `turn.subagent_id`. The customer API does not indicate406whether command output was truncated.407408## Model usage and cost409410An agent may make several model calls while completing a task. Each call follows the model's [token pricing](https://developers.openai.com/api/docs/pricing) and [prompt-caching rules](https://developers.openai.com/api/docs/guides/prompt-caching), as in the Responses API. Estimate cost across all calls needed to complete the task.411412### What contributes to cost?413414Each model call can consume:415416- **Input tokens:** agent instructions, tool definitions, conversation history, user input, files or images, and tool results.417- **Cached input tokens:** input reused from a matching prompt prefix, billed at the model's cached-input rate.418- **Output tokens:** generated text, tool-call arguments, and reasoning.419420Reasoning tokens are billed as output tokens.421422Subagents can also make model calls. Inspect their recorded [turn usage](#inspect-subagent-token-usage) alongside root-agent work when investigating model costs.423424Account for root-agent and subagent work, including retries, plus any applicable tool, sandbox compute, and third-party service charges. For models with cache-write pricing, writing input to the cache also has a cost. The Agents API usage fields below do not expose a separate cache-write count, so they cannot determine the exact model charge when that pricing applies.425426### Prompt caching427428Agents carry context forward within a session. When successive model calls share the same prompt prefix, prompt caching can reuse its earlier processing. The model generates a new response; caching does not replay an old answer. Maintaining a session does not guarantee a cache hit. Reuse depends on a matching prefix and the model's cache eligibility and lifetime rules.429430Keep initial instructions and tool definitions stable where practical, and put new task details in follow-up messages. With [tool search](https://developers.openai.com/api/docs/guides/tools-tool-search#agents-api), discovered definitions are added at the end of the conversation, preserving earlier content for cache reuse. See [Prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching) for model-specific rules.431432A high cached-input percentage does not measure savings on the total task cost. Cached input is still billed, and repeated calls can process a large history. Compare the cost of completing the same task at the quality and latency your application needs.433434### Understand token usage435436Session and turn resources expose best-effort `usage`. It can be `null` when unknown, and recorded counts may change as accounting arrives. Missing usage does not mean zero usage. These counts are not a final bill.437438A recorded usage object contains these token categories:439440```json441{442 "input_tokens": 5000,443 "input_tokens_details": {444 "cached_tokens": 1500445 },446 "output_tokens": 900,447 "output_tokens_details": {448 "reasoning_tokens": 200449 },450 "total_tokens": 5900451}452```453454In this example, the agent processed 5,000 input tokens and generated 900 output tokens. Of the input tokens, 1,500 were cached. Of the output tokens, 200 were reasoning tokens.455456Cached tokens are included in `input_tokens`, and reasoning tokens are included in `output_tokens`.457458### Inspect subagent token usage459460List or retrieve [session turns](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#inspect-session-turns) and inspect each turn's `usage`. The `subagent_id` identifies the subagent; it is `null` for root-agent turns. When `has_more` is `true`, pass `last_id` as `after` with the same `order` to read the remaining turns.461462Usage is best-effort: it can be `null` when unknown, and recorded values may change. You can also inspect each agent's recorded usage in the [tracing dashboard](https://developers.openai.com/api/docs/guides/agents-api/tracing#token-usage).