SpyBara
Go Premium

Documentation 2026-09-10 18:01 UTC to 2026-09-11 20:00 UTC

111 files changed +9,018 −1,054. View all changes and history on the product overview
2026
Tue 29 22:57 Mon 28 22:57 Sat 26 23:59 Fri 25 23:58 Thu 24 23:58 Wed 23 23:58 Tue 22 23:57 Mon 21 23:00 Sat 19 23:00 Fri 18 22:59 Thu 17 10:04 Wed 16 20:58 Tue 15 22:59 Mon 14 22:58 Sun 13 15:02 Fri 11 20:00 Thu 10 18:01 Wed 9 23:59 Sat 5 17:01 Fri 4 23:59 Thu 3 23:00 Wed 2 22:59
Details

284The example below shows how thread history could be migrated before the sunset. The Assistants API call that retrieves thread messages no longer works; use your stored messages instead.284The example below shows how thread history could be migrated before the sunset. The Assistants API call that retrieves thread messages no longer works; use your stored messages instead.

285 285 

286```python286```python

287import os287# Replace the illustrative IDs and URLs below with your own resource values.

288 288 

289from openai import OpenAI289from openai import OpenAI

290 290 

291openai = OpenAI()291openai = OpenAI()

292messages = []292messages = []

293thread_id = os.environ["OPENAI_THREAD_ID"]293thread_id = "thread_123"

294 294 

295for page in openai.beta.threads.messages.list(295for page in openai.beta.threads.messages.list(

296 thread_id=thread_id, order="asc"296 thread_id=thread_id, order="asc"


326```326```

327 327 

328```ruby328```ruby

329# Replace the illustrative IDs and URLs below with your own resource values.

329require "openai"330require "openai"

330 331 

331client = OpenAI::Client.new332client = OpenAI::Client.new

332thread_id = ENV.fetch("OPENAI_THREAD_ID")333thread_id = "thread_123"

333messages = client.beta.threads.messages.list(thread_id, order: :asc)334messages = client.beta.threads.messages.list(thread_id, order: :asc)

334items = []335items = []

335messages.auto_paging_each do |message|336messages.auto_paging_each do |message|


341 else342 else

342 :output_text343 :output_text

343 end344 end

344 {type: type, text: part.text.value}345 {

346 type: type,

347 text: part.text.value

348 }

345 when OpenAI::Models::Beta::Threads::ImageURLContentBlock349 when OpenAI::Models::Beta::Threads::ImageURLContentBlock

346 {350 {

347 type: :input_image,351 type: :input_image,


350 }354 }

351 end355 end

352 end356 end

353 items << {role: message.role, content: content}357 items << {

358 role: message.role,

359 content: content

360 }

354end361end

355conversation = client.conversations.create(362conversation = client.conversations.create(

356 items: items363 items: items


370Assistants API377Assistants API

371 378 

372```python379```python

380# Replace the illustrative IDs and URLs below with your own resource values.

373threads_by_session: dict[str, str] = {}381threads_by_session: dict[str, str] = {}

374 382 

375 383 


386 content=message.content,394 content=message.content,

387 )395 )

388 396 

397 example_assistant_id = "asst_123"

389 run = openai.beta.threads.runs.create(398 run = openai.beta.threads.runs.create(

390 assistant_id=os.environ["OPENAI_ASSISTANT_ID"],399 assistant_id=example_assistant_id,

391 thread_id=thread_id,400 thread_id=thread_id,

392 )401 )

393 while run.status in ("queued", "in_progress"):402 while run.status in ("queued", "in_progress"):


407```416```

408 417 

409```ruby418```ruby

419# Replace the illustrative IDs and URLs below with your own resource values.

410require "openai"420require "openai"

411 421 

412client = OpenAI::Client.new422client = OpenAI::Client.new

413assistant_id = ENV.fetch("OPENAI_ASSISTANT_ID")423assistant_id = "asst_123"

414threads_by_session = {}424threads_by_session = {}

415 425 

416handle_message = lambda do |session_id:, content:|426handle_message = lambda do |session_id:, content:|


439 order: :desc,449 order: :desc,

440 limit: 1450 limit: 1

441 )451 )

442 {content: messages.data&.first&.content}452 { content: messages.data&.first&.content }

443end453end

444 454 

445puts(handle_message.call(455puts(

456 handle_message.call(

446 session_id: "example-session",457 session_id: "example-session",

447 content: "What are the five Ds of dodgeball?"458 content: "What are the five Ds of dodgeball?"

448))459 )

460)

449```461```

450 462 

451 463 


457Responses API469Responses API

458 470 

459```javascript471```javascript

472// Replace the illustrative IDs and URLs below with your own resource values.

460import express from "express";473import express from "express";

461import OpenAI from "openai";474import OpenAI from "openai";

462 475 


494 }507 }

495 const conversationId = await conversationIdPromise;508 const conversationId = await conversationIdPromise;

496 509 

497 const promptId = process.env.OPENAI_PROMPT_ID;510 const promptId = "pmpt_123";

498 if (!promptId) {

499 response.status(500).json({ error: "OPENAI_PROMPT_ID is required." });

500 return;

501 }

502 511 

503 const result = await client.responses.create({512 const result = await client.responses.create({

504 prompt: { id: promptId },513 prompt: { id: promptId },


513```522```

514 523 

515```python524```python

525# Replace the illustrative IDs and URLs below with your own resource values.

516conversations_by_session: dict[str, str] = {}526conversations_by_session: dict[str, str] = {}

517 527 

518 528 


523 conversation_id = openai.conversations.create().id533 conversation_id = openai.conversations.create().id

524 conversations_by_session[message.session_id] = conversation_id534 conversations_by_session[message.session_id] = conversation_id

525 535 

536 example_prompt_id = "pmpt_123"

526 response = openai.responses.create(537 response = openai.responses.create(

527 prompt={"id": os.environ["OPENAI_PROMPT_ID"]},538 prompt={"id": example_prompt_id},

528 input=[{"role": "user", "content": message.content}],539 input=[{"role": "user", "content": message.content}],

529 conversation=conversation_id,540 conversation=conversation_id,

530 )541 )


533```544```

534 545 

535```ruby546```ruby

547# Replace the illustrative IDs and URLs below with your own resource values.

536require "openai"548require "openai"

537 549 

538client = OpenAI::Client.new550client = OpenAI::Client.new


546 end558 end

547 559 

548 response = client.responses.create(560 response = client.responses.create(

549 prompt: {id: ENV.fetch("OPENAI_PROMPT_ID")},561 prompt: { id: "pmpt_123" },

550 input: [{role: :user, content: content}],562 input: [

563 {

564 role: :user,

565 content: content

566 }

567 ],

551 conversation: conversation_id568 conversation: conversation_id

552 )569 )

553 {content: response.output_text}570 { content: response.output_text }

554end571end

555 572 

556puts(handle_message.call(573puts(

574 handle_message.call(

557 session_id: "example-session",575 session_id: "example-session",

558 content: "What are the five Ds of dodgeball?"576 content: "What are the five Ds of dodgeball?"

559))577 )

578)

560```579```

guides/agents.md +29 −77

Details

1# Agents SDK1# Agents

2 2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 4 

5Agents are applications that plan, call tools, collaborate across specialists, and keep enough state to complete multi-step work.5Agents can plan and complete tasks using tools, work with other agents, and maintain context across steps. Choose a runtime based on where you want orchestration to run and who should manage the state between tasks.

6 

7## Get your first agent running

8 

9Start with the [Agents SDK quickstart](https://developers.openai.com/api/docs/guides/agents/quickstart) to install the SDK, define one agent, and run it. Once that works, return here to choose the next capability your application needs.

10 

11## Get the Agents SDK

12 

13Use the GitHub repositories for more examples, issues, and language-specific reference details.

14 

15 

16 

17 [TypeScript SDK

18 

19 

20 

21 Open the TypeScript SDK repository on GitHub.](https://github.com/openai/openai-agents-js)

22 [Python SDK

23 

24 

25 

26 Open the Python SDK repository on GitHub.](https://github.com/openai/openai-agents-python)

27 

28 

29 6 

30## Choose your starting point7## Choose your starting point

31 8 

32| If you want to | Start here | Why |9| You want to | Start here |

33| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- |10| ------------------------------------------------------------------------------------ | ------------------------------------------------------ |

34| Build a code-first agent app | [Quickstart](https://developers.openai.com/api/docs/guides/agents/quickstart) | This is the shortest path to a working SDK integration. |11| Run an agent with the Codex harness managed by OpenAI | [Agents API](https://developers.openai.com/api/docs/guides/agents-api/quickstart) |

35| Define one specialist cleanly | [Agent definitions](https://developers.openai.com/api/docs/guides/agents/define-agents) | Start here when you are still shaping the contract for a single agent. |12| Control the agent loop in your application with reusable agents, tools, and handoffs | [Agents SDK](https://developers.openai.com/api/docs/guides/agents/quickstart) |

36| Choose models, defaults, and transport | [Models and providers](https://developers.openai.com/api/docs/guides/agents/models) | Use this when model choice, provider setup, or transport strategy affects the workflow. |13| Work directly with model responses and control your integration | [Responses API](https://developers.openai.com/api/docs/guides/migrate-to-responses) |

37| Understand the runtime loop and state | [Running agents](https://developers.openai.com/api/docs/guides/agents/running-agents) | This is where the agent loop, streaming, and continuation strategies live. |14| Add an embedded chat experience | [ChatKit](https://developers.openai.com/api/docs/guides/chatkit) |

38| Run work in a container-based environment | [Sandbox agents](https://developers.openai.com/api/docs/guides/agents/sandboxes) | Use this when the agent needs files, commands, packages, snapshots, mounts, or provider links. |

39| Design specialist ownership | [Orchestration and handoffs](https://developers.openai.com/api/docs/guides/agents/orchestration) | Use this when you need more than one agent and must decide who owns the reply. |

40| Add validation or human review | [Guardrails and human review](https://developers.openai.com/api/docs/guides/agents/guardrails-approvals) | Use this when the workflow should block or pause before risky work continues. |

41| Understand what a run returns | [Results and state](https://developers.openai.com/api/docs/guides/agents/results) | This page explains final output, resumable state, and next-turn surfaces. |

42| Add hosted tools, function tools, or MCP | [Using tools](https://developers.openai.com/api/docs/guides/tools#usage-in-the-agents-sdk) and [Integrations and observability](https://developers.openai.com/api/docs/guides/agents/integrations-observability) | Tool semantics live in the platform tools docs; SDK-specific MCP and tracing live here. |

43| Inspect and improve runs | [Integrations and observability](https://developers.openai.com/api/docs/guides/agents/integrations-observability) and [evaluate agent workflows](https://developers.openai.com/api/docs/guides/agent-evals) | Use traces for debugging first, then move into evaluation loops. |

44| Build a voice-first workflow | [Voice agents](https://developers.openai.com/api/docs/guides/voice-agents) | Use the SDK's voice pipeline and realtime agent patterns. |

45 

46## Build with the SDK

47 

48Use the SDK track when your server owns deployment, tool implementations, state storage, and approval decisions, while the SDK runs the agent loop and invokes those tools. That path is the best fit when you want:

49 

50- typed application code in TypeScript or Python

51- direct control over tools, MCP servers, and runtime behavior

52- custom storage or server-managed conversation strategies

53- tight integration with existing product logic or infrastructure

54 

55A typical SDK reading order is:

56 15 

57- Start with [Quickstart](https://developers.openai.com/api/docs/guides/agents/quickstart) to get one working run on screen.16<a id="agents-sdk-vs-responses-api"></a>

58- Use [Agent definitions](https://developers.openai.com/api/docs/guides/agents/define-agents) and [Models and providers](https://developers.openai.com/api/docs/guides/agents/models) to shape one specialist cleanly.

59- Continue to [Running agents](https://developers.openai.com/api/docs/guides/agents/running-agents), [Orchestration and handoffs](https://developers.openai.com/api/docs/guides/agents/orchestration), and [Guardrails and human review](https://developers.openai.com/api/docs/guides/agents/guardrails-approvals) as the workflow grows more complex.

60- Use [Results and state](https://developers.openai.com/api/docs/guides/agents/results) and [Integrations and observability](https://developers.openai.com/api/docs/guides/agents/integrations-observability) when application logic depends on the run object or deeper visibility into behavior.

61 17 

62## Agents SDK vs. Responses API18<a id="compare-agent-runtimes"></a>

63 19 

64Use the Responses API when you want to own the loop. Use the Agents SDK when you want the SDK to run it.20## Compare agent runtime options

65 21 

66### Choose the Responses API when22| | Agents API | Agents SDK | Responses API |

23| ------------------------ | ------------------------------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------- |

24| **Use for** | Long-running tasks where OpenAI manages the agent and saves its progress | Building agents with custom tools and workflows in your application | Calling models directly or building an agent from scratch |

25| Where the agent runs | OpenAI runs a managed Codex harness | The SDK runs inside your application | Your application, with optional hosted orchestration |

26| Agent integration effort | Low | Medium | High |

27| State between tasks | Saved session configuration, turns, and items | Your storage and SDK sessions, or Responses conversation state | Manual history, response chaining, or Conversations |

28| Tool execution | Service-connected tools, application function handlers, and an optional sandbox | Tools and integrations configured in your application | Hosted tools and tools your application runs |

29| Execution environment | OpenAI hosted sandbox, self-hosted sandbox, or no sandbox | Your runtime and sandbox provider integrations | Your own execution environment |

30| Start here | [Agents API overview](https://developers.openai.com/api/docs/guides/agents-api/overview) | [Agents SDK overview](https://developers.openai.com/api/docs/guides/agents/sdk) | [Responses guide](https://developers.openai.com/api/docs/guides/migrate-to-responses) |

67 31 

68- You want direct control over model interactions, output items, tools, state, and orchestration, whether the workflow takes one call or many.32The Agents API runs the Codex harness and manages the underlying agent infrastructure so you can focus on what your agents do. It includes automatic context compaction, multi-agent orchestration, programmatic tool calling, and support for MCP servers. See [Architecture](https://developers.openai.com/api/docs/guides/agents-api/architecture).

69- You want to implement custom tool routing, loops, or branching directly in your application.

70 33 

71In the [Responses function-calling flow](https://developers.openai.com/api/docs/guides/function-calling#the-tool-calling-flow), your application receives function calls, executes them, returns their output, and calls the model again.34The Agents SDK gives your application control over deployment, storage, approvals, and runtime integration. Its runner handles the agent loop and handoffs. See [Running agents](https://developers.openai.com/api/docs/guides/agents/running-agents).

72 35 

73For example, a Responses API workflow might search a knowledge base and generate a cited answer.

74 36 

75### Choose the Agents SDK when

76 37 

77- You want the SDK to manage the agent loop and recurring orchestration such as repeated tool calls or branching.

78- Different specialists need different instructions, tools, or policies.

79- You want built-in sessions, tracing, guardrails, or resumable approval flows.

80 38 

81The [Agents SDK runner](https://developers.openai.com/api/docs/guides/agents/running-agents#the-agent-loop) performs the tool loop, switches agents after handoffs, and stops when the run finishes or pauses for approval.39## Add tools, skills, and prompt caching

82 40 

83For example, an Agents SDK workflow might investigate a support request, hand it to the correct specialist, call internal systems, request approval for a refund, and record the result.41Tool design, reusable skills, and prompt caching apply across agent workflows. Their configuration and lifecycle can differ by API.

84 42 

85### Compare the Responses API and Agents SDK43- Start with [Using tools](https://developers.openai.com/api/docs/guides/tools) for function calling, MCP, and hosted capabilities.

44- Read [Programmatic Tool Calling](https://developers.openai.com/api/docs/guides/tools-programmatic-tool-calling) for orchestration with JavaScript and the configuration for each API.

45- Use [Skills](https://developers.openai.com/api/docs/guides/tools-skills) for reusable instructions and the supported loading mechanisms.

46- Read [Prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching) for shared caching behavior, then [Agents API observability and usage](https://developers.openai.com/api/docs/guides/agents-api/observability) for session accounting.

86 47 

87| | Responses API | Agents SDK |48An Agents API session, an SDK session, a Responses conversation, and a sandbox are different resources. Follow the state and cleanup instructions for the runtime you choose.

88| -------------------------- | ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

89| **Best for** | Custom model-powered features and workflows | Bounded conversational or transactional workflows with defined tools and recurring orchestration patterns |

90| **Core abstraction** | A model response | An agent run |

91| **Tools** | Platform tools, function calling, and remote [Model Context Protocol (MCP)](https://developers.openai.com/api/docs/guides/tools-connectors-mcp) | Platform tools attached to reusable agents, plus tool wrappers, local MCP connections, and [agents as tools](https://developers.openai.com/api/docs/guides/agents/orchestration#use-agents-as-tools-for-manager-style-workflows) |

92| **Workflow orchestration** | You manage custom loops and branching | The SDK provides the agent loop and lifecycle |

93| **Multi-agent workflows** | Build routing and delegation yourself | Built-in agents-as-tools and [handoffs](https://developers.openai.com/api/docs/guides/agents/orchestration#use-handoffs-for-delegated-ownership) |

94| **State** | Manual history, response chaining, or [Conversations](https://developers.openai.com/api/docs/guides/conversation-state#using-the-conversations-api) | The same options, plus [SDK sessions and resumable run state](https://developers.openai.com/api/docs/guides/agents/running-agents#choose-one-conversation-strategy) |

95| **Safety and approvals** | Tool-specific approvals; you build broader controls | Input, output, and tool [guardrails plus resumable approval flows](https://developers.openai.com/api/docs/guides/agents/guardrails-approvals) |

96| **Debugging and tracing** | Response objects and API logs | [Built-in traces](https://developers.openai.com/api/docs/guides/agents/integrations-observability#tracing) across model calls, tools, agents, guardrails, and handoffs |

Details

1# Architecture

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5OpenAI runs the agent harness. Your application sends it work and receives results. Add an environment when the agent needs compute or files.

6 

7## The pieces

8 

9- **Harness:** The OpenAI-hosted Codex instance that runs the model and tool loop and maintains the agent's session.

10- **Environment:** Where the agent runs commands, executes code, and works with files. An environment can be a remote sandbox, your laptop, a Docker container, or an AWS Lambda function.

11- **Application server:** Your code that connects the agent to your product. It submits tasks, receives events, and handles function tools. When you provide the environment, your code also manages its lifecycle.

12 

13Start with the pieces your task needs. The harness can work without an environment, and your application can receive progress through streaming or webhooks.

14 

15## Start without an environment

16 

17An agent that answers questions or uses tools to access external services may not need its own compute or files. Set `environment.type` to `none`. This fragment shows the environment setting. Session creation also needs an agent and initial input:

18 

19```json

20{

21 "environment": {

22 "type": "none"

23 }

24}

25```

26 

27Your application sends input to a session. The harness calls the model, uses the configured tools, and returns results. OpenAI maintains the session for later work.

28 

29The harness can call remote MCP tools directly. For [function tools](https://developers.openai.com/api/docs/guides/agents-api/tools/functions), your code receives each call, runs the function, and returns its result.

30 

31Without an environment, the built-in Bash and apply-patch tools, workspace files, and executor MCPs are unavailable.

32 

33<picture>

34 <source

35 media="(max-width: 640px)"

36 srcSet="/images/api/agents-api/architectures-4-mobile.webp"

37 width="680"

38 height="1260"

39 />

40 <img src="https://developers.openai.com/images/api/agents-api/architectures-4.webp"

41 width="1400"

42 height="844"

43 alt="With no sandbox, the application supplies function tools or a virtual shell, and the Agents API can call remote MCP servers. There is no executor or built-in shell."

44 loading="lazy"

45 />

46</picture>

47 

48The optional virtual runtime shown here provides files and shell commands through your application's function tools.

49 

50## Add an OpenAI-hosted environment

51 

52When the agent needs to run scripts, edit files, or create artifacts, set `environment.type` to `openai_hosted`. OpenAI creates and manages a sandbox for the session.

53 

54You configure the packages, files, and network access the agent needs. The harness runs commands in the sandbox directly. Your application continues to send tasks, receive events, and handle any function tools.

55 

56<picture>

57 <source

58 media="(max-width: 640px)"

59 srcSet="/images/api/agents-api/architectures-1-mobile.webp"

60 width="680"

61 height="1348"

62 />

63 <img src="https://developers.openai.com/images/api/agents-api/architectures-1.webp"

64 width="1400"

65 height="700"

66 alt="An application starts sessions and receives events from the Agents API, which runs the managed Codex harness and exchanges tool calls and results with a sandbox. The application controls compute only for self-hosted sandboxes."

67 loading="lazy"

68 />

69</picture>

70 

71The dashed arrow applies only when you manage the environment yourself, as described below.

72 

73See [OpenAI-hosted environments](https://developers.openai.com/api/docs/guides/agents-api/environments/openai-hosted) for configuration options.

74 

75## Connect your own environment

76 

77Use `environment.type: "self_hosted"` when the agent needs your infrastructure, private network, or custom software.

78 

79Your code starts the environment and connects an executor to the session. The executor runs the commands and tools that the harness requests. Your application manages the connection and lifecycle without forwarding each command.

80 

81You own provisioning, reconnection, shutdown, and any files you need to preserve. Your application server or a webhook handler can manage this work.

82 

83<picture>

84 <source

85 media="(max-width: 640px)"

86 srcSet="/images/api/agents-api/architectures-2-mobile.webp"

87 width="680"

88 height="1560"

89 />

90 <img src="https://developers.openai.com/images/api/agents-api/architectures-2.webp"

91 width="1400"

92 height="1320"

93 alt="The application creates a self-hosted session, starts compute, and connects an executor. It receives events and checks the turn outcome before stopping compute."

94 loading="lazy"

95 />

96</picture>

97 

98Before stopping compute, coordinate incoming work and confirm that no execution is pending.

99 

100See [Connect a sandbox](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted) and [Sandbox lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle) for setup and shutdown requirements.

101 

102## Receive progress and results

103 

104With any environment choice, you can use either or both of these:

105 

106- **Streaming:** Receive detailed events as the agent works, such as output to display in your product.

107- **Webhooks:** Receive session state changes without keeping a stream open. Your handler can retrieve results, run function tools, or manage a self-hosted environment.

108 

109Function tools need a handler that receives calls and returns results. If that handler is unavailable, the agent can remain waiting for a result. Failures in your event or lifecycle handlers can also interrupt progress updates or environment management.

110 

111See [Session events](https://developers.openai.com/api/docs/guides/agents-api/sessions) and [Webhooks](https://developers.openai.com/api/docs/guides/agents-api/sessions/webhooks) for integration details.

Details

1# Configuring Agents

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5An agent configuration defines how the agent behaves. You can supply it when creating a session or save it for reuse. The session holds the conversation and work, while the saved agent holds reusable settings.

6 

7## Define the agent's behavior

8 

9Start with the model and instructions, then add the tools and controls your task needs:

10 

11- **Model:** Which model does the work.

12- **Instructions:** What the agent should do and how it should behave.

13- **Tools:** What actions the agent can take, such as searching the web or calling your functions.

14- **Reasoning and output:** How much reasoning the model uses and the format and detail of its responses.

15 

16Pass these settings in `agent` when you create a session. This example supplies a model, instructions, and the first user message:

17 

18Configure an agent for one session

19 

20```javascript

21import OpenAI from "openai";

22const client = new OpenAI();

23 

24const session = await client.beta.agents.sessions.create({

25 agent: {

26 model: "gpt-6-astra",

27 instructions: "Answer the user clearly and concisely.",

28 },

29 environment: {

30 type: "none",

31 },

32 input: [

33 {

34 role: "user",

35 content: [

36 {

37 type: "input_text",

38 text: "What can you help with?",

39 },

40 ],

41 },

42 ],

43});

44 

45console.log(session);

46```

47 

48```python

49from openai import OpenAI

50 

51client = OpenAI()

52 

53session = client.beta.agents.sessions.create(

54 agent={

55 "model": "gpt-6-astra",

56 "instructions": "Answer the user clearly and concisely.",

57 },

58 environment={"type": "none"},

59 input=[

60 {

61 "role": "user",

62 "content": [{"type": "input_text", "text": "What can you help with?"}],

63 }

64 ],

65)

66print(session.to_json())

67```

68 

69```go

70import (

71 "context"

72 "fmt"

73 

74 "github.com/openai/openai-go/v3"

75)

76 

77ctx := context.Background()

78client := openai.NewClient()

79result, err := client.Beta.Agents.Sessions.New(ctx,

80 openai.BetaAgentSessionNewParams{

81 Agent: openai.BetaAgentSessionNewParamsAgent{

82 Model: openai.String("gpt-6-astra"),

83 Instructions: openai.String("Answer the user clearly and concisely."),

84 },

85 Environment: openai.EnvironmentParamUnion{OfParamNone: &openai.EnvironmentParamNone{}},

86 Input: openai.BetaAgentSessionNewParamsInputUnion{

87 OfArrayOfInputMessages: []openai.AgentSessionInputMessageParam{

88 {

89 Content: []openai.InputContentParamUnion{

90 {

91 OfParamInputText: &openai.InputContentParamInputText{Text: "What can you help with?"},

92 },

93 },

94 },

95 },

96 },

97 })

98if err != nil {

99 panic(err)

100}

101fmt.Println(result)

102```

103 

104```java

105import com.openai.client.OpenAIClient;

106import com.openai.client.okhttp.OpenAIOkHttpClient;

107import com.openai.models.beta.agents.sessions.SessionCreateParams;

108 

109OpenAIClient client = OpenAIOkHttpClient.fromEnv();

110var result =

111 client

112 .beta()

113 .agents()

114 .sessions()

115 .create(

116 SessionCreateParams.builder()

117 .agent(

118 SessionCreateParams.Agent.builder()

119 .model("gpt-6-astra")

120 .instructions("Answer the user clearly and concisely.")

121 .build())

122 .environmentNone()

123 .input("What can you help with?")

124 .build());

125System.out.println(result);

126```

127 

128```ruby

129require "openai"

130 

131client = OpenAI::Client.new

132result = client.beta.agents.sessions.create(

133 agent: {

134 model: "gpt-6-astra",

135 instructions: "Answer the user clearly and concisely."

136 },

137 environment: { type: "none" },

138 input: [

139 {

140 role: "user",

141 content: [

142 {

143 type: "input_text",

144 text: "What can you help with?"

145 }

146 ]

147 }

148 ]

149)

150puts result

151```

152 

153 

154See the [Agents API reference](https://developers.openai.com/api/reference/resources/beta/subresources/agents) for configuration fields and accepted values. See [Functions](https://developers.openai.com/api/docs/guides/agents-api/tools/functions) and [MCP connections](https://developers.openai.com/api/docs/guides/agents-api/tools/mcp) for tool setup, and [Multi-agent](https://developers.openai.com/api/docs/guides/agents-api/multi-agent) for delegation.

155 

156## Reuse an agent across sessions

157 

158Save an agent to reuse its configuration across sessions. Create it once, then pass its ID as `agent_id` when starting each session:

159 

160Reuse an agent

161 

162```javascript

163import OpenAI from "openai";

164 

165const client = new OpenAI();

166const agent = await client.beta.agents.create({

167 model: "gpt-6-astra",

168 instructions: "Answer technical questions accurately.",

169 reasoning: {

170 summary: "auto",

171 },

172});

173const session = await client.beta.agents.sessions.create({

174 agent_id: agent.id,

175 environment: { type: "none" },

176 input: "Explain how an agent connects to an MCP server.",

177});

178console.log(session);

179```

180 

181```python

182from openai import OpenAI

183 

184client = OpenAI()

185agent = client.beta.agents.create(

186 model="gpt-6-astra",

187 instructions="Answer technical questions accurately.",

188 reasoning={"summary": "auto"},

189 timeout=360,

190)

191session = client.beta.agents.sessions.create(

192 agent_id=agent.id,

193 environment={"type": "none"},

194 input="Explain how an agent connects to an MCP server.",

195)

196print(session.to_json())

197```

198 

199```go

200import (

201 "context"

202 "fmt"

203 

204 "github.com/openai/openai-go/v3"

205)

206 

207ctx := context.Background()

208client := openai.NewClient()

209agent, err := client.Beta.Agents.New(ctx,

210 openai.BetaAgentNewParams{

211 Model: "gpt-6-astra",

212 Instructions: openai.String("Answer technical questions accurately."),

213 Reasoning: openai.AgentReasoningParam{Summary: "auto"},

214 })

215if err != nil {

216 panic(err)

217}

218result, err := client.Beta.Agents.Sessions.New(ctx,

219 openai.BetaAgentSessionNewParams{

220 AgentID: openai.String(agent.ID),

221 Environment: openai.EnvironmentParamUnion{OfParamNone: &openai.EnvironmentParamNone{}},

222 Input: openai.BetaAgentSessionNewParamsInputUnion{OfString: openai.String("Explain how an agent connects to an MCP server.")},

223 })

224if err != nil {

225 panic(err)

226}

227fmt.Println(result)

228```

229 

230```java

231import com.openai.client.OpenAIClient;

232import com.openai.client.okhttp.OpenAIOkHttpClient;

233import com.openai.models.beta.agents.AgentCreateParams;

234import com.openai.models.beta.agents.AgentReasoningParam;

235import com.openai.models.beta.agents.sessions.SessionCreateParams;

236 

237OpenAIClient client = OpenAIOkHttpClient.fromEnv();

238var agent =

239 client

240 .beta()

241 .agents()

242 .create(

243 AgentCreateParams.builder()

244 .model("gpt-6-astra")

245 .instructions("Answer technical questions accurately.")

246 .reasoning(

247 AgentReasoningParam.builder()

248 .summary(AgentReasoningParam.Summary.of("auto"))

249 .build())

250 .build());

251var result =

252 client

253 .beta()

254 .agents()

255 .sessions()

256 .create(

257 SessionCreateParams.builder()

258 .agentId(agent.id())

259 .environmentNone()

260 .input("Explain how an agent connects to an MCP server.")

261 .build());

262System.out.println(result);

263```

264 

265```ruby

266require "openai"

267 

268client = OpenAI::Client.new

269agent = client.beta.agents.create(

270 model: "gpt-6-astra",

271 instructions: "Answer technical questions accurately.",

272 reasoning: { summary: "auto" }

273)

274result = client.beta.agents.sessions.create(

275 agent_id: agent.id,

276 environment: { type: "none" },

277 input: "Explain how an agent connects to an MCP server."

278)

279puts result

280```

281 

282 

283Each session has its own conversation and work. See the [Agents API reference](https://developers.openai.com/api/reference/resources/beta/subresources/agents) to list, retrieve, update, or delete saved agents. Credentials stay in [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults), separate from the saved configuration.

284 

285## Override settings for one session

286 

287Include both `agent_id` and `agent` to customize a session that uses a saved agent. The session inherits omitted settings, including the model.

288 

289Replace the illustrative `agent_123` value with the saved agent's ID before running this example:

290 

291Override an agent for one session

292 

293```javascript

294// Replace the illustrative IDs and URLs below with your own resource values.

295import OpenAI from "openai";

296const client = new OpenAI();

297 

298const agentId = "agent_123";

299const session = await client.beta.agents.sessions.create({

300 agent_id: agentId,

301 agent: {

302 instructions: "Answer this question in one concise paragraph.",

303 },

304 environment: {

305 type: "none",

306 },

307 input: [

308 {

309 role: "user",

310 content: [

311 {

312 type: "input_text",

313 text: "Explain how an agent connects to an MCP server.",

314 },

315 ],

316 },

317 ],

318});

319 

320console.log(session);

321```

322 

323```python

324# Replace the illustrative IDs and URLs below with your own resource values.

325from openai import OpenAI

326 

327client = OpenAI()

328 

329agent_id = "agent_123"

330session = client.beta.agents.sessions.create(

331 agent_id=agent_id,

332 agent={"instructions": "Answer this question in one concise paragraph."},

333 environment={"type": "none"},

334 input=[

335 {

336 "role": "user",

337 "content": [

338 {

339 "type": "input_text",

340 "text": "Explain how an agent connects to an MCP server.",

341 }

342 ],

343 }

344 ],

345)

346print(session.to_json())

347```

348 

349```go

350// Replace the illustrative IDs and URLs below with your own resource values.

351import (

352 "context"

353 "fmt"

354 

355 "github.com/openai/openai-go/v3"

356)

357 

358ctx := context.Background()

359client := openai.NewClient()

360result, err := client.Beta.Agents.Sessions.New(ctx,

361 openai.BetaAgentSessionNewParams{

362 AgentID: openai.String("agent_123"),

363 Agent: openai.BetaAgentSessionNewParamsAgent{Instructions: openai.String("Answer this question in one concise paragraph.")},

364 Environment: openai.EnvironmentParamUnion{OfParamNone: &openai.EnvironmentParamNone{}},

365 Input: openai.BetaAgentSessionNewParamsInputUnion{

366 OfArrayOfInputMessages: []openai.AgentSessionInputMessageParam{

367 {

368 Content: []openai.InputContentParamUnion{

369 {

370 OfParamInputText: &openai.InputContentParamInputText{Text: "Explain how an agent connects to an MCP server."},

371 },

372 },

373 },

374 },

375 },

376 })

377if err != nil {

378 panic(err)

379}

380fmt.Println(result)

381```

382 

383```java

384// Replace the illustrative IDs and URLs below with your own resource values.

385import com.openai.client.OpenAIClient;

386import com.openai.client.okhttp.OpenAIOkHttpClient;

387import com.openai.models.beta.agents.sessions.SessionCreateParams;

388 

389OpenAIClient client = OpenAIOkHttpClient.fromEnv();

390var result =

391 client

392 .beta()

393 .agents()

394 .sessions()

395 .create(

396 SessionCreateParams.builder()

397 .agentId("agent_123")

398 .agent(

399 SessionCreateParams.Agent.builder()

400 .instructions("Answer this question in one concise paragraph.")

401 .build())

402 .environmentNone()

403 .input("Explain how an agent connects to an MCP server.")

404 .build());

405System.out.println(result);

406```

407 

408```ruby

409# Replace the illustrative IDs and URLs below with your own resource values.

410require "openai"

411 

412client = OpenAI::Client.new

413result = client.beta.agents.sessions.create(

414 agent_id: "agent_123",

415 agent: { instructions: "Answer this question in one concise paragraph." },

416 environment: { type: "none" },

417 input: [

418 {

419 role: "user",

420 content: [

421 {

422 type: "input_text",

423 text: "Explain how an agent connects to an MCP server."

424 }

425 ]

426 }

427 ]

428)

429puts result

430```

431 

432 

433Overrides apply only to that session. They do not change the saved agent or other sessions. Supplied objects and arrays replace the entire field rather than merging with the saved value. For example, supplying `tools` replaces the saved tool list.

434 

435See the [Create session reference](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/methods/create) for request fields.

436 

437## Environment settings

438 

439Set `environment` alongside `agent` when creating a session. It determines where the agent runs commands and works with files.

440 

441 

442 

443 

444 

445 

446 

447 

448 

449 

450 

451 

452Choose `none`, `openai_hosted`, or `self_hosted`. [Architecture](https://developers.openai.com/api/docs/guides/agents-api/architecture) explains when to use each option and who manages the environment.

453 

454For an OpenAI-hosted environment, configure the packages, initial files, and network access the task needs. You can reuse an environment template across sessions. For a self-hosted environment, prepare your compute and [connect an executor](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted).

455 

456See the [Create session reference](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/methods/create) for environment fields and [Plugins](https://developers.openai.com/api/docs/guides/agents-api/tools/plugins) for skills, plugins, and templates. See [Session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files) for files you want to keep after execution.

Details

1# Files and artifacts

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5## Files and published artifacts

6 

7Files live in the agent's environment. An artifact is a published copy of a file from an OpenAI-hosted environment. You can download that copy after the environment expires.

8 

9| Environment | How to retrieve files |

10| --------------- | ------------------------------------------------------------------------------------------------------------------------- |

11| `self_hosted` | Use your provider's file API or mounted filesystem. |

12| `openai_hosted` | Use the session Artifacts API for files under `/workspace/outputs`. |

13| `none` | No environment filesystem. Read output from [session items](https://developers.openai.com/api/docs/guides/agents-api/sessions#retrieve-session-items). |

14 

15## Upload files

16 

17For an OpenAI-hosted environment, supply input files in `environment.files` when you create the session. Choose each file's destination under `/workspace`.

18 

19Use `type: "file_id"` with a `file_id` from the [Files API](https://developers.openai.com/api/reference/resources/files/methods/create), or `type: "inline"` with base64-encoded `data`. Both forms require a `path`.

20 

21To add files after the environment connects, use the [environment Files API](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/environments/subresources/files/methods/create).

22 

23## Retrieve your files

24 

25 

26 

27 

28### From your own environment

29 

30Ask the agent to write its output to a known path. After the turn completes, retrieve the file through your provider or infrastructure. Save it in your application's storage before the environment expires or you delete it.

31 

32Files from self-hosted environments are not published through the Artifacts API, including files under `/workspace/outputs`. See [Sandbox providers](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#sandbox-providers) for provider-specific file access.

33 

34 

35 

36 

37 

38 

39 

40 

41### From an OpenAI-hosted environment

42 

43Ask the agent to save the file under `/workspace/outputs`, such as `/workspace/outputs/report.pdf`. OpenAI publishes outputs as immutable artifacts when the turn completes.

44 

45Pass your API client, session ID, completed turn ID, artifact path, and local destination to this function. It lists artifacts and downloads the file matching both the turn and path:

46 

47Find and download an artifact

48 

49```python

50# Pass the saved session ID, completed turn ID, artifact path, and local destination.

51def download_artifact(client, session_id, turn_id, path, destination):

52 for artifact in client.beta.agents.sessions.artifacts.list(session_id):

53 if artifact.turn_id != turn_id or artifact.path != path:

54 continue

55 with client.beta.agents.sessions.artifacts.with_streaming_response.content(

56 artifact.id, session_id=session_id

57 ) as response:

58 response.stream_to_file(destination)

59 return

60 raise FileNotFoundError(f"No artifact for {path!r} in turn {turn_id}")

61```

62 

63 

64 

65 

66 

67See the [List artifacts](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/subresources/artifacts/methods/list), [Retrieve metadata](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/subresources/artifacts/methods/retrieve), and [Download content](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/subresources/artifacts/methods/content) references for request and response fields.

68 

69### Download multiple files

70 

71The API downloads one artifact per request; it does not provide a batch-download

72endpoint. To download several files, list the artifacts and request each file's

73`content`. For a single download, ask the agent to bundle the results into a ZIP

74file under `/workspace/outputs`, then download that archive as one artifact.

75 

76## File lifetime

77 

78Published artifacts survive environment expiration. Download anything you need to retain before deleting the session.

79 

80Artifacts cannot be uploaded or edited through this API. To publish a new version, ask the agent to update the file and complete another turn. Use the turn ID and path to distinguish versions.

81 

82 

83 

84 

85[Delete an artifact](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/subresources/artifacts/methods/delete) when you no longer need the published copy. Deletion leaves the file in the environment intact.

86 

87## File limits

88 

89| File operation | Limit |

90| -------------------------------------- | ------------------------------------------------ |

91| Files included when creating a session | 50 files per request. |

92| Inline upload | 5 MiB per file, measured before base64 encoding. |

93| Inline uploads in one creation request | 10 MiB total, measured before base64 encoding. |

94| File copied from the Files API | 50 MiB per file. |

95| Published artifact | 200 MiB per file. |

96| Outputs published together | 500 MiB total. |

Details

1# Sandbox lifecycle

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5An agent session can outlive its environment. Your application manages the compute and files used by a `self_hosted` environment.

6 

7 

8 

9 

10 

11 

12## Start an environment

13 

14Your application can start compute after creating a session. Use your [provider's SDK or API](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#sandbox-providers), then [connect the executor](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted) with the session's environment ID and an environment key.

15 

16See the [application-managed sandbox examples](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/application_managed) in the OpenAI Cookbook.

17 

18<picture>

19 <source

20 media="(max-width: 640px)"

21 srcSet="/images/api/agents-api/application-managed-sandboxes-mobile.webp"

22 width="680"

23 height="1296"

24 />

25 <img src="https://developers.openai.com/images/api/agents-api/application-managed-sandboxes.webp"

26 width="1400"

27 height="788"

28 alt="The application sends input, receives events, and controls provider compute. The sandbox executor connects outbound to the Agents API, then exchanges commands and results over the connection."

29 loading="lazy"

30 />

31</picture>

32 

33Use one component to manage each session's environment. Store the mapping between the session and provider compute. Repeated or concurrent requests must not create duplicate environments.

34 

35 

36 

37 

38### Start compute from webhooks

39 

40You can also wait until input needs an environment connection. The API emits `agent.session.action_required` with `required_action.type: "environment_connection"` before waiting for the executor. Your webhook handler starts or reconnects the environment.

41 

42See the [webhook-managed sandbox examples](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/webhook_managed) in the OpenAI Cookbook.

43 

44<picture>

45 <source

46 media="(max-width: 640px)"

47 srcSet="/images/api/agents-api/webhook-managed-sandboxes-mobile.webp"

48 width="680"

49 height="1812"

50 />

51 <img src="https://developers.openai.com/images/api/agents-api/webhook-managed-sandboxes.webp"

52 width="1400"

53 height="1072"

54 alt="The application exchanges input and events with the Agents API. A webhook controller verifies connection requests, checks the current session, and starts or reconnects a provider sandbox. Its executor connects outbound and exchanges commands and results."

55 loading="lazy"

56 />

57</picture>

58 

59 

60 

61 

62Follow [webhook setup](https://developers.openai.com/api/docs/guides/agents-api/sessions/webhooks#set-up-a-webhook) to register your handler for `agent.session.action_required` and `agent.session.failed`. Keep its signing secret and session-read credential separate from the executor's [environment key](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#authentication). If multiple provider handlers share a project, route events to the handler that owns the session.

63 

64 

65 

66 

67The handler and worker have separate jobs:

68 

691. **Verify and queue.** Verify the webhook signature. Queue connection requests only when `data.required_action.type` is `environment_connection`. Also queue session failures. Return a successful HTTP response only after queuing succeeds.

702. **Check current state.** The worker retrieves the session. Ignore deleted sessions and resolved actions. For a self-hosted session that still needs a connection, start or reconnect its executor using `session.environment.id` and `session.environment.remote_url`. For a session that is still failed, release its compute.

71 

72The session stream reports the same request as `agent.session.requires_action`. A `function_call` required action needs a function result, not environment startup. Turn creation and `agent.session.in_progress` events arrive too late to start an offline executor.

73 

74 

75 

76 

77After deploying the handler, [create a self-hosted session](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#create-a-session) and [send input](https://developers.openai.com/api/docs/guides/agents-api/sessions#send-input). Match the working directory and any agent filter configured in your handler. The original submission continues if the executor connects before the deadline.

78 

79 

80 

81 

82## Keep the environment available or stop it

83 

84Keep compute running between turns for reuse, or allow a grace period after a turn ends before stopping it. Coordinate shutdown with incoming work. Cancel a pending shutdown when a connection is requested or execution starts. Recheck state before stopping compute.

85 

86An idle event alone is not a safe shutdown signal. It can arrive when a connection request clears, before waiting input starts its turn. If your application cannot coordinate shutdown with incoming work, keep the environment running.

87 

88 

89 

90 

91 

92 

93## Reconnect after a disconnect

94 

95Connection events report state. Use `agent.session.environment.connected` and `agent.session.environment.disconnected` to observe connections. Setup can also emit `agent.session.environment.pending` or `agent.session.environment.failed`. These events do not request compute. Use the `environment_connection` required action to trigger startup, and check provider health separately.

96 

97A mid-turn disconnect can fail a tool even if the turn completes. Inspect tool results and the agent's final response. The disconnect does not automatically request reconnection through a webhook or restart a killed command. Later input can request reconnection.

98 

99The API waits up to five minutes for an input-time connection. Configure client and proxy timeouts for this wait. If it expires, the submission fails. Initial input can fail asynchronously and leave the session in `failed`.

100 

101The API does not guarantee recovery of pending input after a process crash. Check the request or session outcome before retrying. Do not resubmit while the original request is waiting. A late connection does not replay input that timed out.

102 

103Reusing the environment ID does not restore files in replacement compute. Use provider storage or snapshots to preserve them.

104 

105## Clean up

106 

107Stop accepting new input. Coordinate cleanup with any startup work already in progress to avoid leaving compute running.

108 

109[Delete the session](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#delete-a-session) and stop provider compute separately. Deleting a session neither stops its environment nor emits a deletion webhook.

Details

1# OpenAI-hosted sandboxes

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5An OpenAI-hosted sandbox gives your agent a Linux workspace with Python, Node.js,

6and command-line tools. OpenAI provisions and connects it; your application supplies

7the task and retrieves the results. Choose a [self-hosted sandbox](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted)

8when you need your own image, compute, or private network.

9 

10## Configure the sandbox

11 

12Set `environment.type` to `openai_hosted` and add only the settings your workload

13needs. The working directory is `/workspace`.

14 

15- `packages`: Install Python, system, or global `npm` packages with `python`, `system`, or `npm` lists. Pin versions when needed, such as `pandas==2.2.3`.

16- `setup_commands`: Run ordered shell commands before the agent starts, such as `[{ "command": "mkdir -p reports" }]`. Each command has its own optional `cwd`, defaulting to `/workspace`.

17- `files`: [Supply input files](https://developers.openai.com/api/docs/guides/agents-api/environments/files#upload-files) by Files API ID or inline base64 content.

18- `env`: Set string-valued environment variables. Runtime-reserved names, including `PATH`, `CODEX_*`, and `OPENAI_API_KEY`, are rejected.

19- `skills`, `plugins`, `capability_directories`: Add [skills](https://developers.openai.com/api/docs/guides/tools-skills#agents-api) and [plugins](https://developers.openai.com/api/docs/guides/agents-api/tools/plugins).

20- `environment_template_id`: [Reuse saved configuration](https://developers.openai.com/api/docs/guides/agents-api/tools/plugins#reuse-a-hosted-plugin-setup) across sessions. Omitted settings inherit the template; network overrides cannot broaden its policy.

21 

22Packages and input files are prepared before setup commands run. A nonzero setup

23exit status prevents the agent from starting. Use a setup command to check required

24dependencies or files. Templates save configuration, not a running workspace.

25 

26### Control network access

27 

28| `network.access` | Behavior |

29| ---------------- | -------------------------------------------------------------------------------- |

30| `enabled` | Allow outbound access. This is the default unless you inherit a template policy. |

31| `disabled` | Block outbound access. |

32| `restricted` | Allow only the hosts listed in `allowed_domains`. |

33 

34Restricted mode accepts 1–100 exact host names, such as `api.example.com`.

35Do not include wildcards, protocols, paths, or ports. Subdomains and redirect

36destinations need their own entries. Hosted stdio MCP servers currently require

37`enabled` access; see [stdio MCP requirements](https://developers.openai.com/api/docs/guides/agents-api/tools/mcp#start-a-server-over-stdio).

38 

39### Check that setup succeeded

40 

41The create-session response means setup has started. Retrieve

42`GET /v1/agents/environments/{environment_id}` using the session's `environment.id`:

43`provisioning` means setup is running; `connected` means setup succeeded.

44For `failed`, read `environment.error` in the `agent.session.environment.failed`

45event. Wait for `connected` before adding or listing live files.

46 

47## Files and lifetime

48 

49Each session has a separate workspace. Files persist across turns while its

50sandbox exists. Files under `/workspace/outputs` are published as immutable

51artifacts when a turn completes; those copies remain downloadable after the

52sandbox expires.

53 

54Use [Files and artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files) for uploads,

55path rules, live file operations, downloads, and limits. Save outputs you need

56before deleting the session.

57 

58### Sandbox expiry

59 

60Connected sandboxes receive keep-alives, including between turns. If activity and

61keep-alives stop for an hour, the sandbox can be deleted. This timeout isn’t

62configurable.

63 

64Delete the session when you're done to request sandbox cleanup. If deletion

65returns `409` while setup or execution finishes, wait and retry with a limit on

66the number of attempts. Closing an event stream does not cancel the task.

67 

68## Pricing

69 

70OpenAI-hosted sandboxes use standard [container rates](https://developers.openai.com/api/docs/pricing#built-in-tools).

71Model usage is billed separately at the selected model's [API rates](https://developers.openai.com/api/docs/pricing).

72 

73## Example: Create a report

74 

75Give the agent a CSV containing `10`, `20`, and `30`. It runs Python to calculate

76the sum and writes `/workspace/outputs/summary.json`.

77 

78Set `OPENAI_API_KEY` in your application terminal using the

79[quickstart prerequisites](https://developers.openai.com/api/docs/guides/agents-api/quickstart#prerequisites).

80Keep this key outside the sandbox. Use a version of your

81[OpenAI SDK](https://developers.openai.com/api/docs/libraries) that includes the beta Agents API.

82 

83Create summary.json

84 

85```javascript

86import OpenAI from "openai";

87 

88const client = new OpenAI();

89const stream = await client.beta.agents.sessions.create({

90 agent: { model: "gpt-6-astra" },

91 environment: {

92 type: "openai_hosted",

93 network: { access: "disabled" },

94 files: [

95 {

96 type: "inline",

97 path: "/workspace/amounts.csv",

98 data: "YW1vdW50CjEwCjIwCjMwCg==",

99 },

100 ],

101 },

102 input:

103 "Use Python to sum the amount column in /workspace/amounts.csv. Write a JSON object with the total to /workspace/outputs/summary.json, then read it back to verify it.",

104 stream: true,

105});

106 

107for await (const event of stream) {

108 console.log(event);

109}

110```

111 

112```python

113from openai import OpenAI

114 

115client = OpenAI()

116stream = client.beta.agents.sessions.create(

117 agent={"model": "gpt-6-astra"},

118 environment={

119 "type": "openai_hosted",

120 "network": {"access": "disabled"},

121 "files": [

122 {

123 "type": "inline",

124 "path": "/workspace/amounts.csv",

125 "data": "YW1vdW50CjEwCjIwCjMwCg==",

126 }

127 ],

128 },

129 input="Use Python to sum the amount column in /workspace/amounts.csv. Write a JSON object with the total to /workspace/outputs/summary.json, then read it back to verify it.",

130 stream=True,

131)

132 

133with stream:

134 for event in stream:

135 print(event.model_dump_json())

136```

137 

138```go

139package main

140 

141import (

142 "context"

143 "fmt"

144 

145 "github.com/openai/openai-go/v3"

146)

147 

148func main() {

149 ctx := context.Background()

150 client := openai.NewClient()

151 stream := client.Beta.Agents.Sessions.NewStreaming(ctx, openai.BetaAgentSessionNewParams{

152 Agent: openai.BetaAgentSessionNewParamsAgent{Model: openai.String("gpt-6-astra")},

153 Environment: openai.EnvironmentParamUnion{OfParamOpenAIHosted: &openai.EnvironmentParamOpenAIHosted{

154 Network: openai.EnvironmentParamOpenAIHostedNetwork{Access: "disabled"},

155 Files: []openai.HostedEnvironmentFileParamUnion{{OfParamInline: &openai.HostedEnvironmentFileParamInline{

156 Path: "/workspace/amounts.csv",

157 Data: "YW1vdW50CjEwCjIwCjMwCg==",

158 }}},

159 }},

160 Input: openai.BetaAgentSessionNewParamsInputUnion{OfString: openai.String("Use Python to sum the amount column in /workspace/amounts.csv. Write a JSON object with the total to /workspace/outputs/summary.json, then read it back to verify it.")},

161 })

162 defer stream.Close()

163 

164 for stream.Next() {

165 fmt.Println(stream.Current().RawJSON())

166 }

167 if err := stream.Err(); err != nil {

168 panic(err)

169 }

170}

171```

172 

173```java

174import com.openai.client.okhttp.OpenAIOkHttpClient;

175import com.openai.models.beta.agents.EnvironmentParam;

176import com.openai.models.beta.agents.HostedEnvironmentFileParam;

177import com.openai.models.beta.agents.sessions.SessionCreateParams;

178 

179public class HostedReport {

180 public static void main(String[] args) throws Exception {

181 var client = OpenAIOkHttpClient.fromEnv();

182 var params =

183 SessionCreateParams.builder()

184 .agent(SessionCreateParams.Agent.builder().model("gpt-6-astra").build())

185 .environment(

186 EnvironmentParam.OpenAIHosted.builder()

187 .network(

188 EnvironmentParam.OpenAIHosted.Network.builder()

189 .access(EnvironmentParam.OpenAIHosted.Network.Access.DISABLED)

190 .build())

191 .addFile(

192 HostedEnvironmentFileParam.Inline.builder()

193 .path("/workspace/amounts.csv")

194 .data("YW1vdW50CjEwCjIwCjMwCg==")

195 .build())

196 .build())

197 .input(

198 "Use Python to sum the amount column in /workspace/amounts.csv. Write a JSON object"

199 + " with the total to /workspace/outputs/summary.json, then read it back to"

200 + " verify it.")

201 .build();

202 

203 try (var stream = client.beta().agents().sessions().createStreaming(params)) {

204 stream.stream().forEach(System.out::println);

205 }

206 }

207}

208```

209 

210```ruby

211require "openai"

212require "json"

213 

214client = OpenAI::Client.new

215stream = client.beta.agents.sessions.create_streaming(

216 agent: { model: "gpt-6-astra" },

217 environment: {

218 type: :openai_hosted,

219 network: { access: :disabled },

220 files: [

221 {

222 type: :inline,

223 path: "/workspace/amounts.csv",

224 data: "YW1vdW50CjEwCjIwCjMwCg=="

225 }

226 ]

227 },

228 input: "Use Python to sum the amount column in /workspace/amounts.csv. Write a JSON object with the total to /workspace/outputs/summary.json, then read it back to verify it."

229)

230 

231begin

232 stream.each { |event| puts event.to_json }

233ensure

234 stream.close

235end

236```

237 

238```bash

239curl --no-buffer --fail-with-body https://api.openai.com/v1/agents/sessions \\\n -H "OpenAI-Beta: agents=v1" \\\n -H "Authorization: Bearer $OPENAI_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "agent": {\n "model": "gpt-6-astra"\n },\n "environment": {\n "type": "openai_hosted",\n "network": {\n "access": "disabled"\n },\n "files": [\n {\n "type": "inline",\n "path": "/workspace/amounts.csv",\n "data": "YW1vdW50CjEwCjIwCjMwCg=="\n }\n ]\n },\n "input": "Use Python to sum the amount column in /workspace/amounts.csv. Write a JSON object with the total to /workspace/outputs/summary.json, then read it back to verify it.",\n "stream": true\n}\'

240```

241 

242 

243The base64 value in `files` contains the CSV input. The code prints session events.

244Save `session.id` from `agent.session.created`. After `agent.session.turn.completed`,

245[list the artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#list-artifacts), find `summary.json`,

246and [download it](https://developers.openai.com/api/docs/guides/agents-api/environments/files#download-an-artifact). Its contents should be:

247 

248```json

249{ "total": 60 }

250```

251 

252A completed turn does not guarantee every tool succeeded. If the task fails or the

253stream ends before completion, [inspect the saved session items](https://developers.openai.com/api/docs/guides/agents-api/sessions#retrieve-session-items).

254[Delete the session](https://developers.openai.com/api/docs/guides/agents-api/quickstart#4-clean-up) when you're done.

255 

256## Troubleshooting

257 

258| Problem | What to check |

259| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |

260| Setup fails | Inspect the environment-failure event and fix the package, input-file, or setup-command error before creating another session. |

261| A sandbox request is blocked | Check `network` and any hosts reached through redirects. |

262| A live file operation fails | Confirm the sandbox is `connected`. If it expired, create a new session and supply the inputs again. |

263| A status or file-list request returns `5xx` | Retry with increasing delays and a deadline. Keep the request ID if the error persists. |

Details

1# Blaxel

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5See the [application-managed](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/application_managed/blaxel) and [webhook-managed](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/webhook_managed/blaxel) examples in the OpenAI Cookbook.

6 

7See [Self-hosted sandboxes](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted) for executor setup and connection requirements.

8 

9Choose a provisioning mode:

10 

11- **[Application-managed](#before-you-begin):** Follow this guide to start and stop sandboxes from your application.

12- **[Webhook-managed](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle#set-up-webhook-managed-sandboxes):** Deploy a handler that starts or reconnects sandboxes from OpenAI webhooks.

13 

14See [Sandbox lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle) to compare the two modes.

15 

16## Before you begin

17 

18You need an OpenAI project API key, a Blaxel API key and workspace, and the Codex CLI package.

19 

20Set `OPENAI_API_KEY`, a separate restricted `OPENAI_EXECUTOR_API_KEY`, `BL_API_KEY`, and `BL_WORKSPACE` in your environment. Grant the application key `api.agents.read` and `api.agents.write` for session operations, plus `api.responses.write` for model inference. Add `api.vaults.read` and `api.vaults.write` if your application manages vaults. Create the executor's [environment key](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#authentication) and use the same organization, project, and user or service account for both keys. Only the restricted executor key enters the sandbox. Choose the sandbox region in your provisioning code. Use `us-was-1` if you need the Agent Drive persistence option below.

21 

22## 1. Set up the Blaxel environment

23 

24Create a [self-hosted session](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#create-or-reuse-a-session) and save its environment ID. Use the Blaxel SDK or API to create an isolated sandbox with the configured working directory. Install the Codex CLI in the sandbox, then [start its executor](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#start-the-executor) with that environment ID and the restricted executor key.

25 

26The Blaxel Node image uses Alpine Linux, so install `ripgrep` with `apk`. Pass the restricted executor key as `CODEX_API_KEY` only to the executor process. Set `keep_alive=True` to prevent the sandbox from scaling to zero while the executor runs. Bounded setup, executor, and sandbox timeouts prevent abandoned resources from running indefinitely.

27 

28For regular use, build a Blaxel image with Codex and `ripgrep` already installed so the sandbox can connect sooner.

29 

30## 2. Run the session

31 

32Use the HTTP examples in [Run and continue sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions) to send input and stream the result after the Blaxel executor connects. When finished, [delete the session](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#delete-a-session) and stop the provider sandbox separately.

33 

34Start the sandbox before submitting input. The turn waits for the environment to connect, and the session reports the connection through `agent.session.environment.connected` on the event stream.

35 

36Use `agent.session.turn.completed` to identify a successful turn. A failed or cancelled turn can also be followed by `agent.session.idle`, so do not treat an idle session as proof that the turn succeeded.

37 

38## Optional: Persist files between sessions

39 

40Use [Blaxel Agent Drive](https://docs.blaxel.ai/Agent-drive/Overview) to preserve files across sandboxes and sessions. Mount the same drive in each sandbox to share files; Agent Drive requires the `us-was-1` region and does not transfer conversation history or session state.

41 

42## References

43 

44- Read [Blaxel Sandbox documentation](https://docs.blaxel.ai/Sandboxes/Overview)

45- Read [Blaxel Python SDK](https://docs.blaxel.ai/sdk-reference/sdk-python)

46- Read [Blaxel TypeScript SDK](https://docs.blaxel.ai/sdk-reference/sdk-ts)

Details

1# Cloudflare

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5This guide uses **webhook-managed provisioning** with Cloudflare's reference Worker.

6 

7See the [application-managed](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/application_managed/cloudflare) and [webhook-managed](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/webhook_managed/cloudflare) examples in the OpenAI Cookbook.

8 

9## How it works

10 

111. Your application creates an Agents API session and sends input.

122. OpenAI sends session webhooks to a Worker in your Cloudflare account.

133. The Worker starts or reconnects a session-specific Container running `codex exec-server`. The executor connects outbound to OpenAI so the agent can run commands and work with files.

14 

15Your application uses the Agents API; the reference Worker manages sandbox provisioning. See [Sandbox lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle) for connection and recovery behavior.

16 

17## Before you begin

18 

19You need a Cloudflare account with Containers access, an OpenAI application API key, and a separate restricted executor key. Follow [executor authentication](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#authentication) to configure the keys. Keep the application key outside the Container.

20 

21[Create an agent](https://developers.openai.com/api/docs/guides/agents-api/configuration#reuse-an-agent-across-sessions) and save its ID as `OPENAI_AGENT_ID`. Use the same agent ID in your application and the reference Worker.

22 

23## Deploy the reference Worker

24 

25Cloudflare's [reference Worker](https://github.com/cloudflare/sandbox-sdk/tree/main/openai/agents-api) includes the webhook handler, Container image, deployment configuration, and cleanup endpoint.

26 

27Generate a secret for the cleanup endpoint and save it as `EXECUTOR_CLIENT_SECRET`:

28 

29```bash

30openssl rand -hex 32

31```

32 

33Deploy the Worker in your Cloudflare account:

34 

35 

36 

37Deploy to Cloudflare

38 

39 

40 

41Enter these values when prompted:

42 

43| Variable | Value |

44| ------------------------- | ------------------------------------------------------- |

45| `OPENAI_API_KEY` | Key used by the Worker to retrieve session state |

46| `OPENAI_EXECUTOR_API_KEY` | Restricted key passed to `codex exec-server` |

47| `OPENAI_AGENT_ID` | Agent ID served by this Worker |

48| `OPENAI_WEBHOOK_SECRET` | `pending-webhook-registration` for the first deployment |

49| `EXECUTOR_CLIENT_SECRET` | Secret generated for cleanup |

50 

51Save the deployed Worker URL as `WORKER_URL`.

52 

53### Register the webhook

54 

55Follow [webhook setup](https://developers.openai.com/api/docs/guides/agents-api/sessions/webhooks#set-up-a-webhook) to register `$WORKER_URL/webhook` in your OpenAI project. Enable the events listed by Cloudflare's reference integration:

56 

57- `agent.session.created`

58- `agent.session.action_required`

59- `agent.session.in_progress`

60- `agent.session.idle`

61- `agent.session.failed`

62 

63Replace `OPENAI_WEBHOOK_SECRET` with the signing secret returned by OpenAI, then deploy the new Worker version. Check its configuration. These examples use standard HTTP clients to call the Worker:

64 

65Check Worker health

66 

67```javascript

68// Replace the illustrative IDs and URLs below with your own resource values.

69 

70const response = await fetch(

71 "https://worker.example.com".replace(/\/+$/, "") + "/health",

72 { method: "GET" }

73);

74if (!response.ok) throw new Error(`Request failed: ${response.status}`);

75console.log(await response.text());

76```

77 

78```python

79# Replace the illustrative IDs and URLs below with your own resource values.

80import urllib.request

81 

82url = "https://worker.example.com".rstrip("/") + "/health"

83request = urllib.request.Request(url, method="GET")

84with urllib.request.urlopen(request) as response:

85 print(response.read().decode())

86```

87 

88```go

89// Replace the illustrative IDs and URLs below with your own resource values.

90import (

91 "io"

92 "net/http"

93 "os"

94 "strings"

95)

96 

97endpoint := strings.TrimRight("https://worker.example.com", "/") + "/health"

98request, err := http.NewRequest("GET", endpoint, nil)

99if err != nil {

100 panic(err)

101}

102response, err := http.DefaultClient.Do(request)

103if err != nil {

104 panic(err)

105}

106defer response.Body.Close()

107if response.StatusCode/100 != 2 {

108 panic(response.Status)

109}

110if _, err := io.Copy(os.Stdout, response.Body); err != nil {

111 panic(err)

112}

113```

114 

115```java

116// Replace the illustrative IDs and URLs below with your own resource values.

117import java.net.URI;

118import java.net.http.HttpClient;

119import java.net.http.HttpRequest;

120import java.net.http.HttpResponse;

121 

122String endpoint = "https://worker.example.com".replaceAll("/+$", "") + "/health";

123var request =

124 HttpRequest.newBuilder(URI.create(endpoint))

125 .method("GET", HttpRequest.BodyPublishers.noBody())

126 .build();

127var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());

128if (response.statusCode() / 100 != 2)

129 throw new IllegalStateException("Request failed: " + response.statusCode());

130System.out.println(response.body());

131```

132 

133```ruby

134# Replace the illustrative IDs and URLs below with your own resource values.

135require "uri"

136require "net/http"

137 

138uri = URI("https://worker.example.com".sub(%r{/+\z}, "") + "/health")

139request = Net::HTTP::Get.new(uri)

140response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(request) }

141raise "Request failed: #{response.code}" unless response.is_a?(Net::HTTPSuccess)

142 

143puts response.body

144```

145 

146```bash

147curl --fail-with-body "$WORKER_URL/health"

148```

149 

150 

151The response should contain both `"configured": true` and `"webhook_configured": true`.

152 

153An `environment_connection` required action is the signal to reconnect an offline executor. An idle event alone isn't a safe shutdown signal; see [lifecycle behavior](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle#lifecycle-behavior).

154 

155## Run a session

156 

157Follow the [session steps](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle#run-a-session) with your application's `OPENAI_API_KEY` and the same `OPENAI_AGENT_ID` configured in the Worker. Create a self-hosted session and ask the agent to write and read `/workspace/hello.txt`.

158 

159The Worker receives the session webhooks and connects the sandbox executor. Your application streams the agent's output through the Agents API.

160 

161Save the session ID as `SESSION_ID`. To continue the conversation, open the session event stream before sending follow-up input. If the executor is offline, the new input requests an environment connection and waits for the Worker to reconnect it. Reconnection does not by itself restore files from a previous Container.

162 

163### Run your application in a Worker

164 

165Cloudflare's [basic Worker application](https://github.com/cloudflare/sandbox-sdk/tree/main/openai/agents-api/basic) uses the `@openai/agents-api` TypeScript SDK to create sessions, send initial and follow-up input, and clean up resources. Its `POST /demo` endpoint runs the workflow.

166 

167This application also uses webhook-managed provisioning. Running your application in a Worker doesn't mean it must provision the sandbox directly.

168 

169## Cleanup

170 

171When the application no longer needs the sandbox, call the reference Worker's authenticated cleanup endpoint:

172 

173Clean up the Worker sandbox

174 

175```javascript

176// Replace the illustrative IDs and URLs below with your own resource values.

177 

178const response = await fetch("https://worker.example.com/executors/sess_123", {

179 method: "DELETE",

180 headers: { Authorization: `Bearer ${process.env.EXECUTOR_CLIENT_SECRET}` },

181});

182if (!response.ok) throw new Error(`Request failed: ${response.status}`);

183console.log(await response.text());

184```

185 

186```python

187# Replace the illustrative IDs and URLs below with your own resource values.

188import os

189from urllib.parse import quote

190import urllib.request

191 

192url = (

193 "https://worker.example.com".rstrip("/")

194 + "/executors/"

195 + quote("sess_123", safe="")

196)

197request = urllib.request.Request(

198 url,

199 method="DELETE",

200 headers={"Authorization": "Bearer " + os.environ["EXECUTOR_CLIENT_SECRET"]},

201)

202with urllib.request.urlopen(request) as response:

203 print(response.read().decode())

204```

205 

206```go

207// Replace the illustrative IDs and URLs below with your own resource values.

208import (

209 "io"

210 "net/http"

211 "net/url"

212 "os"

213 "strings"

214)

215 

216endpoint := strings.TrimRight("https://worker.example.com", "/") + "/executors/" + url.PathEscape("sess_123")

217request, err := http.NewRequest("DELETE", endpoint, nil)

218if err != nil {

219 panic(err)

220}

221request.Header.Set("Authorization", "Bearer "+os.Getenv("EXECUTOR_CLIENT_SECRET"))

222response, err := http.DefaultClient.Do(request)

223if err != nil {

224 panic(err)

225}

226defer response.Body.Close()

227if response.StatusCode/100 != 2 {

228 panic(response.Status)

229}

230if _, err := io.Copy(os.Stdout, response.Body); err != nil {

231 panic(err)

232}

233```

234 

235```java

236// Replace the illustrative IDs and URLs below with your own resource values.

237import java.net.URI;

238import java.net.URLEncoder;

239import java.net.http.HttpClient;

240import java.net.http.HttpRequest;

241import java.net.http.HttpResponse;

242import java.nio.charset.StandardCharsets;

243 

244String endpoint =

245 "https://worker.example.com".replaceAll("/+$", "")

246 + "/executors/"

247 + URLEncoder.encode("sess_123", StandardCharsets.UTF_8).replace("+", "%20");

248var request =

249 HttpRequest.newBuilder(URI.create(endpoint))

250 .header("Authorization", "Bearer " + System.getenv("EXECUTOR_CLIENT_SECRET"))

251 .method("DELETE", HttpRequest.BodyPublishers.noBody())

252 .build();

253var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());

254if (response.statusCode() / 100 != 2)

255 throw new IllegalStateException("Request failed: " + response.statusCode());

256System.out.println(response.body());

257```

258 

259```ruby

260# Replace the illustrative IDs and URLs below with your own resource values.

261require "uri"

262require "net/http"

263 

264uri = URI("https://worker.example.com".sub(%r{/+\z}, "") + "/executors/" + URI.encode_www_form_component("sess_123").gsub("+", "%20"))

265request = Net::HTTP::Delete.new(uri)

266request["Authorization"] = "Bearer #{ENV.fetch("EXECUTOR_CLIENT_SECRET")}"

267response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(request) }

268raise "Request failed: #{response.code}" unless response.is_a?(Net::HTTPSuccess)

269 

270puts response.body

271```

272 

273```bash

274curl --fail-with-body \

275 --request DELETE \

276 --header "Authorization: Bearer $EXECUTOR_CLIENT_SECRET" \

277 "$WORKER_URL/executors/$SESSION_ID"

278```

279 

280 

281[Delete the Agents API session](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#delete-a-session) separately. Session deletion does not emit a webhook, so perform both operations for immediate cleanup. Retrieve files you need before releasing the Container.

282 

283## Advanced: Application-managed provisioning

284 

285For direct control of sandbox provisioning, use the Cloudflare Sandbox SDK with the [application-managed lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle#manage-sandboxes-from-your-application) and [executor connection instructions](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted). Use one provisioning controller per session.

286 

287## References

288 

289- Read [Use Cloudflare Containers with OpenAI Agents API](https://developers.cloudflare.com/sandbox/guides/openai-agents-api/) for configuration, lifecycle behavior, snapshots, and image customization.

290- Read [Cloudflare Sandbox documentation](https://developers.cloudflare.com/sandbox/).

291- Read [Cloudflare Sandbox TypeScript SDK reference](https://developers.cloudflare.com/sandbox/api/).

Details

1# Daytona

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5See the [application-managed](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/application_managed/daytona) and [webhook-managed](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/webhook_managed/daytona) examples in the OpenAI Cookbook.

6 

7See [Self-hosted sandboxes](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted) for executor setup and connection requirements.

8 

9Choose a provisioning mode:

10 

11- **[Application-managed](#application-managed):** Follow this guide to start and stop sandboxes from your application.

12- **[Webhook-managed](#webhook-managed):** Deploy a handler that starts or reconnects sandboxes from OpenAI webhooks.

13 

14See [Sandbox lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle) to compare the two modes.

15 

16## Webhook-managed

17 

18Use a controller to verify OpenAI webhook deliveries and queue provisioning work. Keep that controller separate from the worker sandbox that runs each session's executor. Follow [Deploy and connect a handler](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle#deploy-and-connect-a-handler) to register the endpoint and signing secret.

19 

20Handle `environment_connection` requests by starting or reconnecting the worker, and release it when the session fails. Configure worker and controller timeouts explicitly. Stopping compute on idle requires a policy that coordinates with incoming work; see [Lifecycle behavior](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle#lifecycle-behavior).

21 

22## Application-managed

23 

24### Before you begin

25 

26You need an OpenAI project API key, a Daytona API key, and the Codex CLI package.

27 

28Set `OPENAI_API_KEY`, a separate restricted `OPENAI_EXECUTOR_API_KEY`, and `DAYTONA_API_KEY` in your environment. Grant the application key `api.agents.read` and `api.agents.write` for session operations, plus `api.responses.write` for model inference. Add `api.vaults.read` and `api.vaults.write` if your application manages vaults. Create the executor's [environment key](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#authentication) and use the same organization, project, and user or service account for both keys. Only the restricted executor key enters the sandbox.

29 

30### 1. Set up the Daytona environment

31 

32Create a [self-hosted session](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#create-or-reuse-a-session) and save its environment ID. Use the Daytona SDK or API to create an isolated sandbox with the configured working directory. Install the Codex CLI in the sandbox, then [start its executor](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#start-the-executor) with that environment ID and the restricted executor key.

33 

34The executor's connection to OpenAI is outbound and long-lived, and Daytona's inactivity tracking does not observe it. Set `auto_stop_interval=0` so the Sandbox is not stopped while the agent is working, and configure a lifetime limit so interrupted runs do not leave compute running indefinitely.

35 

36For regular use, put Codex and `ripgrep` in a Daytona snapshot so the Sandbox can connect sooner.

37 

38### 2. Run the session

39 

40Use the HTTP examples in [Run and continue sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions) to send input and stream the result after the Daytona executor connects. When finished, [delete the session](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#delete-a-session) and stop the provider sandbox separately.

41 

42Start the Sandbox before submitting input. The turn waits for the environment to connect, and the session reports the connection through `agent.session.environment.connected` on the event stream.

43 

44Use `agent.session.turn.completed` to identify a successful turn. A failed or cancelled turn can also be followed by `agent.session.idle`, so do not treat an idle session as proof that the turn succeeded. A session retrieval immediately after an event can briefly return the prior status.

45 

46## References

47 

48- Read [Daytona documentation](https://www.daytona.io/docs/en/)

49- Read [Daytona Python SDK reference](https://www.daytona.io/docs/en/python-sdk/)

50- Read [Daytona TypeScript SDK reference](https://www.daytona.io/docs/en/typescript-sdk/)

Details

1# DigitalOcean

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5See the [application-managed](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/application_managed/digitalocean) and [webhook-managed](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/webhook_managed/digitalocean) examples in the OpenAI Cookbook.

6 

7## How it works

8 

9DigitalOcean's Managed Agents Runtime Services (M.A.R.S.) starts a Firecracker microVM using the `codex-agentapi` image. The image includes Codex and starts the executor, which connects outbound to the Agents API.

10 

11Choose **[webhook-managed](#webhook-managed)** provisioning to start or resume sandboxes from OpenAI events, or **[application-managed](#application-managed)** provisioning to control them from your application. For an interactive quickstart, use the optional [DigitalOcean CLI flow](#try-it-with-the-digitalocean-cli). See [Sandbox lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle) for connection and recovery behavior.

12 

13M.A.R.S. is in invite-only private preview. Request access through [DigitalOcean's private-preview announcement](https://www.digitalocean.com/blog/managed-agents-runtime-services-private-preview).

14 

15## Before you begin

16 

17You need a sandbox-enabled DigitalOcean account with access to `codex-agentapi` and an OpenAI project with Agents API access.

18 

19Set `OPENAI_API_KEY` for your application or CLI and a separate restricted `OPENAI_EXECUTOR_API_KEY` for the sandbox. The keys must have the same owner, organization, and project. Store only the executor key in the sandbox's `CODEX_API_KEY` secret. See [executor authentication](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#authentication).

20 

21For webhook controllers or Python applications, set `DIGITALOCEAN_TOKEN` and install the [PyDo beta SDK](https://github.com/digitalocean/pydo/releases/tag/v0.40.0-beta.7) with async support (`pydo[aio]`). Use the [OpenAI SDK](https://developers.openai.com/api/docs/libraries#install-an-official-sdk) for Agents API requests. CLI installation is needed only for the CLI flow.

22 

23## Webhook-managed

24 

251. [Create a stored agent](https://developers.openai.com/api/docs/guides/agents-api/configuration#reuse-an-agent-across-sessions) and save its ID as `OPENAI_AGENT_ID`. Deploy an HTTPS webhook controller in DigitalOcean App Platform with this ID, `OPENAI_API_KEY` for session reads, `DIGITALOCEAN_TOKEN`, and `OPENAI_EXECUTOR_API_KEY`.

262. [Register its `/webhook` endpoint](https://developers.openai.com/api/docs/guides/agents-api/sessions/webhooks) with your OpenAI project. Enable `agent.session.action_required` and `agent.session.failed`, then store the signing secret as `OPENAI_WEBHOOK_SECRET` and redeploy the controller.

273. Follow the [session steps](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle#run-a-session) with the same `OPENAI_AGENT_ID` and `/workspace` as the working directory. Open the event stream and send input. When OpenAI requests an `environment_connection`, the controller verifies the signature, retrieves the current session, and checks its agent ID and required actions. It looks up `mars-{session_id}` in DigitalOcean and resumes a paused sandbox or creates one if none is active.

284. On `agent.session.failed`, retrieve the session again and delete its sandbox only if the current session status is still `failed`.

29 

30The image connects the executor to the session's environment. Your application sends input and streams results through the Agents API; the controller handles provisioning and reconnection. Serialize provisioning per session to handle duplicate and concurrent deliveries. See [webhook-managed lifecycle guidance](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle#set-up-webhook-managed-sandboxes) for controller requirements.

31 

32## Try it with the DigitalOcean CLI

33 

34The CLI creates both resources and lets you interact with the agent from your terminal. It provisions the sandbox directly, without a webhook controller.

35 

36Install the [`doctl` beta release](https://github.com/digitalocean/doctl/releases/tag/v1.168.0-beta.8) that includes `harness-runtime`, then authenticate:

37 

38```bash

39doctl auth init

40```

41 

42Save this manifest as `agents.yaml`:

43 

44```yaml

45name: openai-codex-session

46agent: codex-agentapi

47config:

48 agent:

49 model: gpt-5.6-sol

50 instructions: Work from the files in /workspace.

51 environment:

52 type: self_hosted

53 workspace_directory: /workspace

54egress:

55 - api.openai.com

56 - codex-cloud-environments.chatgpt.com

57env:

58 CODEX_ENVIRONMENT_ID: ${ENV_ID}

59secrets:

60 CODEX_API_KEY: ${OPENAI_EXECUTOR_API_KEY}

61```

62 

63The `config` block is the OpenAI create-session request. The CLI authenticates that request with `OPENAI_API_KEY`, fills `${ENV_ID}` from the response, and passes only the restricted executor key to the sandbox. Keep resolved manifests out of logs and source control. Add any destinations your tools need to `egress`.

64 

65Create the session and sandbox:

66 

67```bash

68doctl harness-runtime create --spec agents.yaml

69```

70 

71The command waits up to 300 seconds for readiness by default. Save the OpenAI session ID and DigitalOcean session ID from the session details, then attach:

72 

73```bash

74doctl harness-runtime launch openai-codex-session

75```

76 

77Ask the agent to write `hello` to `/workspace/hello.txt` and read it back. Press **Ctrl+D** to detach without deleting the session, and run the same `launch` command to reattach. Follow [Cleanup](#cleanup) when finished.

78 

79## Application-managed

80 

81Use this path when your application owns session creation and sandbox provisioning. Create the OpenAI session first:

82 

83Create a self-hosted session

84 

85```javascript

86import OpenAI from "openai";

87const client = new OpenAI();

88 

89const session = await client.beta.agents.sessions.create({

90 agent: {

91 model: "gpt-6-astra",

92 instructions:

93 "You are a helpful coding assistant. Write clean code and verify that it works.",

94 },

95 environment: {

96 type: "self_hosted",

97 workspace_directory: "/workspace",

98 },

99});

100 

101console.log(session);

102```

103 

104```python

105from openai import OpenAI

106 

107client = OpenAI()

108 

109session = client.beta.agents.sessions.create(

110 agent={

111 "model": "gpt-6-astra",

112 "instructions": "You are a helpful coding assistant. Write clean code and verify that it works.",

113 },

114 environment={"type": "self_hosted", "workspace_directory": "/workspace"},

115)

116print(session.to_json())

117```

118 

119```go

120import (

121 "context"

122 "fmt"

123 

124 "github.com/openai/openai-go/v3"

125)

126 

127ctx := context.Background()

128client := openai.NewClient()

129result, err := client.Beta.Agents.Sessions.New(ctx,

130 openai.BetaAgentSessionNewParams{

131 Agent: openai.BetaAgentSessionNewParamsAgent{

132 Model: openai.String("gpt-6-astra"),

133 Instructions: openai.String("You are a helpful coding assistant. Write clean code and verify that it works."),

134 },

135 Environment: openai.EnvironmentParamUnion{

136 OfParamSelfHosted: &openai.EnvironmentParamSelfHosted{WorkspaceDirectory: "/workspace"},

137 },

138 })

139if err != nil {

140 panic(err)

141}

142fmt.Println(result)

143```

144 

145```java

146import com.openai.client.OpenAIClient;

147import com.openai.client.okhttp.OpenAIOkHttpClient;

148import com.openai.models.beta.agents.EnvironmentParam;

149import com.openai.models.beta.agents.sessions.SessionCreateParams;

150 

151OpenAIClient client = OpenAIOkHttpClient.fromEnv();

152var result =

153 client

154 .beta()

155 .agents()

156 .sessions()

157 .create(

158 SessionCreateParams.builder()

159 .agent(

160 SessionCreateParams.Agent.builder()

161 .model("gpt-6-astra")

162 .instructions(

163 "You are a helpful coding assistant. Write clean code and verify"

164 + " that it works.")

165 .build())

166 .environment(

167 EnvironmentParam.SelfHosted.builder()

168 .workspaceDirectory("/workspace")

169 .build())

170 .build());

171System.out.println(result);

172```

173 

174```ruby

175require "openai"

176 

177client = OpenAI::Client.new

178result = client.beta.agents.sessions.create(

179 agent: {

180 model: "gpt-6-astra",

181 instructions: "You are a helpful coding assistant. Write clean code and verify that it works."

182 },

183 environment: {

184 type: "self_hosted",

185 workspace_directory: "/workspace"

186 }

187)

188puts result

189```

190 

191 

192Save `session.id` and the environment ID as described in [Connect a sandbox](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#create-or-reuse-a-session). Save this sandbox-only manifest as `sandbox.yaml`; the agent configuration was already sent to OpenAI:

193 

194```yaml

195agent: codex-agentapi

196egress:

197 - api.openai.com

198 - codex-cloud-environments.chatgpt.com

199env:

200 CODEX_ENVIRONMENT_ID: ${ENV_ID}

201secrets:

202 CODEX_API_KEY: ${OPENAI_EXECUTOR_API_KEY}

203```

204 

2051. Create a `pydo.aio.Client` using `DIGITALOCEAN_TOKEN` and call `client.agents.create_session`. Set `params.openai_session_id` to the OpenAI session ID, `body.manifest` to the contents of `sandbox.yaml`, and `body.variables` to a mapping of `ENV_ID` and `OPENAI_EXECUTOR_API_KEY` to their values. Save the returned DigitalOcean `session_id`.

2062. [Open the event stream and send input](https://developers.openai.com/api/docs/guides/agents-api/sessions#send-input), asking the agent to write and read `/workspace/hello.txt`. Input waits for the executor to connect. Confirm the connection event and a completed turn, and inspect the agent's output for tool failures.

2073. Retrieve the file with `workspace_download`, using the relative path `hello.txt`. Keep both resources for follow-up turns, or [clean up](#cleanup).

208 

209Use bounded setup and execution timeouts and handle connection failures in your application. Do not attach a provisioning webhook handler to sessions your application or CLI manages directly.

210 

211## Cleanup

212 

213Save any files you need, then [delete the OpenAI session](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#delete-a-session) and destroy the DigitalOcean sandbox. Session deletion does not emit a webhook, so perform both operations and report cleanup failures.

214 

215With PyDo, call `client.agents.destroy_session` with the DigitalOcean session ID. With the CLI, pass that ID or the sandbox's name:

216 

217```bash

218doctl harness-runtime remove openai-codex-session

219```

220 

221Remove the OpenAI webhook registration before deleting a webhook controller.

222 

223## References

224 

225- Read [DigitalOcean sandbox setup](https://github.com/digitalocean/pydo/tree/v0.40.0-beta.7/examples/agents/doc_python_sdk)

226- Read [DigitalOcean Python SDK](https://github.com/digitalocean/pydo)

227- Read [DigitalOcean CLI beta release](https://github.com/digitalocean/doctl/releases/tag/v1.168.0-beta.8)

Details

1# E2B

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5See the [application-managed](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/application_managed/e2b) and [webhook-managed](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/webhook_managed/e2b) examples in the OpenAI Cookbook.

6 

7Choose a provisioning mode:

8 

9- **[Application-managed](#application-managed):** Your application creates and connects the E2B sandbox directly.

10- **[Webhook-managed](#webhook-managed):** Deploy a handler that starts or reconnects sandboxes from OpenAI webhooks.

11 

12See [Sandbox lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle) to compare the two modes.

13 

14## Before you begin

15 

16Set `E2B_API_KEY`, `OPENAI_API_KEY`, and a separate restricted `OPENAI_EXECUTOR_API_KEY`. Keep the application key outside the worker sandbox. The executor key must match the session owner's organization, project, and user or service account. See [executor authentication](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#authentication).

17 

18## Webhook-managed

19 

20Implement a controller that verifies OpenAI webhooks and provisions a separate E2B worker for each session. Follow [Deploy and connect a handler](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle#deploy-and-connect-a-handler) for credentials, endpoint registration, and signature verification.

21 

22Persist the session-to-sandbox mapping. On a connection request, resume a paused worker or replace a deleted one. Pausing preserves its files; replacement does not. Set running timeouts for the controller and workers, and remove the OpenAI webhook when you stop using the controller.

23 

24## Application-managed

25 

26Use the E2B SDK or API from your application to manage the sandbox:

27 

281. [Create a self-hosted session](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#create-or-reuse-a-session) and save its environment ID.

292. Create an isolated E2B sandbox with the session's working directory and install the Codex CLI inside it.

303. [Start the executor](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#start-the-executor) in the sandbox using the environment ID and restricted executor key.

314. Use [Run and continue sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions) to send input and check the turn's outcome.

325. [Delete the session](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#delete-a-session) and stop the E2B sandbox when finished.

33 

34Configure the sandbox lifetime separately from the timeout for the executor command. A command with no timeout does not keep an expired sandbox running.

35 

36## References

37 

38- Read [E2B documentation](https://docs.e2b.dev/)

39- Read [E2B Python SDK](https://github.com/e2b-dev/E2B/tree/main/packages/python-sdk)

40- Read [E2B TypeScript SDK](https://github.com/e2b-dev/E2B/tree/main/packages/js-sdk)

Details

1# Modal

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5See the [application-managed](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/application_managed/modal) and [webhook-managed](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/webhook_managed/modal) examples in the OpenAI Cookbook.

6 

7See [Self-hosted sandboxes](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted) for executor setup and connection requirements.

8 

9Choose a provisioning mode:

10 

11- **[Application-managed](#before-you-begin):** Follow this guide to start and stop sandboxes from your application.

12- **[Webhook-managed](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle#set-up-webhook-managed-sandboxes):** Deploy a handler that starts or reconnects sandboxes from OpenAI webhooks.

13 

14See [Sandbox lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle) to compare the two modes.

15 

16## Before you begin

17 

18You need an OpenAI project API key, a Modal token ID and secret, and the Codex CLI package.

19 

20Set `OPENAI_API_KEY` for application requests and a separate restricted `OPENAI_EXECUTOR_API_KEY` for sandbox registration. Grant the application key `api.agents.read` and `api.agents.write` for session operations, plus `api.responses.write` for model inference. Add `api.vaults.read` and `api.vaults.write` if your application manages vaults. Create the executor's [environment key](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#authentication) and use the same organization, project, and user or service account for both keys. Only the restricted executor key enters the sandbox.

21 

22## 1. Set up the Modal environment

23 

24Create a [self-hosted session](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#create-or-reuse-a-session) and save its environment ID. Use the Modal SDK or API to create an isolated sandbox with the configured working directory. Install the Codex CLI in the sandbox, then [start its executor](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#start-the-executor) with that environment ID and the restricted executor key.

25 

26## 2. Run the session

27 

28Use the HTTP examples in [Run and continue sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions) to send input and stream the result after the Modal executor connects. When finished, [delete the session](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#delete-a-session) and stop the provider sandbox separately.

29 

30## References

31 

32- Read [Modal Sandbox documentation](https://modal.com/docs/guide/sandboxes)

33- Read [Modal Python SDK reference](https://modal.com/docs/sdk/py/latest/Sandbox)

34- Read [Modal JavaScript/TypeScript SDK reference](https://modal.com/docs/sdk/js/latest/Sandbox)

Details

1# Oracle Cloud Infrastructure (OCI)

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5This guide follows Oracle's beta Python example and uses **application-managed provisioning**: your application creates and deletes both the Agents API session and the OCI sandbox.

6 

7See the [application-managed example](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/application_managed/oci) in the OpenAI Cookbook.

8 

9See [Sandbox lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle) for the provisioning modes and connection behavior.

10 

11OCI GenAI Sandboxes are in beta. Contact your Oracle account manager to

12 request access for your account.

13 

14## Before you begin

15 

16Create a sandbox-enabled Generative AI Project. Grant your OCI identity permission to manage projects and sandboxes in its compartment. Replace the placeholders in these IAM policies:

17 

18```text

19allow group <group-name> to manage generative-ai-sandbox in compartment <compartment-name>

20allow group <group-name> to manage generative-ai-project in compartment <compartment-name>

21```

22 

23Use a sandbox runtime with Node.js and `npm`. Oracle's example requests `python-3.11` by default; choose a compatible custom runtime if it doesn't include `npm`.

24 

25If your project restricts outbound traffic, allow HTTPS to `registry.npmjs.org` to install Codex, HTTPS to `api.openai.com`, and secure WebSocket connections to `codex-cloud-environments.chatgpt.com`. See [executor network access](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#network-access).

26 

27## 1. Install the OCI CLI and beta SDK

28 

29Create a virtual environment and install the OCI CLI:

30 

31```bash

32uv venv --python 3.14

33source .venv/bin/activate

34uv pip install --upgrade oci-cli

35```

36 

37Then install the beta Python SDK supplied by Oracle during onboarding:

38 

39```bash

40uv pip install "/path/to/oci-<beta-version>-py3-none-any.whl"

41```

42 

43Install the beta SDK **after** the CLI. Installing or upgrading `oci-cli` afterward can replace it with the `oci` package from PyPI; reinstall the beta wheel if that happens. The beta SDK must include `oci.generative_ai_sandbox`.

44 

45## 2. Configure the OCI environment

46 

47Authenticate a security-token profile in the region enabled for your account:

48 

49```bash

50oci session authenticate --profile-name Sandbox --region us-chicago-1

51```

52 

53Set the project OCID and OpenAI credentials without committing them:

54 

55```bash

56export OCI_SANDBOX_PROJECT_ID="ocid1.generativeaiproject..."

57export OPENAI_API_KEY="..."

58export OPENAI_EXECUTOR_API_KEY="..."

59```

60 

61Use the application key for Agents API requests. Pass only the separate restricted executor key into the sandbox as `CODEX_API_KEY`. Both keys must have the same owner, organization, and project. See [executor authentication](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#authentication).

62 

63Oracle's example reads the `Sandbox` profile and uses `us-chicago-1`. To override its defaults:

64 

65```bash

66export OCI_SANDBOX_PROFILE="my-profile"

67export OCI_SANDBOX_REGION="us-chicago-1"

68```

69 

70The example also accepts these optional settings:

71 

72| Setting | Default |

73| ------------------------ | ------------------------------------------------------------- |

74| `OCI_SANDBOX_ENDPOINT` | `https://inference.generativeai.<region>.oci.oraclecloud.com` |

75| `OCI_SANDBOX_RUNTIME` | `python-3.11` |

76| `OCI_SANDBOX_SHAPE` | `SMALL` |

77| `OCI_SANDBOX_EXPIRATION` | `PT30M` (30 minutes) |

78 

79When configuring your own application, use the profile's security token and private key with `oci.auth.signers.SecurityTokenSigner`. Create a sandbox client with `GenerativeAiSandboxClient` from `oci.generative_ai_sandbox`, using the selected region and endpoint.

80 

81## 3. Run an application-managed session

82 

83Use the [self-hosted connection guide](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted) for the Agents API requests and executor startup command. Follow the same flow as Oracle's example:

84 

851. Create a self-hosted Agents API session with `/workspace` as its working directory. Save the session ID and environment ID.

862. Create an OCI GenAI Sandbox and wait for it to reach `RUNNING`.

873. Install Codex and write `/workspace/brief.txt` into the sandbox.

884. Start `codex exec-server` using the session's environment ID and the restricted executor key.

895. Open the session event stream, then send input asking the agent to turn `brief.txt` into a migration plan. Wait for completion and read the generated `/workspace/plan.md`.

906. Stop and delete the OCI sandbox, then [delete the Agents API session](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#delete-a-session). Attempt both cleanup operations even if one fails.

91 

92Keep both resources alive for follow-up turns and retrieve files before deleting the sandbox. Use the beta SDK version specified by Oracle; preview releases may rename sandbox APIs.

93 

94## References

95 

96- Read [OCI Generative AI documentation](https://docs.oracle.com/en-us/iaas/Content/generative-ai/)

97- Read [OCI Python SDK documentation](https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/pythonsdk.htm)

98- Read [OCI TypeScript SDK documentation](https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/typescriptsdk.htm)

99- Read [OCI CLI authentication](https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/clitoken.htm)

Details

1# Runloop

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5See the [application-managed example](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/application_managed/runloop) in the OpenAI Cookbook.

6 

7This guide uses **application-managed** provisioning: your application starts the Devbox, connects its executor, and shuts it down when finished. See [Sandbox lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle) for the lifecycle behavior.

8 

9## Before you begin

10 

11Use the Runloop SDK or API to manage a Devbox, and HTTP requests to manage Agents API sessions.

12 

13Set `RUNLOOP_API_KEY`, `OPENAI_API_KEY`, and a separate restricted `OPENAI_EXECUTOR_API_KEY`. The OpenAI keys must have the same owner, organization, and project. Only the executor key enters the Devbox. See [executor authentication](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#authentication).

14 

15## Application-managed

16 

171. [Create a self-hosted session](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#create-or-reuse-a-session) and save its environment ID.

182. Create a Runloop Devbox with the session's working directory and install the Codex CLI inside it.

193. [Start the executor](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#start-the-executor) in the background with the environment ID and restricted executor key.

204. [Send input and inspect the result](https://developers.openai.com/api/docs/guides/agents-api/sessions). For a file task, create `brief.txt` in the workspace and ask the agent to write a migration plan to `plan.md`.

215. Check that the turn completed, retrieve any files you need, then shut down the Devbox and [delete the session](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#delete-a-session).

22 

23Use bounded setup and execution timeouts, plus a Devbox lifetime limit as a fallback if your application exits unexpectedly. Keep the session and Devbox alive if you need follow-up turns. Use one provisioning owner per session; do not attach a provisioning webhook handler to sessions your application manages directly.

24 

25## References

26 

27- Read [Runloop documentation](https://docs.runloop.ai/)

28- Read [Runloop Python SDK](https://runloopai.github.io/api-client-python/)

29- Read [Runloop TypeScript SDK](https://runloopai.github.io/api-client-ts/stable/)

Details

1# Vercel

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5See the [application-managed](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/application_managed/vercel) and [webhook-managed](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/webhook_managed/vercel) examples in the OpenAI Cookbook.

6 

7See [Self-hosted sandboxes](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted) for executor setup and connection requirements.

8 

9Choose a provisioning mode:

10 

11- **[Application-managed](#before-you-begin):** Follow this guide to start and stop sandboxes from your application.

12- **[Webhook-managed](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle#set-up-webhook-managed-sandboxes):** Deploy a handler that starts or reconnects sandboxes from OpenAI webhooks.

13 

14See [Sandbox lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle) to compare the two modes.

15 

16## Before you begin

17 

18Use a Vercel project with Sandbox access. How the Vercel Sandbox SDK authenticates depends on where this application is running:

19 

20- **Running locally:** set `VERCEL_TOKEN`, `VERCEL_TEAM_ID`, and `VERCEL_PROJECT_ID` in your environment.

21- **Deployed on Vercel:** use Vercel OIDC.

22 

23Set `OPENAI_API_KEY` for application requests and a separate restricted `OPENAI_EXECUTOR_API_KEY` for sandbox registration. Grant the application key `api.agents.read` and `api.agents.write` for session operations, plus `api.responses.write` for model inference. Add `api.vaults.read` and `api.vaults.write` if your application manages vaults. Create the executor's [environment key](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#authentication) and use the same organization, project, and user or service account for both keys. Only the restricted executor key enters the sandbox.

24 

25## 1. Set up the Vercel environment

26 

27Create a [self-hosted session](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#create-or-reuse-a-session) and save its environment ID. Use the Vercel SDK or API to create an isolated sandbox with the configured working directory. Install the Codex CLI in the sandbox, then [start its executor](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#start-the-executor) with that environment ID and the restricted executor key.

28 

29For regular use, put Codex in a Vercel snapshot so the sandbox can connect sooner.

30 

31## 2. Run the session

32 

33Use the HTTP examples in [Run and continue sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions) to send input and stream the result after the Vercel executor connects. When finished, [delete the session](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#delete-a-session) and stop the provider sandbox separately.

34 

35## References

36 

37- Read [Vercel Sandbox documentation](https://vercel.com/docs/sandbox)

38- Read [Vercel Sandbox Python SDK reference](https://vercel.com/docs/sandbox/python-sdk-reference)

39- Read [Vercel Sandbox JavaScript/TypeScript SDK reference](https://vercel.com/docs/sandbox/sdk-reference)

Details

1# Sandbox security

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5Agent-generated code can access the files, credentials, and network available to its environment.

6 

7 

8 

9 

10## Isolate workloads

11 

12Run workloads in isolated compute, such as virtual machines. Use separate environments for users or workloads that must not share data. Create a dedicated OpenAI project for your application or workload.

13 

14 

15 

16 

17## Restrict network access

18 

19Allow outbound traffic only to approved endpoints, including the executor's [required hosts](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#network-access).

20 

21Configure network access based on where each tool connection runs:

22 

23- **Executor MCPs** connect from your environment. Allow access to the servers they need.

24- **Remote MCPs** connect from OpenAI's service. Their endpoints must be reachable from that service.

25 

26See [MCP tools](https://developers.openai.com/api/docs/guides/agents-api/tools/mcp) for connection options.

27 

28 

29 

30 

31## Separate credentials

32 

33Grant your application key `api.agents.read` and `api.agents.write` for sessions, plus `api.responses.write` for inference. Add `api.vaults.read` and `api.vaults.write` to manage vaults.

34 

35Give the executor an [environment key](https://platform.openai.com/agents?tab=environments&environment_view=keys) as `CODEX_API_KEY`. This key only permits connecting environments. It cannot authorize any other API action. See [Executor authentication](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#authentication) for setup.

36 

37Agent-generated code can read the environment key. Keep your application API key outside the environment. Do not embed keys in images, source code, or logs. Rotate or revoke keys when needed.

38 

39 

40 

41 

42## Broker third-party access

43 

44Keep third-party credentials outside the environment. Where possible, route requests through a credential broker. The broker injects secrets into approved outbound requests without placing them in the agent's environment.

45 

46<picture>

47 <source

48 media="(max-width: 640px)"

49 srcSet="/images/api/agents-api/sandbox-security-1-mobile.webp"

50 width="680"

51 height="1252"

52 />

53 <img src="https://developers.openai.com/images/api/agents-api/sandbox-security-1.webp"

54 width="1400"

55 height="848"

56 alt="In an example you configure, sandbox tools send requests to an external proxy that adds scoped credentials for approved destinations. The restricted executor key remains readable inside the sandbox."

57 loading="lazy"

58 />

59</picture>

60 

61Store long-lived credentials in a secrets manager. Injecting a stored secret into the environment still exposes it to agent-generated code. Rotate credentials regularly and revoke them immediately if you suspect exposure.

Details

1# Self-hosted sandboxes

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5Connect your own environment when you want more control over the agent's environment or want to use compute you trust. The environment can be a laptop, a container, or a remote sandbox. To have OpenAI provision the environment, use an [OpenAI-hosted sandbox](https://developers.openai.com/api/docs/guides/agents-api/environments/openai-hosted).

6 

7 

8 

9 

10## How the connection works

11 

12OpenAI runs the [agent harness](https://developers.openai.com/api/docs/guides/agents-api/architecture#the-pieces). You run `codex exec-server`, the executor, inside your environment. It runs shell commands, reads and writes files, and uses local MCP servers at the harness's request.

13 

14The executor registers with the API using an environment ID and a restricted API key. It then connects over WebSocket to receive commands and return results. All connections are outbound. The executor reconnects if the connection drops.

15 

16<picture>

17 <source

18 media="(max-width: 640px)"

19 srcSet="/images/api/agents-api/self-hosted-sandboxes-1-mobile.webp"

20 width="680"

21 height="876"

22 />

23 <img src="https://developers.openai.com/images/api/agents-api/self-hosted-sandboxes-1.webp"

24 width="1400"

25 height="444"

26 alt="The sandbox executor initiates an outbound connection to the Agents API and exchanges commands and results. The sandbox holds the restricted executor key and environment ID."

27 loading="lazy"

28 />

29</picture>

30 

31 

32 

33 

34## Prepare your environment

35 

36Prepare the files and dependencies your agent needs. Isolate environments by user or workload. Agents that share an environment can access the same files, credentials, and other resources.

37 

38Create the working directory and install the Codex CLI inside the environment. This example uses `/workspace`:

39 

40```bash

41mkdir -p /workspace

42npm install -g @openai/codex@alpha

43```

44 

45 

46 

47 

48### Network access

49 

50Allow outbound connections to these hosts:

51 

52- `https://api.openai.com` for environment registration.

53- `wss://codex-cloud-environments.chatgpt.com` for commands and results.

54 

55### Authentication

56 

57Create a separate restricted executor key. It must belong to the same organization, project, and user or service account that owns the session.

58 

59Create an environment key on the [Agents tab](https://platform.openai.com/agents?tab=environments&environment_view=keys) in the platform dashboard. Set every other permission to **None**. Supply this key to the environment as `CODEX_API_KEY`. Keep your broader application API key outside the environment.

60 

61Agent-generated code can read the executor key, but the key only permits connecting environments. It cannot authorize any other API action. Keep it out of source code, container images, and logs. Rotate or revoke it when needed.

62 

63 

64 

65 

66## Create a session

67 

68Run this example in your application, outside the environment. If you already have a self-hosted session, reuse it.

69 

70Create a session with your own environment

71 

72```javascript

73import OpenAI from "openai";

74const client = new OpenAI();

75 

76const session = await client.beta.agents.sessions.create({

77 agent: {

78 model: "gpt-6-astra",

79 instructions:

80 "You are a helpful coding assistant. Write clean code and verify that it works.",

81 },

82 environment: {

83 type: "self_hosted",

84 workspace_directory: "/workspace",

85 },

86});

87 

88console.log(session);

89```

90 

91```python

92from openai import OpenAI

93 

94client = OpenAI()

95 

96session = client.beta.agents.sessions.create(

97 agent={

98 "model": "gpt-6-astra",

99 "instructions": "You are a helpful coding assistant. Write clean code and verify that it works.",

100 },

101 environment={"type": "self_hosted", "workspace_directory": "/workspace"},

102)

103print(session.to_json())

104```

105 

106```go

107import (

108 "context"

109 "fmt"

110 

111 "github.com/openai/openai-go/v3"

112)

113 

114ctx := context.Background()

115client := openai.NewClient()

116result, err := client.Beta.Agents.Sessions.New(ctx,

117 openai.BetaAgentSessionNewParams{

118 Agent: openai.BetaAgentSessionNewParamsAgent{

119 Model: openai.String("gpt-6-astra"),

120 Instructions: openai.String("You are a helpful coding assistant. Write clean code and verify that it works."),

121 },

122 Environment: openai.EnvironmentParamUnion{

123 OfParamSelfHosted: &openai.EnvironmentParamSelfHosted{WorkspaceDirectory: "/workspace"},

124 },

125 })

126if err != nil {

127 panic(err)

128}

129fmt.Println(result)

130```

131 

132```java

133import com.openai.client.OpenAIClient;

134import com.openai.client.okhttp.OpenAIOkHttpClient;

135import com.openai.models.beta.agents.EnvironmentParam;

136import com.openai.models.beta.agents.sessions.SessionCreateParams;

137 

138OpenAIClient client = OpenAIOkHttpClient.fromEnv();

139var result =

140 client

141 .beta()

142 .agents()

143 .sessions()

144 .create(

145 SessionCreateParams.builder()

146 .agent(

147 SessionCreateParams.Agent.builder()

148 .model("gpt-6-astra")

149 .instructions(

150 "You are a helpful coding assistant. Write clean code and verify"

151 + " that it works.")

152 .build())

153 .environment(

154 EnvironmentParam.SelfHosted.builder()

155 .workspaceDirectory("/workspace")

156 .build())

157 .build());

158System.out.println(result);

159```

160 

161```ruby

162require "openai"

163 

164client = OpenAI::Client.new

165result = client.beta.agents.sessions.create(

166 agent: {

167 model: "gpt-6-astra",

168 instructions: "You are a helpful coding assistant. Write clean code and verify that it works."

169 },

170 environment: {

171 type: "self_hosted",

172 workspace_directory: "/workspace"

173 }

174)

175puts result

176```

177 

178 

179Store `session.id` with your application's conversation state. Pass `session.environment.id` and `session.environment.remote_url` to the executor. Use the remote URL unchanged, including when reconnecting. See [Configuring Agents](https://developers.openai.com/api/docs/guides/agents-api/configuration#reuse-an-agent-across-sessions) to use a stored agent.

180 

181 

182 

183 

184You can reuse your environment image, `workspace_directory`, and `capability_directories` across sessions. Each session has its own environment ID and needs its own executor. API [environment templates](https://developers.openai.com/api/docs/guides/agents-api/tools/plugins#reuse-a-hosted-plugin-setup) apply only to OpenAI-hosted environments.

185 

186## Start the executor

187 

188Open the [session event stream](https://developers.openai.com/api/docs/guides/agents-api/sessions/events#consume-a-stream) from your application to receive connection events. Then run this command inside the environment with the restricted `CODEX_API_KEY` configured above. Replace the placeholders with the environment values returned by the API:

189 

190```bash

191codex exec-server \

192 --remote "<session.environment.remote_url>" \

193 --environment-id "<session.environment.id>"

194```

195 

196Leave the executor running while the agent works.

197 

198 

199 

200 

201## Send work and monitor the connection

202 

203[Send input](https://developers.openai.com/api/docs/guides/agents-api/sessions#send-input) from your application while the event stream stays open. The agent needs both a connected environment and user input to start work.

204 

205The stream reports these connection states:

206 

207- `agent.session.environment.pending`: The session is waiting for the executor to connect.

208- `agent.session.environment.connected`: The environment is ready.

209- `agent.session.environment.failed`: The connection failed. Check the environment error and executor logs.

210 

211Continue following the stream for the turn's outcome and output. See [Environment lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle) to manage startup, reconnection, and shutdown from your application or through webhooks.

212 

213## Sandbox providers

214 

215Choose a sandbox provider to run code and work with files. See [Sandbox lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle) to compare application-managed and webhook-managed provisioning.

216 

217| Provider | Guide |

218| --------------------------------- | ------------------------------------------------------------------------------------- |

219| Modal | [Modal setup](https://developers.openai.com/api/docs/guides/agents-api/environments/providers/modal) |

220| Cloudflare | [Cloudflare setup](https://developers.openai.com/api/docs/guides/agents-api/environments/providers/cloudflare) |

221| Vercel | [Vercel setup](https://developers.openai.com/api/docs/guides/agents-api/environments/providers/vercel) |

222| Daytona | [Daytona setup](https://developers.openai.com/api/docs/guides/agents-api/environments/providers/daytona) |

223| Blaxel | [Blaxel setup](https://developers.openai.com/api/docs/guides/agents-api/environments/providers/blaxel) |

224| E2B | [E2B setup](https://developers.openai.com/api/docs/guides/agents-api/environments/providers/e2b) |

225| Runloop | [Runloop setup](https://developers.openai.com/api/docs/guides/agents-api/environments/providers/runloop) |

226| DigitalOcean | [DigitalOcean setup](https://developers.openai.com/api/docs/guides/agents-api/environments/providers/digitalocean) |

227| Oracle Cloud Infrastructure (OCI) | [OCI setup](https://developers.openai.com/api/docs/guides/agents-api/environments/providers/oci) |

228 

229For webhook-managed provisioning, implement a handler using [Sandbox lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle#start-compute-from-webhooks) and your provider's SDK or API. Keep provisioning ownership and cleanup policies explicit.

Details

1# Multi-agent

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5Multi-agent lets an agent delegate tasks to subagents. Each subagent has its own context and can work in parallel with the others. The main agent coordinates their work and combines their results.

6 

7## When to use subagents

8 

9Use subagents for independent tasks, such as reviewing separate documents or investigating different causes of a failure. Give each task a clear question and expected result.

10 

11Keep short tasks and dependent steps in the main agent. Agents that edit the same files must coordinate their changes.

12 

13 

14 

15 

16## Enable multi-agent orchestration

17 

18Set `agent.multi_agent.enabled` to `true` when you create a session. The harness supplies tools to create, message, wait for, and interrupt subagents. You do not declare these tools yourself.

19 

20 

21 

22 

23This example asks two subagents to review separate release notes, then combines their findings. It needs no environment or configured tools:

24 

25Compare release notes

26 

27```javascript

28import OpenAI from "openai";

29 

30const client = new OpenAI();

31 

32const events = await client.beta.agents.sessions.create({

33 agent: {

34 model: "gpt-6-astra",

35 instructions:

36 "Delegate each release to a separate subagent. Ask each to extract customer-visible changes and required migration steps using only its release notes. Wait for both results, then combine them into one release summary with release labels. Do not invent missing details.",

37 multi_agent: { enabled: true, max_concurrent_subagents: 2 },

38 },

39 environment: { type: "none" },

40 input:

41 "Release A: Search now supports filtering by date. Existing queries continue to work. Release B: The export endpoint now returns a download URL instead of file bytes. Update clients to fetch that URL.",

42 stream: true,

43});

44for await (const event of events) {

45 console.log(JSON.stringify(event));

46}

47```

48 

49```python

50from openai import OpenAI

51 

52client = OpenAI()

53 

54with client.beta.agents.sessions.create(

55 agent={

56 "model": "gpt-6-astra",

57 "instructions": "Delegate each release to a separate subagent. Ask each to extract customer-visible changes and required migration steps using only its release notes. Wait for both results, then combine them into one release summary with release labels. Do not invent missing details.",

58 "multi_agent": {"enabled": True, "max_concurrent_subagents": 2},

59 },

60 environment={"type": "none"},

61 input="Release A: Search now supports filtering by date. Existing queries continue to work. Release B: The export endpoint now returns a download URL instead of file bytes. Update clients to fetch that URL.",

62 stream=True,

63) as events:

64 for event in events:

65 print(event.model_dump_json())

66```

67 

68```go

69import (

70 "context"

71 "fmt"

72 "github.com/openai/openai-go/v3"

73)

74 

75ctx := context.Background()

76client := openai.NewClient()

77events := client.Beta.Agents.Sessions.NewStreaming(ctx, openai.BetaAgentSessionNewParams{Agent: openai.BetaAgentSessionNewParamsAgent{Model: openai.String("gpt-6-astra"),

78 Instructions: openai.String("Delegate each release to a separate subagent. Ask each to extract customer-visible changes and required migration steps using only its release notes. Wait for both results, then combine them into one release summary with release labels. Do not invent missing details."),

79 MultiAgent: openai.MultiAgentConfigParam{Enabled: true,

80 MaxConcurrentSubagents: openai.Int(2)}},

81 Environment: openai.EnvironmentParamUnion{OfParamNone: &openai.EnvironmentParamNone{}},

82 Input: openai.BetaAgentSessionNewParamsInputUnion{OfString: openai.String("Release A: Search now supports filtering by date. Existing queries continue to work. Release B: The export endpoint now returns a download URL instead of file bytes. Update clients to fetch that URL.")}})

83defer events.Close()

84for events.Next() {

85 fmt.Println(events.Current().RawJSON())

86}

87if err := events.Err(); err != nil {

88 panic(err)

89}

90```

91 

92```java

93import com.openai.client.OpenAIClient;

94import com.openai.client.okhttp.OpenAIOkHttpClient;

95import com.openai.models.beta.agents.MultiAgentConfigParam;

96import com.openai.models.beta.agents.sessions.SessionCreateParams;

97 

98OpenAIClient client = OpenAIOkHttpClient.fromEnv();

99try (var events =

100 client

101 .beta()

102 .agents()

103 .sessions()

104 .createStreaming(

105 SessionCreateParams.builder()

106 .agent(

107 SessionCreateParams.Agent.builder()

108 .model("gpt-6-astra")

109 .instructions(

110 "Delegate each release to a separate subagent. Ask each to extract"

111 + " customer-visible changes and required migration steps using"

112 + " only its release notes. Wait for both results, then combine"

113 + " them into one release summary with release labels. Do not"

114 + " invent missing details.")

115 .multiAgent(

116 MultiAgentConfigParam.builder()

117 .enabled(true)

118 .maxConcurrentSubagents(2L)

119 .build())

120 .build())

121 .environmentNone()

122 .input(

123 "Release A: Search now supports filtering by date. Existing queries"

124 + " continue to work. Release B: The export endpoint now returns a"

125 + " download URL instead of file bytes. Update clients to fetch that"

126 + " URL.")

127 .build())) {

128 events.stream().forEach(System.out::println);

129}

130```

131 

132```ruby

133require "openai"

134require "json"

135 

136client = OpenAI::Client.new

137 

138events = client.beta.agents.sessions.create_streaming(

139 agent: {

140 model: "gpt-6-astra",

141 instructions: "Delegate each release to a separate subagent. Ask each to extract customer-visible changes and required migration steps using only its release notes. Wait for both results, then combine them into one release summary with release labels. Do not invent missing details.",

142 multi_agent: {

143 enabled: true,

144 max_concurrent_subagents: 2

145 }

146 },

147 environment: { type: "none" },

148 input: "Release A: Search now supports filtering by date. Existing queries continue to work. Release B: The export endpoint now returns a download URL instead of file bytes. Update clients to fetch that URL."

149)

150begin

151 events.each { |event| puts JSON.generate(event.to_h) }

152ensure

153 events.close

154end

155```

156 

157```bash

158curl --no-buffer --fail-with-body https://api.openai.com/v1/agents/sessions \

159 -H "OpenAI-Beta: agents=v1" \

160 -H "Authorization: Bearer $OPENAI_API_KEY" \

161 -H "Content-Type: application/json" \

162 -d '{

163 "agent": {

164 "model": "gpt-6-astra",

165 "instructions": "Delegate each release to a separate subagent. Ask each to extract customer-visible changes and required migration steps using only its release notes. Wait for both results, then combine them into one release summary with release labels. Do not invent missing details.",

166 "multi_agent": { "enabled": true, "max_concurrent_subagents": 2 }

167 },

168 "environment": { "type": "none" },

169 "input": "Release A: Search now supports filtering by date. Existing queries continue to work. Release B: The export endpoint now returns a download URL instead of file bytes. Update clients to fetch that URL.",

170 "stream": true

171 }'

172```

173 

174 

175With `environment.type: "none"`, include the initial `input` in the create request. Setting `stream: true` also streams the first turn. See [Session events and items](https://developers.openai.com/api/docs/guides/agents-api/sessions/events) for stream handling and recovery.

176 

177### Concurrency settings

178 

179`max_concurrent_subagents` limits how many subagents can run at once. The default is `6`, excluding the coordinator. Set a positive integer when delegation is enabled.

180 

181To disable delegation, omit `multi_agent`, or set `enabled` to `false` and omit the limit. These settings apply at session creation. Changes to a stored agent apply to new sessions.

182 

183## Use an environment

184 

185When agents need files or command execution, [add an environment](https://developers.openai.com/api/docs/guides/agents-api/architecture). The coordinator and subagents share its filesystem. Creating a subagent does not create another environment.

186 

187This example creates a session for work in your own environment:

188 

189Enable delegation with your own environment

190 

191```javascript

192const result = await client.beta.agents.sessions.create({

193 agent: {

194 model: "gpt-6-astra",

195 instructions:

196 "Prepare release notes from the repository. Have one subagent identify customer-visible changes and another check migration guides and examples, then combine their findings.",

197 multi_agent: {

198 enabled: true,

199 max_concurrent_subagents: 3,

200 },

201 },

202 environment: {

203 type: "self_hosted",

204 workspace_directory: "/workspace",

205 },

206});

207```

208 

209```python

210result = client.beta.agents.sessions.create(

211 agent={

212 "model": "gpt-6-astra",

213 "instructions": "Prepare release notes from the repository. Have one subagent identify customer-visible changes and another check migration guides and examples, then combine their findings.",

214 "multi_agent": {"enabled": True, "max_concurrent_subagents": 3},

215 },

216 environment={"type": "self_hosted", "workspace_directory": "/workspace"},

217)

218```

219 

220```go

221result, err := client.Beta.Agents.Sessions.New(ctx,

222 openai.BetaAgentSessionNewParams{

223 Agent: openai.BetaAgentSessionNewParamsAgent{

224 Model: openai.String("gpt-6-astra"),

225 Instructions: openai.String("Prepare release notes from the repository. Have one subagent identify customer-visible changes and another check migration guides and examples, then combine their findings."),

226 MultiAgent: openai.MultiAgentConfigParam{

227 Enabled: true,

228 MaxConcurrentSubagents: openai.Int(3),

229 },

230 },

231 Environment: openai.EnvironmentParamUnion{

232 OfParamSelfHosted: &openai.EnvironmentParamSelfHosted{WorkspaceDirectory: "/workspace"},

233 },

234 })

235if err != nil {

236 panic(err)

237}

238```

239 

240```java

241var result =

242 client

243 .beta()

244 .agents()

245 .sessions()

246 .create(

247 SessionCreateParams.builder()

248 .agent(

249 SessionCreateParams.Agent.builder()

250 .model("gpt-6-astra")

251 .instructions(

252 "Prepare release notes from the repository. Have one subagent"

253 + " identify customer-visible changes and another check"

254 + " migration guides and examples, then combine their"

255 + " findings.")

256 .multiAgent(

257 MultiAgentConfigParam.builder()

258 .enabled(true)

259 .maxConcurrentSubagents(3L)

260 .build())

261 .build())

262 .environment(

263 EnvironmentParam.SelfHosted.builder()

264 .workspaceDirectory("/workspace")

265 .build())

266 .build());

267```

268 

269```ruby

270result = client.beta.agents.sessions.create(

271 agent: {

272 model: "gpt-6-astra",

273 instructions: "Prepare release notes from the repository. Have one subagent identify customer-visible changes and another check migration guides and examples, then combine their findings.",

274 multi_agent: {

275 enabled: true,

276 max_concurrent_subagents: 3

277 }

278 },

279 environment: {

280 type: "self_hosted",

281 workspace_directory: "/workspace"

282 }

283)

284```

285 

286```bash

287curl https://api.openai.com/v1/agents/sessions \

288 -H "OpenAI-Beta: agents=v1" \

289 -H "Authorization: Bearer $OPENAI_API_KEY" \

290 -H "Content-Type: application/json" \

291 -d '{

292 "agent": {

293 "model": "gpt-6-astra",

294 "instructions": "Prepare release notes from the repository. Have one subagent identify customer-visible changes and another check migration guides and examples, then combine their findings.",

295 "multi_agent": {

296 "enabled": true,

297 "max_concurrent_subagents": 3

298 }

299 },

300 "environment": {

301 "type": "self_hosted",

302 "workspace_directory": "/workspace"

303 }

304 }'

305```

306 

307 

308Store the returned session and environment IDs in your application. [Connect the environment](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted), then [send input](https://developers.openai.com/api/docs/guides/agents-api/sessions#send-input) to start work.

309 

310### Tools available to subagents

311 

312Subagents inherit configured MCP tools, their credentials and allowed tools, and web search settings. They can also use the environment's files and command-line tools. Subagents do not support [function tools](https://developers.openai.com/api/docs/guides/agents-api/tools/functions).

313 

314## Observe delegation

315 

316The [session event stream](https://developers.openai.com/api/docs/guides/agents-api/sessions/events) reports subagent activity:

317 

318- `agent.session.subagent.created` provides the new subagent's ID.

319- `agent.session.turn.item.added` and `agent.session.turn.item.done` report coordination actions. Their item types include `create_subagent_call`, `send_subagent_input_call`, `wait_for_subagents_call`, and `interrupt_subagent_call`.

320 

321The harness executes these actions. A completed create or wait action does not mean the subagent finished its task. On a create item, `agent_id` identifies the agent that requested the subagent.

322 

323 

324 

325 

326Coordination items can omit message content. An `agent_message` item contains inter-agent text when available, but the stream does not provide a full conversation transcript.

327 

328 

329 

330 

331Read the main agent's response for the combined result. Use [saved items and turns](https://developers.openai.com/api/docs/guides/agents-api/sessions/events#fetch-items-and-turns) to inspect prior work, including each subagent's history.

332 

333### Attribute commands

334 

335Given a command item and its session ID, retrieve the command's turn to identify the agent that ran it. The turn's `subagent_id` is `null` for the main agent.

336 

337Identify the agent that ran a command

338 

339```javascript

340// Use the saved session ID and command execution item from your application.

341const turn = await client.beta.agents.sessions.turns.retrieve(

342 command.turn_id,

343 { session_id: sessionId }

344);

345console.log(turn.subagent_id);

346```

347 

348```python

349# Use the saved session ID and command execution item from your application.

350turn = client.beta.agents.sessions.turns.retrieve(

351 command.turn_id, session_id=session_id

352)

353print(turn.subagent_id)

354```

355 

356```go

357// Use the saved session ID and command execution item from your application.

358turn, err := client.Beta.Agents.Sessions.Turns.Get(ctx, sessionID, item.TurnID)

359if err != nil {

360 panic(err)

361}

362fmt.Println(turn.SubagentID)

363```

364 

365```java

366// Use the saved session ID and command execution item from your application.

367var turn =

368 client

369 .beta()

370 .agents()

371 .sessions()

372 .turns()

373 .retrieve(

374 TurnRetrieveParams.builder()

375 .sessionId(sessionId)

376 .turnId(command.turnId())

377 .build());

378System.out.println(turn.subagentId());

379```

380 

381```ruby

382# Use the saved session ID and command execution item from your application.

383turn = client.beta.agents.sessions.turns.retrieve(item.turn_id, session_id: session_id)

384puts turn.subagent_id

385```

Details

1# Observability and usage

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5Track live agent activity, inspect completed work, and review detailed turn traces:

6 

71. 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.

11 

12## View the session in the dashboard

13 

14Go to [platform.openai.com/logs?api=agents](https://platform.openai.com/logs?api=agents) and open the **Agents** tab.

15 

16Search for a session by ID to inspect its turns, tool calls, and subagents.

17 

18Use 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. Trace retrieval and external trace exporters are not part of the public beta API.

19 

20## Follow events and inspect session history

21 

22Every 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:

23 

24Follow live session events

25 

26```javascript

27// Replace the illustrative IDs and URLs below with your own resource values.

28import OpenAI from "openai";

29 

30const 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```

51 

52```python

53# Replace the illustrative IDs and URLs below with your own resource values.

54from openai import OpenAI

55 

56client = 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```

70 

71```go

72// Replace the illustrative IDs and URLs below with your own resource values.

73import (

74 "context"

75 "fmt"

76 

77 "github.com/openai/openai-go/v3"

78)

79 

80ctx := 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```

99 

100```java

101// 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;

107 

108OpenAIClient 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```

126 

127```ruby

128# Replace the illustrative IDs and URLs below with your own resource values.

129require "openai"

130require "json"

131 

132client = OpenAI::Client.new

133events = client.beta.agents.sessions.events.stream_streaming("sess_123")

134begin

135 events.each do |event|

136 case event.type.to_s

137 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 end

140 puts JSON.generate(event.to_h)

141 end

142ensure

143 events.close

144end

145```

146 

147```bash

148curl -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```

154 

155 

156The stream stays open across idle events so you don't miss queued work. Press **Ctrl+C** to stop watching.

157 

158As the session runs, you’ll see events such as:

159 

160```text

161agent.session.environment.connected

162agent.session.turn.created

163agent.session.turn.in_progress

164agent.session.turn.item.added

165agent.session.turn.output_text.delta

166agent.session.turn.completed

167agent.session.idle

168```

169 

170To inspect work that has already happened, retrieve the session’s saved items:

171 

172Inspect saved session items

173 

174```javascript

175// Replace the illustrative IDs and URLs below with your own resource values.

176import OpenAI from "openai";

177const client = new OpenAI();

178 

179const 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```

186 

187```python

188# Replace the illustrative IDs and URLs below with your own resource values.

189from openai import OpenAI

190 

191client = OpenAI()

192 

193session_id = "sess_123"

194items = client.beta.agents.sessions.items.list(session_id, order="asc", limit=100)

195print(items.to_json())

196```

197 

198```go

199// Replace the illustrative IDs and URLs below with your own resource values.

200import (

201 "context"

202 "fmt"

203 

204 "github.com/openai/openai-go/v3"

205)

206 

207ctx := 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```

220 

221```java

222// 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;

226 

227OpenAIClient client = OpenAIOkHttpClient.fromEnv();

228var result =

229 client

230 .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```

242 

243```ruby

244# Replace the illustrative IDs and URLs below with your own resource values.

245require "openai"

246 

247client = OpenAI::Client.new

248result = client.beta.agents.sessions.items.list(

249 "sess_123",

250 order: "asc",

251 limit: 100

252)

253puts result.data

254```

255 

256```bash

257curl \

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```

262 

263 

264## Inspect turns and identify delegated commands

265 

266Session 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`:

267 

268Identify delegated command execution

269 

270```javascript

271// Replace the illustrative IDs and URLs below with your own resource values.

272import OpenAI from "openai";

273const client = new OpenAI();

274 

275const 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```

287 

288```python

289# Replace the illustrative IDs and URLs below with your own resource values.

290from openai import OpenAI

291 

292client = OpenAI()

293 

294session_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```

301 

302```go

303// Replace the illustrative IDs and URLs below with your own resource values.

304import (

305 "context"

306 "fmt"

307 

308 "github.com/openai/openai-go/v3"

309)

310 

311ctx := 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```

331 

332```java

333// 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;

338 

339OpenAIClient client = OpenAIOkHttpClient.fromEnv();

340var result =

341 client

342 .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 client

355 .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```

363 

364```ruby

365# Replace the illustrative IDs and URLs below with your own resource values.

366require "openai"

367 

368client = OpenAI::Client.new

369result = client.beta.agents.sessions.turns.list(

370 "sess_123",

371 limit: 20,

372 order: "desc"

373)

374puts result.data

375turn = client.beta.agents.sessions.turns.retrieve(

376 "turn_123",

377 session_id: "sess_123"

378)

379puts turn.subagent_id

380```

381 

382```bash

383curl "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"

386 

387curl "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```

391 

392 

393Use the returned `last_id` as the next page's `after` value when `has_more` is `true`.

394 

395Command 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.

396 

397## Inspect a turn trace

398 

399Use the Platform dashboard to inspect a completed turn and its agent activity.

400Detailed trace retrieval is not available through an ordinary project API key. Dashboard trace endpoints require separate access and are not a

401supported customer API.

402 

403Turn 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).

404 

405To attribute a shell command, retrieve the turn identified by its command item's

406`turn_id`, then inspect `turn.subagent_id`. The customer API does not indicate

407whether command output was truncated.

408 

409## Model usage and cost

410 

411An 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.

412 

413### What contributes to cost?

414 

415Each model call can consume:

416 

417- **Input tokens:** agent instructions, tool definitions, conversation history, user input, files or images, and tool results.

418- **Cached input tokens:** input reused from a matching prompt prefix, billed at the model's cached-input rate.

419- **Output tokens:** generated text, tool-call arguments, and reasoning.

420 

421Reasoning tokens are billed as output tokens.

422 

423Subagents can also make model calls. Inspect their recorded [turn usage](#inspect-subagent-token-usage) alongside root-agent work when investigating model costs.

424 

425Account 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.

426 

427### Prompt caching

428 

429Agents 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.

430 

431Keep 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.

432 

433A 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.

434 

435### Understand token usage

436 

437Session 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.

438 

439A recorded usage object contains these token categories:

440 

441```json

442{

443 "input_tokens": 5000,

444 "input_tokens_details": {

445 "cached_tokens": 1500

446 },

447 "output_tokens": 900,

448 "output_tokens_details": {

449 "reasoning_tokens": 200

450 },

451 "total_tokens": 5900

452}

453```

454 

455In 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.

456 

457Cached tokens are included in `input_tokens`, and reasoning tokens are included in `output_tokens`.

458 

459### Inspect subagent token usage

460 

461List 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.

462 

463Usage 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).

guides/agents-api/overview.md +355 −0 created

Details

1# Agents API

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5The Agents API gives your application access to the Codex harness through an OpenAI-managed API.

6 

7OpenAI manages sessions, orchestration, context compaction, and recovery while your application provides tools and chooses its execution environment.

8 

9Agents can operate in a sandbox where they can execute code, edit files, connect to MCP servers, and produce artifacts.

10 

11## Pricing

12 

13Model usage is billed at the selected model's [API rates](https://developers.openai.com/api/docs/pricing). OpenAI tools use their [standard rates](https://developers.openai.com/api/docs/pricing#built-in-tools), and OpenAI-hosted sandboxes use standard [container rates](https://developers.openai.com/api/docs/pricing#built-in-tools).

14 

15## Try an example

16 

17Try these complete examples:

18 

19- [Create and run a directory-tree script](https://developers.openai.com/api/docs/guides/agents-api/quickstart#1-run-a-task) in an OpenAI-hosted sandbox.

20- [Compare release notes with subagents](https://developers.openai.com/api/docs/guides/agents-api/multi-agent#example-compare-release-notes) and combine their findings into one answer.

21 

22Explore complete applications:

23 

24- [Incident response agent](https://developers.openai.com/showcase/agents-api-sev-bot): investigate alerts and request approval for recovery actions.

25- [Slack bot](https://developers.openai.com/showcase/agents-api-slack-bot): investigate requests using connected workplace tools.

26- [Data analyst](https://developers.openai.com/showcase/agents-api-data-analyst): answer warehouse questions with read-only SQL.

27- [GitHub issue investigator](https://developers.openai.com/showcase/agents-api-github-issues): reproduce reported bugs and share findings on GitHub.

28- [Document reviewer](https://developers.openai.com/showcase/agents-api-document-review): review documents with policy skills and specialist agents.

29 

30## Core concepts

31 

32The Agents API is built around four main concepts:

33 

34- **Agent:** The model, instructions, tools, and MCP servers available to the agent.

35- **Environment:** An optional sandbox or computer where the agent accesses files, loads skills, and runs commands.

36- **Session:** A durable instance of an agent that works on tasks and responds to input.

37- **Events and items:** The inputs sent to an agent and the output produced during a session.

38 

39### A session from start to finish

40 

41Start with an OpenAI-hosted sandbox in the [quickstart](https://developers.openai.com/api/docs/guides/agents-api/quickstart):

42 

431. **Create a session.** Configure the agent; OpenAI provisions its environment.

442. **Give it a task.** User input starts a turn of work once the environment is ready.

453. **Follow progress.** Stream output or use webhooks to learn when the agent finishes or needs input.

464. **Continue or steer.** Send another task to the same session, or guide the agent during its current turn.

47 

48With an OpenAI-hosted session, your application sends input and receives events, while OpenAI runs the agent and provisions and manages its sandbox. See [environment options](https://developers.openai.com/api/docs/guides/agents-api/configuration#environment-settings) for setup and limitations.

49 

50<picture>

51 <source

52 media="(max-width: 640px)"

53 srcSet="/images/api/agents-api/overview-1-mobile.webp"

54 width="680"

55 height="1288"

56 />

57 <img src="https://developers.openai.com/images/api/agents-api/overview-1.webp"

58 width="1400"

59 height="552"

60 alt="Your application starts sessions and receives events and output from the Agents API. OpenAI runs the managed Codex harness and provisions and manages its sandbox."

61 loading="lazy"

62 />

63</picture>

64 

65## What the managed harness provides

66 

67The managed Codex harness supports:

68 

69- Running commands and code in a sandbox.

70- Applying relevant skills and instructions.

71- Connecting to external data through tools or MCP.

72- Steering the agent while it works.

73- Summarizing previous work to manage its context window.

74- Breaking work into subtasks and delegating to subagents.

75- Resuming a session where it left off.

76 

77Check the [quickstart prerequisites](https://developers.openai.com/api/docs/guides/agents-api/quickstart#prerequisites) for API-key permissions and SDK setup. Configure these capabilities when you create a session:

78 

79Configure managed-harness capabilities

80 

81```javascript

82import OpenAI from "openai";

83 

84const client = new OpenAI();

85 

86const session = await client.beta.agents.sessions.create({

87 agent: {

88 model: "gpt-6-astra",

89 instructions:

90 "Use the OpenAI documentation MCP and web search to answer technical questions accurately. Delegate independent research tasks to subagents when useful.",

91 tools: [

92 { type: "programmatic_tool_calling" },

93 {

94 type: "mcp",

95 server_label: "openai_docs",

96 transport: {

97 type: "http",

98 server_url: "https://developers.openai.com/mcp",

99 },

100 },

101 { type: "web_search" },

102 ],

103 multi_agent: { enabled: true, max_concurrent_subagents: 4 },

104 },

105 environment: {

106 type: "self_hosted",

107 workspace_directory: "/workspace",

108 capability_directories: ["/workspace/capabilities/skills"],

109 },

110 input: [

111 {

112 role: "user",

113 content: [

114 {

115 type: "input_text",

116 text: "Research how to connect an MCP server to an OpenAI agent, check for recent updates, and summarize the recommended setup.",

117 },

118 ],

119 },

120 ],

121});

122console.log(session.id);

123```

124 

125```python

126from openai import OpenAI

127 

128client = OpenAI()

129 

130session = client.beta.agents.sessions.create(

131 agent={

132 "model": "gpt-6-astra",

133 "instructions": "Use the OpenAI documentation MCP and web search to answer technical questions accurately. Delegate independent research tasks to subagents when useful.",

134 "tools": [

135 {"type": "programmatic_tool_calling"},

136 {

137 "type": "mcp",

138 "server_label": "openai_docs",

139 "transport": {

140 "type": "http",

141 "server_url": "https://developers.openai.com/mcp",

142 },

143 },

144 {"type": "web_search"},

145 ],

146 "multi_agent": {"enabled": True, "max_concurrent_subagents": 4},

147 },

148 environment={

149 "type": "self_hosted",

150 "workspace_directory": "/workspace",

151 "capability_directories": ["/workspace/capabilities/skills"],

152 },

153 input=[

154 {

155 "role": "user",

156 "content": [

157 {

158 "type": "input_text",

159 "text": "Research how to connect an MCP server to an OpenAI agent, check for recent updates, and summarize the recommended setup.",

160 }

161 ],

162 }

163 ],

164)

165print(session.id)

166```

167 

168```go

169import (

170 "context"

171 "fmt"

172 "github.com/openai/openai-go/v3"

173)

174 

175ctx := context.Background()

176client := openai.NewClient()

177session, err := client.Beta.Agents.Sessions.New(ctx, openai.BetaAgentSessionNewParams{Agent: openai.BetaAgentSessionNewParamsAgent{Model: openai.String("gpt-6-astra"),

178 Instructions: openai.String("Use the OpenAI documentation MCP and web search to answer technical questions accurately. Delegate independent research tasks to subagents when useful."),

179 Tools: []openai.AgentToolParamUnion{openai.AgentToolParamUnion{OfParamProgrammaticToolCalling: &openai.AgentToolParamProgrammaticToolCalling{}},

180 openai.AgentToolParamUnion{OfParamMcp: &openai.AgentToolParamMcp{ServerLabel: "openai_docs",

181 Transport: openai.McpTransportParamUnion{OfParamHTTP: &openai.McpTransportParamHTTP{ServerURL: "https://developers.openai.com/mcp"}}}},

182 openai.AgentToolParamUnion{OfParamWebSearch: &openai.AgentToolParamWebSearch{}}},

183 MultiAgent: openai.MultiAgentConfigParam{Enabled: true,

184 MaxConcurrentSubagents: openai.Int(4)}},

185 Environment: openai.EnvironmentParamUnion{OfParamSelfHosted: &openai.EnvironmentParamSelfHosted{WorkspaceDirectory: "/workspace",

186 CapabilityDirectories: []string{"/workspace/capabilities/skills"}}},

187 Input: openai.BetaAgentSessionNewParamsInputUnion{OfArrayOfInputMessages: []openai.AgentSessionInputMessageParam{openai.AgentSessionInputMessageParam{Content: []openai.InputContentParamUnion{openai.InputContentParamUnion{OfParamInputText: &openai.InputContentParamInputText{Text: "Research how to connect an MCP server to an OpenAI agent, check for recent updates, and summarize the recommended setup."}}}}}}})

188if err != nil {

189 panic(err)

190}

191fmt.Println(session.ID)

192```

193 

194```java

195import com.openai.client.OpenAIClient;

196import com.openai.client.okhttp.OpenAIOkHttpClient;

197import com.openai.models.beta.agents.AgentToolParam;

198import com.openai.models.beta.agents.EnvironmentParam;

199import com.openai.models.beta.agents.McpTransportParam;

200import com.openai.models.beta.agents.MultiAgentConfigParam;

201import com.openai.models.beta.agents.sessions.SessionCreateParams;

202import java.util.List;

203 

204OpenAIClient client = OpenAIOkHttpClient.fromEnv();

205var session =

206 client

207 .beta()

208 .agents()

209 .sessions()

210 .create(

211 SessionCreateParams.builder()

212 .agent(

213 SessionCreateParams.Agent.builder()

214 .model("gpt-6-astra")

215 .instructions(

216 "Use the OpenAI documentation MCP and web search to answer"

217 + " technical questions accurately. Delegate independent"

218 + " research tasks to subagents when useful.")

219 .addTool(AgentToolParam.ProgrammaticToolCalling.builder().build())

220 .addTool(

221 AgentToolParam.Mcp.builder()

222 .serverLabel("openai_docs")

223 .transport(

224 McpTransportParam.Http.builder()

225 .serverUrl("https://developers.openai.com/mcp")

226 .build())

227 .build())

228 .addTool(AgentToolParam.WebSearch.builder().build())

229 .multiAgent(

230 MultiAgentConfigParam.builder()

231 .enabled(true)

232 .maxConcurrentSubagents(4L)

233 .build())

234 .build())

235 .environment(

236 EnvironmentParam.SelfHosted.builder()

237 .workspaceDirectory("/workspace")

238 .capabilityDirectories(List.of("/workspace/capabilities/skills"))

239 .build())

240 .input(

241 "Research how to connect an MCP server to an OpenAI agent, check for recent"

242 + " updates, and summarize the recommended setup.")

243 .build());

244System.out.println(session.id());

245```

246 

247```ruby

248require "openai"

249 

250client = OpenAI::Client.new

251 

252session = client.beta.agents.sessions.create(

253 agent: {

254 model: "gpt-6-astra",

255 instructions: "Use the OpenAI documentation MCP and web search to answer technical questions accurately. Delegate independent research tasks to subagents when useful.",

256 tools: [

257 { type: "programmatic_tool_calling" },

258 {

259 type: "mcp",

260 server_label: "openai_docs",

261 transport: {

262 type: "http",

263 server_url: "https://developers.openai.com/mcp"

264 }

265 },

266 { type: "web_search" }

267 ],

268 multi_agent: {

269 enabled: true,

270 max_concurrent_subagents: 4

271 }

272 },

273 environment: {

274 type: "self_hosted",

275 workspace_directory: "/workspace",

276 capability_directories: ["/workspace/capabilities/skills"]

277 },

278 input: [

279 {

280 role: "user",

281 content: [

282 {

283 type: "input_text",

284 text: "Research how to connect an MCP server to an OpenAI agent, check for recent updates, and summarize the recommended setup."

285 }

286 ]

287 }

288 ]

289)

290puts session.id

291```

292 

293```bash

294curl -sS -X POST "https://api.openai.com/v1/agents/sessions" \

295 -H "OpenAI-Beta: agents=v1" \

296 -H "Authorization: Bearer $OPENAI_API_KEY" \

297 -H "Content-Type: application/json" \

298 -d '{

299 "agent": {

300 "model": "gpt-6-astra",

301 "instructions": "Use the OpenAI documentation MCP and web search to answer technical questions accurately. Delegate independent research tasks to subagents when useful.",

302 "tools": [

303 {

304 "type": "programmatic_tool_calling"

305 },

306 {

307 "type": "mcp",

308 "server_label": "openai_docs",

309 "transport": {

310 "type": "http",

311 "server_url": "https://developers.openai.com/mcp"

312 }

313 },

314 {

315 "type": "web_search"

316 }

317 ],

318 "multi_agent": {

319 "enabled": true,

320 "max_concurrent_subagents": 4

321 }

322 },

323 "environment": {

324 "type": "self_hosted",

325 "workspace_directory": "/workspace",

326 "capability_directories": ["/workspace/capabilities/skills"]

327 },

328 "input": [

329 {

330 "role": "user",

331 "content": [

332 {

333 "type": "input_text",

334 "text": "Research how to connect an MCP server to an OpenAI agent, check for recent updates, and summarize the recommended setup."

335 }

336 ]

337 }

338 ]

339 }'

340```

341 

342 

343 

344 

345 

346For a runtime comparison, see the [Agents overview](https://developers.openai.com/api/docs/guides/agents#compare-agent-runtimes).

347 

348The Agents API retains session state so you can continue work across turns without

349 rebuilding the conversation context. You can delete sessions and published

350 artifacts when you no longer need them.

351 The Agents API currently supports data residency only in the United States and

352 does not support Zero Data Retention (ZDR). Choosing a self-hosted sandbox does

353 not make the Agents API ZDR-eligible. See [Data controls

354 in the OpenAI platform](https://developers.openai.com/api/docs/guides/your-data#storage-requirements-and-retention-controls-per-endpoint)

355 for details on data residency and retention.

Details

1# Agents API quickstart

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5Build a coding assistant that writes `tree.py`, runs it, and shows a directory tree. OpenAI manages the agent, its conversation, and the sandbox where it works.

6 

7## Prerequisites

8 

9Create an [application API key](https://platform.openai.com/api-keys) in your OpenAI Platform project. Grant `api.agents.read` and `api.agents.write` for session operations, plus `api.responses.write` for model inference, then export it:

10 

11```bash

12export OPENAI_API_KEY="your-api-key"

13```

14 

15Keep this key outside the agent's sandbox. See [OpenAI-hosted sandboxes](https://developers.openai.com/api/docs/guides/agents-api/environments/openai-hosted#configure-the-sandbox) for sandbox configuration and limits.

16 

17Requests require the `OpenAI-Beta: agents=v1` header. The OpenAI SDKs add it

18 automatically; include it explicitly when using cURL.

19 

20## 1. Run a task

21 

22Choose a language, install the OpenAI SDK, and run the example. The SDK examples use the `beta.agents` namespace. The request creates a session, submits a task, and streams progress.

23 

24 

25 

26Python

27 

28 

29Install or update the Python SDK:

30 

31```bash

32pip install --upgrade openai

33```

34 

35Save the example as `quickstart.py`:

36 

37Create and run tree.py

38 

39```python

40from openai import OpenAI

41 

42with OpenAI() as client:

43 with client.beta.agents.sessions.create(

44 agent={

45 "model": "gpt-6-astra",

46 "instructions": "Write clean code, run it, and report the actual output.",

47 },

48 environment={"type": "openai_hosted"},

49 input="Create tree.py, a Python script that prints a readable tree of the files in the current directory. Run it and show me the output.",

50 stream=True,

51 ) as events:

52 for event in events:

53 print(event.to_json(indent=None), flush=True)

54```

55 

56 

57Run it from your terminal:

58 

59```bash

60python quickstart.py

61```

62 

63

64 

65 

66

67 

68

69JavaScript

70 

71 

72Install the JavaScript SDK:

73 

74```bash

75npm install openai

76```

77 

78Save the example as `quickstart.mjs`:

79 

80Create and run tree.py

81 

82```javascript

83import OpenAI from "openai";

84 

85const client = new OpenAI();

86const events = await client.beta.agents.sessions.create({

87 agent: {

88 model: "gpt-6-astra",

89 instructions: "Write clean code, run it, and report the actual output.",

90 },

91 environment: { type: "openai_hosted" },

92 input:

93 "Create tree.py, a Python script that prints a readable tree of the files in the current directory. Run it and show me the output.",

94 stream: true,

95});

96try {

97 for await (const event of events) {

98 console.log(JSON.stringify(event));

99 }

100} finally {

101 events.controller.abort();

102}

103```

104 

105 

106Run it from your terminal:

107 

108```bash

109node quickstart.mjs

110```

111 

112

113 

114 

115

116 

117

118Go

119 

120 

121In a new directory, create a Go module and install the SDK:

122 

123```bash

124go mod init agents-quickstart

125go get github.com/openai/openai-go/v3@latest

126```

127 

128Save the example as `main.go`:

129 

130Create and run tree.py

131 

132```go

133import (

134 "context"

135 "fmt"

136 

137 "github.com/openai/openai-go/v3"

138)

139 

140ctx := context.Background()

141client := openai.NewClient()

142events := client.Beta.Agents.Sessions.NewStreaming(ctx, openai.BetaAgentSessionNewParams{

143 Agent: openai.BetaAgentSessionNewParamsAgent{

144 Model: openai.String("gpt-6-astra"),

145 Instructions: openai.String("Write clean code, run it, and report the actual output."),

146 },

147 Environment: openai.EnvironmentParamUnion{OfParamOpenAIHosted: &openai.EnvironmentParamOpenAIHosted{}},

148 Input: openai.BetaAgentSessionNewParamsInputUnion{

149 OfString: openai.String("Create tree.py, a Python script that prints a readable tree of the files in the current directory. Run it and show me the output."),

150 },

151})

152defer events.Close()

153if events.Err() != nil {

154 panic(events.Err())

155}

156for events.Next() {

157 event := events.Current()

158 fmt.Println(event.RawJSON())

159}

160if err := events.Err(); err != nil {

161 panic(err)

162}

163```

164 

165 

166Run it from your terminal:

167 

168```bash

169go run .

170```

171 

172

173 

174 

175

176 

177

178Java

179 

180 

181Add the OpenAI SDK to your Maven project's `pom.xml`:

182 

183```xml

184<dependency>

185 <groupId>com.openai</groupId>

186 <artifactId>openai-java</artifactId>

187 <version>${apiReferencePackageVersions.java}</version>

188</dependency>

189```

190 

191 

192Save the example as `src/main/java/AgentsApiSessionsStreamConversationExample.java`:

193 

194Create and run tree.py

195 

196```java

197import com.fasterxml.jackson.databind.json.JsonMapper;

198import com.openai.client.OpenAIClient;

199import com.openai.client.okhttp.OpenAIOkHttpClient;

200import com.openai.core.http.StreamResponse;

201import com.openai.models.beta.agents.AgentSessionEvent;

202import com.openai.models.beta.agents.EnvironmentParam;

203import com.openai.models.beta.agents.sessions.SessionCreateParams;

204 

205OpenAIClient client = OpenAIOkHttpClient.fromEnv();

206var json = new JsonMapper();

207try (StreamResponse<AgentSessionEvent> events =

208 client

209 .beta()

210 .agents()

211 .sessions()

212 .createStreaming(

213 SessionCreateParams.builder()

214 .agent(

215 SessionCreateParams.Agent.builder()

216 .model("gpt-6-astra")

217 .instructions("Write clean code, run it, and report the actual output.")

218 .build())

219 .environment(EnvironmentParam.OpenAIHosted.builder().build())

220 .input(

221 "Create tree.py, a Python script that prints a readable tree of the files"

222 + " in the current directory. Run it and show me the output.")

223 .build())) {

224 var iterator = events.stream().iterator();

225 while (iterator.hasNext()) {

226 var event = iterator.next();

227 System.out.println(json.writeValueAsString(event));

228 }

229}

230```

231 

232 

233Run it from your terminal:

234 

235```bash

236mvn compile exec:java -Dexec.mainClass=AgentsApiSessionsStreamConversationExample

237```

238 

239

240 

241 

242

243 

244

245Ruby

246 

247 

248Install the Ruby SDK:

249 

250```bash

251gem install openai

252```

253 

254Save the example as `quickstart.rb`:

255 

256Create and run tree.py

257 

258```ruby

259require "openai"

260require "json"

261 

262client = OpenAI::Client.new

263events = client.beta.agents.sessions.create_streaming(

264 agent: {

265 model: "gpt-6-astra",

266 instructions: "Write clean code, run it, and report the actual output."

267 },

268 environment: { type: "openai_hosted" },

269 input: "Create tree.py, a Python script that prints a readable tree of the files in the current directory. Run it and show me the output."

270)

271begin

272 events.each do |event|

273 puts JSON.generate(event.to_h)

274 end

275ensure

276 events.close

277end

278```

279 

280 

281Run it from your terminal:

282 

283```bash

284ruby quickstart.rb

285```

286 

287

288 

289 

290

291 

292

293cURL

294 

295 

296Use cURL from your terminal; no SDK installation is needed:

297 

298Create and run tree.py

299 

300```bash

301curl --no-buffer --fail-with-body https://api.openai.com/v1/agents/sessions \\\n -H "OpenAI-Beta: agents=v1" \\\n -H "Authorization: Bearer $OPENAI_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "agent": {\n "model": "gpt-6-astra",\n "instructions": "Write clean code, run it, and report the actual output."\n },\n "environment": { "type": "openai_hosted" },\n "input": "Create tree.py, a Python script that prints a readable tree of the files in the current directory. Run it and show me the output.",\n "stream": true\n }\'

302```

303 

304 

305

306 

307 

308## 2. Follow progress

309 

310The terminal shows streamed events. The SDK examples print JSON; cURL shows the raw event stream. On a successful run, the agent creates `tree.py`, executes it, and reports a directory tree containing that file. Other files and output depend on the sandbox.

311 

312Look for `agent.session.turn.completed`, then check the agent's reported execution result. A completed turn does not guarantee every tool succeeded. Events ending in `turn.failed`, `turn.cancelled`, or `session.failed` indicate failure or cancellation; `agent.session.idle` alone does not mean success. If the stream disconnects early, [retrieve the session and its saved items](https://developers.openai.com/api/docs/guides/agents-api/sessions#how-to-recover-a-disconnected-stream) before retrying.

313 

314## 3. Continue the session

315 

316Save the `session_id` from the events. Use it to [send a follow-up](https://developers.openai.com/api/docs/guides/agents-api/sessions#send-input) such as “Add a maximum-depth option to `tree.py`, run it, and show me the output.” Open the event stream before sending follow-up input so you don't miss early events.

317 

318 

319 

320 

321## 4. Clean up

322 

323Keep the session for more tasks, or delete it when you're done. [Save any files you need](https://developers.openai.com/api/docs/guides/agents-api/environments/files) first.

324 

325Replace the illustrative `sess_123` value in the example with the session ID you saved.

326 

327

328 

329

330Python

331 

332 Delete the session

333 

334```python

335# Replace the illustrative IDs and URLs below with your own resource values.

336 

337from openai import OpenAI

338 

339 

340def delete_session(client: OpenAI, session_id: str):

341 return client.beta.agents.sessions.delete(session_id)

342 

343 

344if __name__ == "__main__":

345 result = delete_session(OpenAI(), "sess_123")

346 print(result.to_json())

347```

348 

349

350 

351

352 

353

354JavaScript

355 

356 Delete the session

357 

358```javascript

359// Replace the illustrative IDs and URLs below with your own resource values.

360import OpenAI from "openai";

361 

362/**

363 * @param {OpenAI} client

364 * @param {string} sessionId

365 */

366async function deleteSession(client, sessionId) {

367 return client.beta.agents.sessions.delete(sessionId);

368}

369 

370const result = await deleteSession(new OpenAI(), "sess_123");

371console.log(result);

372```

373 

374

375 

376

377 

378

379Go

380 

381 Delete the session

382 

383```go

384// Replace the illustrative IDs and URLs below with your own resource values.

385package main

386 

387import (

388 "context"

389 "fmt"

390 

391 "github.com/openai/openai-go/v3"

392)

393 

394func deleteSession(ctx context.Context, client *openai.Client, sessionID string) (*openai.AgentSessionDeleted, error) {

395 return client.Beta.Agents.Sessions.Delete(ctx, sessionID)

396}

397 

398func main() {

399 client := openai.NewClient()

400 result, err := deleteSession(context.Background(), &client, "sess_123")

401 if err != nil {

402 panic(err)

403 }

404 fmt.Println(result)

405}

406```

407 

408

409 

410

411 

412

413Java

414 

415 Delete the session

416 

417```java

418// Replace the illustrative IDs and URLs below with your own resource values.

419import com.openai.client.OpenAIClient;

420import com.openai.client.okhttp.OpenAIOkHttpClient;

421import com.openai.models.beta.agents.AgentSessionDeleted;

422import com.openai.models.beta.agents.sessions.SessionDeleteParams;

423 

424public final class AgentsApiSessionsDeleteSessionExample {

425 public static AgentSessionDeleted deleteSession(OpenAIClient client, String sessionId) {

426 return client

427 .beta()

428 .agents()

429 .sessions()

430 .delete(SessionDeleteParams.builder().sessionId(sessionId).build());

431 }

432 

433 public static void main(String[] args) {

434 var result = deleteSession(OpenAIOkHttpClient.fromEnv(), "sess_123");

435 System.out.println(result);

436 }

437}

438```

439 

440

441 

442

443 

444

445Ruby

446 

447 Delete the session

448 

449```ruby

450# Replace the illustrative IDs and URLs below with your own resource values.

451require "openai"

452 

453def delete_session(client, session_id)

454 client.beta.agents.sessions.delete(session_id)

455end

456 

457puts delete_session(OpenAI::Client.new, "sess_123")

458```

459 

460

461 

462

463 

464

465cURL

466 

467 Delete the session

468 

469```bash

470curl -X DELETE "https://api.openai.com/v1/agents/sessions/sess_123" \\\n -H "OpenAI-Beta: agents=v1" \\\n -H "Authorization: Bearer $OPENAI_API_KEY"

471```

472 

473 

474 

475## Next steps

476 

477- [Explore example applications](https://developers.openai.com/api/docs/guides/agents-api/overview#try-an-example).

478- [Configure an OpenAI-hosted sandbox](https://developers.openai.com/api/docs/guides/agents-api/environments/openai-hosted): add packages and input files, control network access, and download artifacts.

479- [Compare release notes with subagents](https://developers.openai.com/api/docs/guides/agents-api/multi-agent#example-compare-release-notes).

480- [Work with files and artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files).

481- [Choose an environment](https://developers.openai.com/api/docs/guides/agents-api/configuration#environment-settings), or [connect your own sandbox](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted).

guides/agents-api/sessions.md +494 −0 created

Details

1# Run and continue sessions

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5A session keeps an agent's configuration, conversation, and saved work over time. Reuse the same session to send follow-up messages and continue the work.

6 

7 

8 

9 

10## Sessions and turns

11 

12A turn is one cycle of work within a session. A message sent to an idle session starts a new turn. A message sent during an active turn steers that turn.

13 

14Turns run asynchronously. Your application can follow progress through streaming or receive session state changes through [webhooks](https://developers.openai.com/api/docs/guides/agents-api/sessions/webhooks).

15 

16 

17 

18 

19## Start work

20 

21Create a session with an agent configuration and initial `input`. Set `stream` to `true` to receive events from the first turn in the same request.

22 

23With your [API key and SDK configured](https://developers.openai.com/api/docs/guides/agents-api/quickstart#prerequisites), run this example to create and run a script. OpenAI manages its environment:

24 

25Create a session and stream its first turn

26 

27```javascript

28import OpenAI from "openai";

29 

30const client = new OpenAI();

31const events = await client.beta.agents.sessions.create({

32 agent: {

33 model: "gpt-6-astra",

34 instructions: "Write clean code, run it, and report the actual output.",

35 },

36 environment: { type: "openai_hosted" },

37 input:

38 "Create tree.py, a Python script that prints a readable tree of the files in the current directory. Run it and show me the output.",

39 stream: true,

40});

41try {

42 for await (const event of events) {

43 console.log(JSON.stringify(event));

44 }

45} finally {

46 events.controller.abort();

47}

48```

49 

50```python

51from openai import OpenAI

52 

53with OpenAI() as client:

54 with client.beta.agents.sessions.create(

55 agent={

56 "model": "gpt-6-astra",

57 "instructions": "Write clean code, run it, and report the actual output.",

58 },

59 environment={"type": "openai_hosted"},

60 input="Create tree.py, a Python script that prints a readable tree of the files in the current directory. Run it and show me the output.",

61 stream=True,

62 ) as events:

63 for event in events:

64 print(event.to_json(indent=None), flush=True)

65```

66 

67```go

68import (

69 "context"

70 "fmt"

71 

72 "github.com/openai/openai-go/v3"

73)

74 

75ctx := context.Background()

76client := openai.NewClient()

77events := client.Beta.Agents.Sessions.NewStreaming(ctx, openai.BetaAgentSessionNewParams{

78 Agent: openai.BetaAgentSessionNewParamsAgent{

79 Model: openai.String("gpt-6-astra"),

80 Instructions: openai.String("Write clean code, run it, and report the actual output."),

81 },

82 Environment: openai.EnvironmentParamUnion{OfParamOpenAIHosted: &openai.EnvironmentParamOpenAIHosted{}},

83 Input: openai.BetaAgentSessionNewParamsInputUnion{

84 OfString: openai.String("Create tree.py, a Python script that prints a readable tree of the files in the current directory. Run it and show me the output."),

85 },

86})

87defer events.Close()

88if events.Err() != nil {

89 panic(events.Err())

90}

91for events.Next() {

92 event := events.Current()

93 fmt.Println(event.RawJSON())

94}

95if err := events.Err(); err != nil {

96 panic(err)

97}

98```

99 

100```java

101import com.fasterxml.jackson.databind.json.JsonMapper;

102import com.openai.client.OpenAIClient;

103import com.openai.client.okhttp.OpenAIOkHttpClient;

104import com.openai.core.http.StreamResponse;

105import com.openai.models.beta.agents.AgentSessionEvent;

106import com.openai.models.beta.agents.EnvironmentParam;

107import com.openai.models.beta.agents.sessions.SessionCreateParams;

108 

109OpenAIClient client = OpenAIOkHttpClient.fromEnv();

110var json = new JsonMapper();

111try (StreamResponse<AgentSessionEvent> events =

112 client

113 .beta()

114 .agents()

115 .sessions()

116 .createStreaming(

117 SessionCreateParams.builder()

118 .agent(

119 SessionCreateParams.Agent.builder()

120 .model("gpt-6-astra")

121 .instructions("Write clean code, run it, and report the actual output.")

122 .build())

123 .environment(EnvironmentParam.OpenAIHosted.builder().build())

124 .input(

125 "Create tree.py, a Python script that prints a readable tree of the files"

126 + " in the current directory. Run it and show me the output.")

127 .build())) {

128 var iterator = events.stream().iterator();

129 while (iterator.hasNext()) {

130 var event = iterator.next();

131 System.out.println(json.writeValueAsString(event));

132 }

133}

134```

135 

136```ruby

137require "openai"

138require "json"

139 

140client = OpenAI::Client.new

141events = client.beta.agents.sessions.create_streaming(

142 agent: {

143 model: "gpt-6-astra",

144 instructions: "Write clean code, run it, and report the actual output."

145 },

146 environment: { type: "openai_hosted" },

147 input: "Create tree.py, a Python script that prints a readable tree of the files in the current directory. Run it and show me the output."

148)

149begin

150 events.each do |event|

151 puts JSON.generate(event.to_h)

152 end

153ensure

154 events.close

155end

156```

157 

158```bash

159curl --no-buffer --fail-with-body https://api.openai.com/v1/agents/sessions \\\n -H "OpenAI-Beta: agents=v1" \\\n -H "Authorization: Bearer $OPENAI_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "agent": {\n "model": "gpt-6-astra",\n "instructions": "Write clean code, run it, and report the actual output."\n },\n "environment": { "type": "openai_hosted" },\n "input": "Create tree.py, a Python script that prints a readable tree of the files in the current directory. Run it and show me the output.",\n "stream": true\n }\'

160```

161 

162 

163Store the `session_id` with your application's conversation state. Use it to send follow-up messages and retrieve saved work for that conversation.

164 

165See [Configuring Agents](https://developers.openai.com/api/docs/guides/agents-api/configuration) for reusable agent settings and [Architecture](https://developers.openai.com/api/docs/guides/agents-api/architecture) for environment choices. Sessions with `environment.type: "none"` require initial input. The [Create session reference](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/methods/create) lists the request fields.

166 

167 

168 

169 

170## Follow progress and handle outcomes

171 

172Events report output and changes as the agent works. Check the turn's outcome: completion, failure, or cancellation. An idle session alone does not mean the turn succeeded.

173 

174Look for `agent.session.turn.completed`, `agent.session.turn.failed`, or `agent.session.turn.cancelled`. Inspect the agent's output too: a completed turn does not guarantee every tool succeeded.

175 

176If the session needs a function result or an environment connection, retrieve it and inspect `required_actions`. Your code must [handle the function call](https://developers.openai.com/api/docs/guides/agents-api/tools/functions) or [connect the environment](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted) so work can continue.

177 

178See [Events and Items](https://developers.openai.com/api/docs/guides/agents-api/sessions/events) for event types and payloads.

179 

180 

181 

182 

183 

184 

185## Continue or steer the work

186 

187Send another `agent.session.input.message` to the same session. If the agent is working, the message steers the active turn. If the session is idle, it starts a new turn with the existing conversation.

188 

189Use the conversation's session ID to send input. Subscribe to its [event stream](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/subresources/events/methods/stream) before sending the message so your application receives the turn's early events.

190 

191Pass your API client, session ID, and message to a function in your application:

192 

193Send a follow-up message

194 

195```javascript

196// Pass your saved session ID and message to this helper.

197async function sendMessage(client, sessionId, text) {

198 await client.beta.agents.sessions.events.create(sessionId, {

199 events: [

200 {

201 type: "agent.session.input.message",

202 input: [

203 {

204 role: "user",

205 content: [

206 {

207 type: "input_text",

208 text,

209 },

210 ],

211 },

212 ],

213 },

214 ],

215 });

216}

217```

218 

219```python

220# Pass your saved session ID and message to this helper.

221def send_message(client: OpenAI, session_id: str, text: str) -> None:

222 client.beta.agents.sessions.events.create(

223 session_id,

224 events=[

225 {

226 "type": "agent.session.input.message",

227 "input": [

228 {

229 "role": "user",

230 "content": [

231 {

232 "type": "input_text",

233 "text": text,

234 }

235 ],

236 }

237 ],

238 }

239 ],

240 )

241```

242 

243```go

244// Pass your saved session ID and message to this helper.

245func sendMessage(ctx context.Context, client *openai.Client, sessionID, text string) error {

246 return client.Beta.Agents.Sessions.Events.New(ctx,

247 sessionID,

248 openai.BetaAgentSessionEventNewParams{

249 Events: []openai.AgentSessionInputParamUnion{

250 {

251 OfParamAgentSessionInputMessage: &openai.AgentSessionInputParamAgentSessionInputMessage{

252 Input: []openai.AgentSessionInputMessageParam{

253 {

254 Content: []openai.InputContentParamUnion{

255 {

256 OfParamInputText: &openai.InputContentParamInputText{Text: text},

257 },

258 },

259 },

260 },

261 },

262 },

263 },

264 })

265}

266```

267 

268```java

269// Pass your saved session ID and message to this helper.

270public static void sendMessage(OpenAIClient client, String sessionId, String text) {

271 client

272 .beta()

273 .agents()

274 .sessions()

275 .events()

276 .create(

277 EventCreateParams.builder()

278 .sessionId(sessionId)

279 .addEvent(

280 AgentSessionInputParam.AgentSessionInputMessage.builder()

281 .addInput(

282 AgentSessionInputMessageParam.builder()

283 .addInputTextContent(text)

284 .build())

285 .build())

286 .build());

287}

288```

289 

290```ruby

291# Pass your saved session ID and message to this helper.

292def send_message(client, session_id, text)

293 client.beta.agents.sessions.events.create(

294 session_id,

295 events: [

296 {

297 type: "agent.session.input.message",

298 input: [

299 {

300 role: "user",

301 content: [

302 {

303 type: "input_text",

304 text: text

305 }

306 ]

307 }

308 ]

309 }

310 ]

311 )

312end

313```

314 

315```bash

316curl \

317 "https://api.openai.com/v1/agents/sessions/$session_id/events" \

318 -H "OpenAI-Beta: agents=v1" \

319 -H "Authorization: Bearer $OPENAI_API_KEY" \

320 -H "Content-Type: application/json" \

321 -d '{

322 "events": [

323 {

324 "type": "agent.session.input.message",

325 "input": [

326 {

327 "role": "user",

328 "content": [

329 {

330 "type": "input_text",

331 "text": "List the files in the current directory."

332 }

333 ]

334 }

335 ]

336 }

337 ]

338 }'

339```

340 

341 

342For a combined send-and-stream example, see [Events and Items](https://developers.openai.com/api/docs/guides/agents-api/sessions/events#send-and-stream-a-task).

343 

344 

345 

346 

347 

348 

349## Retrieve saved work

350 

351Events show live progress. Items are the saved messages and tool calls, including completed responses. Retrieve them to display previous work or inspect results after a turn ends:

352 

353Retrieve session items

354 

355```javascript

356// Pass your saved session ID to this helper.

357async function listItems(client, sessionId) {

358 return client.beta.agents.sessions.items.list(sessionId, {

359 order: "asc",

360 limit: 100,

361 });

362}

363```

364 

365```python

366# Pass your saved session ID to this helper.

367def list_items(client: OpenAI, session_id: str):

368 return client.beta.agents.sessions.items.list(session_id, order="asc", limit=100)

369```

370 

371```go

372// Pass your saved session ID to this helper.

373func listItems(ctx context.Context, client *openai.Client, sessionID string) (*pagination.CursorPage[openai.AgentSessionItemUnion], error) {

374 return client.Beta.Agents.Sessions.Items.List(ctx,

375 sessionID,

376 openai.BetaAgentSessionItemListParams{

377 Order: "asc",

378 Limit: openai.Int(100),

379 })

380}

381```

382 

383```java

384// Pass your saved session ID to this helper.

385public static ItemListPage listItems(OpenAIClient client, String sessionId) {

386 return client

387 .beta()

388 .agents()

389 .sessions()

390 .items()

391 .list(

392 ItemListParams.builder()

393 .sessionId(sessionId)

394 .order(ItemListParams.Order.of("asc"))

395 .limit(100L)

396 .build());

397}

398```

399 

400```ruby

401# Pass your saved session ID to this helper.

402def list_items(client, session_id)

403 client.beta.agents.sessions.items.list(

404 session_id,

405 order: "asc",

406 limit: 100

407 )

408end

409```

410 

411```bash

412curl \

413 "https://api.openai.com/v1/agents/sessions/$session_id/items?order=asc&limit=100" \

414 -H "OpenAI-Beta: agents=v1" \

415 -H "Authorization: Bearer $OPENAI_API_KEY"

416```

417 

418 

419See [Managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage) to inspect session state and turn outcomes. Retrieve files through [Files and artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files).

420 

421 

422 

423 

424Streams do not replay missed events. After a disconnect, retrieve the session and its saved items to recover the work. See [Recover a disconnected stream](https://developers.openai.com/api/docs/guides/agents-api/sessions/events#how-to-recover-a-disconnected-stream) for the reconnection procedure.

425 

426## Cancel an active turn

427 

428Cancel the current turn when you want the agent to stop. The session and its previous work remain available:

429 

430Cancel the active turn

431 

432```javascript

433// Pass your saved session ID to this helper.

434async function cancelTurn(client, sessionId) {

435 await client.beta.agents.sessions.events.create(sessionId, {

436 events: [{ type: "agent.session.input.cancel" }],

437 });

438}

439```

440 

441```python

442# Pass your saved session ID to this helper.

443def cancel_turn(client: OpenAI, session_id: str) -> None:

444 client.beta.agents.sessions.events.create(

445 session_id, events=[{"type": "agent.session.input.cancel"}]

446 )

447```

448 

449```go

450// Pass your saved session ID to this helper.

451func cancelTurn(ctx context.Context, client *openai.Client, sessionID string) error {

452 return client.Beta.Agents.Sessions.Events.New(ctx,

453 sessionID,

454 openai.BetaAgentSessionEventNewParams{

455 Events: []openai.AgentSessionInputParamUnion{

456 {OfParamAgentSessionInputCancel: &openai.AgentSessionInputParamAgentSessionInputCancel{}},

457 },

458 })

459}

460```

461 

462```java

463// Pass your saved session ID to this helper.

464public static void cancelTurn(OpenAIClient client, String sessionId) {

465 client

466 .beta()

467 .agents()

468 .sessions()

469 .events()

470 .create(

471 EventCreateParams.builder()

472 .sessionId(sessionId)

473 .addEventAgentSessionInputCancel()

474 .build());

475}

476```

477 

478```ruby

479# Pass your saved session ID to this helper.

480def cancel_turn(client, session_id)

481 client.beta.agents.sessions.events.create(

482 session_id,

483 events: [{ type: "agent.session.input.cancel" }]

484 )

485end

486```

487 

488```bash

489curl "https://api.openai.com/v1/agents/sessions/$session_id/events" \

490 -H "OpenAI-Beta: agents=v1" \

491 -H "Authorization: Bearer $OPENAI_API_KEY" \

492 -H "Content-Type: application/json" \

493 -d '{"events":[{"type":"agent.session.input.cancel"}]}'

494```

Details

1# Events and items

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5Events report what happens as an agent works. Items are the saved messages and tool calls you can retrieve later. Use events to update your application in real time and items to display its saved history.

6 

7 

8 

9 

10 

11 

12Your application sends input events to submit messages, cancel turns, or return tool results. The agent sends events that report output and changes to the session. See [Run and continue sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions) for sending input.

13 

14 

15 

16 

17## Consume a stream

18 

19Subscribe before sending work so your application receives the turn's early events. Pass your API client, the conversation's session ID, and an event handler:

20 

21Stream session events

22 

23```javascript

24// Pass your saved session ID to this helper.

25async function streamSession(client, sessionId, handleEvent) {

26 const events = await client.beta.agents.sessions.events.stream(sessionId);

27 try {

28 for await (const event of events) {

29 await handleEvent(event);

30 switch (event.type) {

31 case "agent.session.idle":

32 continue;

33 case "error":

34 throw new Error(event.error.message);

35 case "agent.session.failed":

36 case "agent.session.environment.failed":

37 throw new Error(`Agent lifecycle failure: ${event.type}`);

38 case "agent.session.turn.failed":

39 if (event.turn.subagent_id === null) {

40 throw new Error(

41 `${event.type}: ${event.turn.error?.message ?? ""}`

42 );

43 }

44 break;

45 case "agent.session.turn.cancelled":

46 if (event.turn.subagent_id === null) {

47 throw new Error("The agent turn was cancelled");

48 }

49 break;

50 case "agent.session.turn.completed":

51 if (event.turn.subagent_id === null) return;

52 break;

53 }

54 }

55 throw new Error(

56 "Stream closed before a turn ended. Retrieve the saved state."

57 );

58 } finally {

59 events.controller.abort();

60 }

61}

62```

63 

64```python

65# Pass your saved session ID to this helper.

66def stream_session(client: OpenAI, session_id: str, handle_event):

67 with client.beta.agents.sessions.events.stream(session_id) as events:

68 for event in events:

69 handle_event(event)

70 match event.type:

71 case "agent.session.idle":

72 continue

73 case "error":

74 raise RuntimeError(event.error.message)

75 case "agent.session.failed" | "agent.session.environment.failed":

76 raise RuntimeError(f"Agent lifecycle failure: {event.type}")

77 case "agent.session.turn.failed":

78 if event.turn.subagent_id is None:

79 detail = event.turn.error.message if event.turn.error else ""

80 raise RuntimeError(f"{event.type}: {detail}")

81 case "agent.session.turn.cancelled":

82 if event.turn.subagent_id is None:

83 raise RuntimeError("The agent turn was cancelled")

84 case "agent.session.turn.completed":

85 if event.turn.subagent_id is None:

86 return

87 raise RuntimeError("Stream closed before a turn ended. Retrieve the saved state.")

88```

89 

90```go

91// Pass your saved session ID to this helper.

92func streamSession(ctx context.Context, client *openai.Client, sessionID string, handleEvent func(openai.AgentSessionEventUnion)) error {

93 events := client.Beta.Agents.Sessions.Events.StreamStreaming(ctx, sessionID)

94 defer events.Close()

95 for events.Next() {

96 event := events.Current()

97 handleEvent(event)

98 switch event.Type {

99 case "agent.session.idle":

100 continue

101 case "error":

102 return fmt.Errorf("agent error: %s", event.RawJSON())

103 case "agent.session.failed", "agent.session.environment.failed":

104 return fmt.Errorf("agent lifecycle failure: %s", event.RawJSON())

105 case "agent.session.turn.failed", "agent.session.turn.cancelled":

106 if event.Turn.SubagentID == "" {

107 return fmt.Errorf("agent turn did not complete: %s", event.RawJSON())

108 }

109 case "agent.session.turn.completed":

110 if event.Turn.SubagentID == "" {

111 return nil

112 }

113 }

114 }

115 if err := events.Err(); err != nil {

116 return err

117 }

118 return fmt.Errorf("stream closed before a turn ended; retrieve the saved state")

119}

120```

121 

122```java

123// Pass your saved session ID to this helper.

124public static void streamSession(

125 OpenAIClient client, String sessionId, Consumer<AgentSessionEvent> handleEvent) {

126 try (StreamResponse<AgentSessionEvent> events =

127 client.beta().agents().sessions().events().streamStreaming(sessionId)) {

128 var iterator = events.stream().iterator();

129 while (iterator.hasNext()) {

130 var event = iterator.next();

131 handleEvent.accept(event);

132 if (event.idle().isPresent()) {

133 continue;

134 }

135 if (event.error().isPresent()) {

136 throw new IllegalStateException("Agent error: " + event);

137 }

138 if (event.failed().isPresent() || event.environmentFailed().isPresent()) {

139 throw new IllegalStateException("Agent lifecycle failure: " + event);

140 }

141 if (event.turnFailed().filter(e -> e.turn().subagentId().isEmpty()).isPresent()

142 || event.turnCancelled().filter(e -> e.turn().subagentId().isEmpty()).isPresent()) {

143 throw new IllegalStateException("Agent turn did not complete: " + event);

144 }

145 if (event.turnCompleted().filter(e -> e.turn().subagentId().isEmpty()).isPresent()) {

146 return;

147 }

148 }

149 throw new IllegalStateException(

150 "Stream closed before a turn ended. Retrieve the saved state.");

151 }

152}

153```

154 

155```ruby

156# Pass your saved session ID to this helper.

157def stream_session(client, session_id, &handle_event)

158 events = client.beta.agents.sessions.events.stream_streaming(session_id)

159 begin

160 events.each do |event|

161 handle_event.call(event)

162 case event.type.to_s

163 when "agent.session.idle"

164 next

165 when "error"

166 raise event.error.message

167 when "agent.session.failed", "agent.session.environment.failed"

168 raise "Agent lifecycle failure: #{event.type}"

169 when "agent.session.turn.failed"

170 raise "#{event.type}: #{event.turn.error&.message}" if event.turn.subagent_id.nil?

171 when "agent.session.turn.cancelled"

172 raise "The agent turn was cancelled" if event.turn.subagent_id.nil?

173 when "agent.session.turn.completed"

174 return nil if event.turn.subagent_id.nil?

175 end

176 end

177 raise "Stream closed before a turn ended. Retrieve the saved state."

178 ensure

179 events.close

180 end

181end

182```

183 

184```bash

185curl -N \

186 "https://api.openai.com/v1/agents/sessions/$session_id/events?stream=true" \

187 -H "OpenAI-Beta: agents=v1" \

188 -H "Authorization: Bearer $OPENAI_API_KEY" \

189 -H "Accept: text/event-stream"

190```

191 

192 

193The helper passes each event to your handler, then checks common event types. It continues on `agent.session.idle` and returns when the root turn completes. It raises an error if the root turn fails or is cancelled, the session or environment fails, or an `error` event arrives. Subagent turn events do not end the stream. Your handler decides how to display output; the caller handles errors from the helper. If the stream closes before a turn ends, the helper raises an error. See [Recover a disconnected stream](#how-to-recover-a-disconnected-stream).

194 

195 

196 

197 

198<details>

199<summary>Send a message after subscribing</summary>

200 

201This version accepts a message and submits it after opening the stream:

202 

203Send and stream a message

204 

205```javascript

206// Pass your saved session ID and message to this helper.

207async function sendAndStream(client, sessionId, text, handleEvent) {

208 const events = await client.beta.agents.sessions.events.stream(sessionId);

209 try {

210 await client.beta.agents.sessions.events.create(sessionId, {

211 events: [

212 {

213 type: "agent.session.input.message",

214 input: [{ role: "user", content: [{ type: "input_text", text }] }],

215 },

216 ],

217 });

218 for await (const event of events) {

219 await handleEvent(event);

220 switch (event.type) {

221 case "agent.session.idle":

222 continue;

223 case "error":

224 throw new Error(event.error.message);

225 case "agent.session.failed":

226 case "agent.session.environment.failed":

227 throw new Error(`Agent lifecycle failure: ${event.type}`);

228 case "agent.session.turn.failed":

229 if (event.turn.subagent_id === null) {

230 throw new Error(

231 `${event.type}: ${event.turn.error?.message ?? ""}`

232 );

233 }

234 break;

235 case "agent.session.turn.cancelled":

236 if (event.turn.subagent_id === null) {

237 throw new Error("The agent turn was cancelled");

238 }

239 break;

240 case "agent.session.turn.completed":

241 if (event.turn.subagent_id === null) return;

242 break;

243 }

244 }

245 throw new Error(

246 "Stream closed before a turn ended. Retrieve the saved state."

247 );

248 } finally {

249 events.controller.abort();

250 }

251}

252```

253 

254```python

255# Pass your saved session ID and message to this helper.

256def send_and_stream(client: OpenAI, session_id: str, text, handle_event):

257 with client.beta.agents.sessions.events.stream(session_id) as events:

258 client.beta.agents.sessions.events.create(

259 session_id,

260 events=[

261 {

262 "type": "agent.session.input.message",

263 "input": [

264 {

265 "role": "user",

266 "content": [{"type": "input_text", "text": text}],

267 }

268 ],

269 }

270 ],

271 )

272 for event in events:

273 handle_event(event)

274 match event.type:

275 case "agent.session.idle":

276 continue

277 case "error":

278 raise RuntimeError(event.error.message)

279 case "agent.session.failed" | "agent.session.environment.failed":

280 raise RuntimeError(f"Agent lifecycle failure: {event.type}")

281 case "agent.session.turn.failed":

282 if event.turn.subagent_id is None:

283 detail = event.turn.error.message if event.turn.error else ""

284 raise RuntimeError(f"{event.type}: {detail}")

285 case "agent.session.turn.cancelled":

286 if event.turn.subagent_id is None:

287 raise RuntimeError("The agent turn was cancelled")

288 case "agent.session.turn.completed":

289 if event.turn.subagent_id is None:

290 return

291 raise RuntimeError("Stream closed before a turn ended. Retrieve the saved state.")

292```

293 

294```go

295// Pass your saved session ID and message to this helper.

296func sendAndStream(ctx context.Context, client *openai.Client, sessionID string, text string, handleEvent func(openai.AgentSessionEventUnion)) error {

297 events := client.Beta.Agents.Sessions.Events.StreamStreaming(ctx, sessionID)

298 defer events.Close()

299 if err := events.Err(); err != nil {

300 return err

301 }

302 err := client.Beta.Agents.Sessions.Events.New(ctx,

303 sessionID,

304 openai.BetaAgentSessionEventNewParams{

305 Events: []openai.AgentSessionInputParamUnion{

306 {

307 OfParamAgentSessionInputMessage: &openai.AgentSessionInputParamAgentSessionInputMessage{

308 Input: []openai.AgentSessionInputMessageParam{

309 {

310 Content: []openai.InputContentParamUnion{

311 {

312 OfParamInputText: &openai.InputContentParamInputText{

313 Text: text,

314 },

315 },

316 },

317 },

318 },

319 },

320 },

321 },

322 })

323 if err != nil {

324 return err

325 }

326 for events.Next() {

327 event := events.Current()

328 handleEvent(event)

329 switch event.Type {

330 case "agent.session.idle":

331 continue

332 case "error":

333 return fmt.Errorf("agent error: %s", event.RawJSON())

334 case "agent.session.failed", "agent.session.environment.failed":

335 return fmt.Errorf("agent lifecycle failure: %s", event.RawJSON())

336 case "agent.session.turn.failed", "agent.session.turn.cancelled":

337 if event.Turn.SubagentID == "" {

338 return fmt.Errorf("agent turn did not complete: %s", event.RawJSON())

339 }

340 case "agent.session.turn.completed":

341 if event.Turn.SubagentID == "" {

342 return nil

343 }

344 }

345 }

346 if err := events.Err(); err != nil {

347 return err

348 }

349 return fmt.Errorf("stream closed before a turn ended; retrieve the saved state")

350}

351```

352 

353```java

354// Pass your saved session ID and message to this helper.

355public static void sendAndStream(

356 OpenAIClient client, String sessionId, String text, Consumer<AgentSessionEvent> handleEvent) {

357 try (StreamResponse<AgentSessionEvent> events =

358 client.beta().agents().sessions().events().streamStreaming(sessionId)) {

359 client

360 .beta()

361 .agents()

362 .sessions()

363 .events()

364 .create(

365 EventCreateParams.builder()

366 .sessionId(sessionId)

367 .addEvent(

368 AgentSessionInputParam.AgentSessionInputMessage.builder()

369 .addInput(

370 AgentSessionInputMessageParam.builder()

371 .addInputTextContent(text)

372 .build())

373 .build())

374 .build());

375 var iterator = events.stream().iterator();

376 while (iterator.hasNext()) {

377 var event = iterator.next();

378 handleEvent.accept(event);

379 if (event.idle().isPresent()) {

380 continue;

381 }

382 if (event.error().isPresent()) {

383 throw new IllegalStateException("Agent error: " + event);

384 }

385 if (event.failed().isPresent() || event.environmentFailed().isPresent()) {

386 throw new IllegalStateException("Agent lifecycle failure: " + event);

387 }

388 if (event.turnFailed().filter(e -> e.turn().subagentId().isEmpty()).isPresent()

389 || event.turnCancelled().filter(e -> e.turn().subagentId().isEmpty()).isPresent()) {

390 throw new IllegalStateException("Agent turn did not complete: " + event);

391 }

392 if (event.turnCompleted().filter(e -> e.turn().subagentId().isEmpty()).isPresent()) {

393 return;

394 }

395 }

396 throw new IllegalStateException(

397 "Stream closed before a turn ended. Retrieve the saved state.");

398 }

399}

400```

401 

402```ruby

403# Pass your saved session ID and message to this helper.

404def send_and_stream(client, session_id, text, &handle_event)

405 events = client.beta.agents.sessions.events.stream_streaming(session_id)

406 begin

407 client.beta.agents.sessions.events.create(

408 session_id,

409 events: [

410 {

411 type: "agent.session.input.message",

412 input: [

413 {

414 role: "user",

415 content: [

416 {

417 type: "input_text",

418 text: text

419 }

420 ]

421 }

422 ]

423 }

424 ]

425 )

426 events.each do |event|

427 handle_event.call(event)

428 case event.type.to_s

429 when "agent.session.idle"

430 next

431 when "error"

432 raise event.error.message

433 when "agent.session.failed", "agent.session.environment.failed"

434 raise "Agent lifecycle failure: #{event.type}"

435 when "agent.session.turn.failed"

436 raise "#{event.type}: #{event.turn.error&.message}" if event.turn.subagent_id.nil?

437 when "agent.session.turn.cancelled"

438 raise "The agent turn was cancelled" if event.turn.subagent_id.nil?

439 when "agent.session.turn.completed"

440 return nil if event.turn.subagent_id.nil?

441 end

442 end

443 raise "Stream closed before a turn ended. Retrieve the saved state."

444 ensure

445 events.close

446 end

447end

448```

449 

450 

451</details>

452 

453 

454 

455 

456 

457 

458 

459 

460## Handle updates

461 

462Use the event's `type` to decide what your application should do:

463 

464- **Display text:** Append `agent.session.turn.output_text.delta` to the relevant content part. When `agent.session.turn.output_text.done` arrives, replace that part with its complete text. Deltas may be absent.

465- **Track work:** Session, turn, and item events report progress. Check for `agent.session.turn.completed`, `agent.session.turn.failed`, or `agent.session.turn.cancelled` to determine the turn's outcome.

466- **Provide required input:** On `agent.session.requires_action`, retrieve the session and inspect `required_actions`. Your code may need to return a function result or connect an environment.

467 

468An idle session or a closed stream alone does not establish success. A completed turn also does not guarantee that every tool succeeded. Inspect the agent's output.

469 

470Use `item_id`, `output_index`, and `content_index` to connect text updates to the same content part. For example, these abbreviated events update one part:

471 

472```json

473{

474 "type": "agent.session.turn.output_text.delta",

475 "item_id": "msg_789",

476 "output_index": 0,

477 "content_index": 0,

478 "delta": "Acme competes"

479}

480```

481 

482```json

483{

484 "type": "agent.session.turn.output_text.done",

485 "item_id": "msg_789",

486 "output_index": 0,

487 "content_index": 0,

488 "text": "Acme competes on price and distribution."

489}

490```

491 

492Each event has its own `event_id`. The shared `item_id` identifies the saved item, which includes the message's content, status, and phase. See [Retrieve saved work](https://developers.openai.com/api/docs/guides/agents-api/sessions#retrieve-session-items).

493 

494See the [streaming events reference](https://developers.openai.com/api/reference/resources/beta/subresources/agents/streaming-events) for all event types and fields. These stream events are distinct from [webhooks](https://developers.openai.com/api/docs/guides/agents-api/sessions/webhooks). For subagent activity and command attribution, see [Observe delegation](https://developers.openai.com/api/docs/guides/agents-api/multi-agent#observe-delegation).

495 

496## Fetch items and turns

497 

498Use the session ID from your application's conversation state to retrieve saved work:

499 

500- **Session items:** [List items](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/subresources/items/methods/list) to retrieve the root agent's messages and tool calls across turns.

501- **Turns:** [List turns](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/subresources/turns/methods/list) to browse the session's work. [Retrieve a turn](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/subresources/turns/methods/retrieve) by ID to inspect its status, timestamps, usage, and error.

502- **Items from one turn:** For a root-agent turn, filter session items by `turn_id`. Each subagent has its own [item history](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/subresources/subagents/subresources/items/methods/list) and a [per-turn items endpoint](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/subresources/subagents/subresources/turns/subresources/items/methods/list).

503 

504List endpoints return one page at a time. Use SDK pagination helpers or the `after` cursor to retrieve more results. A single page may not contain every item for a turn. Use `order: "asc"` to read items from oldest to newest.

505 

506## How to recover a disconnected stream

507 

508Streams do not replay missed events. To restore your application's view:

509 

5101. Open a new stream and buffer incoming events.

5112. Retrieve the session and its saved items while the stream stays connected.

5123. Restore your local state from those items, keyed by item ID.

5134. Apply buffered item updates using `item_id`. Discard updates for items that already reached their final state in the retrieved history.

5145. Resume handling live events.

515 

516An `output_text.done` event can replace a temporary text buffer with the complete text. Saved items let you recover completed work, but not every intermediate event you missed.

Details

1# Manage sessions

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5Store each session ID with your application's data store. Use it to retrieve the session's current state, handle requests from the agent, or delete the session.

6 

7 

8 

9 

10## Find sessions

11 

12List sessions in your project to browse previous work. SDK pagination helpers retrieve additional pages:

13 

14List sessions and retrieve the next page

15 

16```javascript

17import OpenAI from "openai";

18 

19const client = new OpenAI();

20let page = await client.beta.agents.sessions.list({ limit: 20 });

21console.log(page.data);

22if (page.hasNextPage()) {

23 page = await page.getNextPage();

24 console.log(page.data);

25}

26```

27 

28```python

29from openai import OpenAI

30 

31client = OpenAI()

32page = client.beta.agents.sessions.list(limit=20)

33print(page.to_json())

34if page.has_next_page():

35 page = page.get_next_page()

36 print(page.to_json())

37```

38 

39```go

40import (

41 "context"

42 "fmt"

43 

44 "github.com/openai/openai-go/v3"

45)

46 

47ctx := context.Background()

48client := openai.NewClient()

49result, err := client.Beta.Agents.Sessions.List(ctx,

50 openai.BetaAgentSessionListParams{Limit: openai.Int(20)})

51if err != nil {

52 panic(err)

53}

54fmt.Println(result.Data)

55if result.HasMore {

56 result, err = result.GetNextPage()

57 if err != nil {

58 panic(err)

59 }

60 fmt.Println(result.Data)

61}

62```

63 

64```java

65import com.openai.client.OpenAIClient;

66import com.openai.client.okhttp.OpenAIOkHttpClient;

67import com.openai.models.beta.agents.sessions.SessionListParams;

68 

69OpenAIClient client = OpenAIOkHttpClient.fromEnv();

70var result =

71 client.beta().agents().sessions().list(SessionListParams.builder().limit(20L).build());

72System.out.println(result.items());

73if (result.hasNextPage()) {

74 result = result.nextPage();

75 System.out.println(result.items());

76}

77```

78 

79```ruby

80require "openai"

81 

82client = OpenAI::Client.new

83result = client.beta.agents.sessions.list(limit: 20)

84puts result.data

85if result.next_page?

86 result = result.next_page

87 puts result.data

88end

89```

90 

91```bash

92page=$(curl -sS --fail-with-body "https://api.openai.com/v1/agents/sessions?limit=20&order=desc" \

93 -H "OpenAI-Beta: agents=v1" \

94 -H "Authorization: Bearer $OPENAI_API_KEY")

95after=$(printf '%s' "$page" | jq -r 'select(.has_more) | .last_id // empty')

96 

97if [ -n "$after" ]; then

98 curl --get "https://api.openai.com/v1/agents/sessions" \

99 -H "OpenAI-Beta: agents=v1" \

100 -H "Authorization: Bearer $OPENAI_API_KEY" \

101 --data-urlencode "after=$after" \

102 --data-urlencode "limit=20"

103fi

104```

105 

106 

107 

108 

109 

110## Inspect a session

111 

112Retrieve a session to read its status, agent configuration, environment, and `required_actions`. Pass your API client and the conversation's session ID:

113 

114Retrieve a session

115 

116```javascript

117// Pass your saved session ID to this helper.

118async function retrieveSession(client, sessionId) {

119 return client.beta.agents.sessions.retrieve(sessionId);

120}

121```

122 

123```python

124# Pass your saved session ID to this helper.

125def retrieve_session(client: OpenAI, session_id: str):

126 return client.beta.agents.sessions.retrieve(session_id)

127```

128 

129```go

130// Pass your saved session ID to this helper.

131func retrieveSession(ctx context.Context, client *openai.Client, sessionID string) (*openai.AgentSession, error) {

132 return client.Beta.Agents.Sessions.Get(ctx, sessionID)

133}

134```

135 

136```java

137// Pass your saved session ID to this helper.

138public static AgentSession retrieveSession(OpenAIClient client, String sessionId) {

139 return client

140 .beta()

141 .agents()

142 .sessions()

143 .retrieve(SessionRetrieveParams.builder().sessionId(sessionId).build());

144}

145```

146 

147```ruby

148# Pass your saved session ID to this helper.

149def retrieve_session(client, session_id)

150 client.beta.agents.sessions.retrieve(session_id)

151end

152```

153 

154```bash

155curl \

156 "https://api.openai.com/v1/agents/sessions/$session_id" \

157 -H "OpenAI-Beta: agents=v1" \

158 -H "Authorization: Bearer $OPENAI_API_KEY"

159```

160 

161 

162See the [Retrieve session reference](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/methods/retrieve) for the full response schema.

163 

164### Handle required actions

165 

166A session with status `requires_action` needs your application to act before work can continue. When you receive `agent.session.requires_action`, retrieve the session and inspect each entry in `required_actions`:

167 

168- **`function_call`:** Run the function identified by `name` with its `arguments`. Return the result on the same session using the action's `turn_id` and `call_id`. See [Function tools](https://developers.openai.com/api/docs/guides/agents-api/tools/functions#return-the-result).

169- **`environment_connection`:** Connect the environment identified by `environment_id`. See [Connect an environment](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted).

170 

171The event tells your application when to check. The retrieved session tells it what to do. After a restart or stream disconnect, retrieve the session to find pending actions. After handling them, continue following events for the turn's outcome.

172 

173 

174 

175 

176For saved messages, tool calls, and turn outcomes, see [Fetch items and turns](https://developers.openai.com/api/docs/guides/agents-api/sessions/events#fetch-items-and-turns). To identify which agent ran a command, see [Observe delegation](https://developers.openai.com/api/docs/guides/agents-api/multi-agent#observe-delegation).

177 

178## Delete a session

179 

180Delete a session when your application no longer needs it. Deletion removes the session from the API. Physical cleanup may continue asynchronously.

181 

182Delete a session

183 

184```javascript

185// Replace the illustrative IDs and URLs below with your own resource values.

186import OpenAI from "openai";

187 

188/**

189 * @param {OpenAI} client

190 * @param {string} sessionId

191 */

192async function deleteSession(client, sessionId) {

193 return client.beta.agents.sessions.delete(sessionId);

194}

195 

196const result = await deleteSession(new OpenAI(), "sess_123");

197console.log(result);

198```

199 

200```python

201# Replace the illustrative IDs and URLs below with your own resource values.

202 

203from openai import OpenAI

204 

205 

206def delete_session(client: OpenAI, session_id: str):

207 return client.beta.agents.sessions.delete(session_id)

208 

209 

210if __name__ == "__main__":

211 result = delete_session(OpenAI(), "sess_123")

212 print(result.to_json())

213```

214 

215```go

216// Replace the illustrative IDs and URLs below with your own resource values.

217package main

218 

219import (

220 "context"

221 "fmt"

222 

223 "github.com/openai/openai-go/v3"

224)

225 

226func deleteSession(ctx context.Context, client *openai.Client, sessionID string) (*openai.AgentSessionDeleted, error) {

227 return client.Beta.Agents.Sessions.Delete(ctx, sessionID)

228}

229 

230func main() {

231 client := openai.NewClient()

232 result, err := deleteSession(context.Background(), &client, "sess_123")

233 if err != nil {

234 panic(err)

235 }

236 fmt.Println(result)

237}

238```

239 

240```java

241// Replace the illustrative IDs and URLs below with your own resource values.

242import com.openai.client.OpenAIClient;

243import com.openai.client.okhttp.OpenAIOkHttpClient;

244import com.openai.models.beta.agents.AgentSessionDeleted;

245import com.openai.models.beta.agents.sessions.SessionDeleteParams;

246 

247public final class AgentsApiSessionsDeleteSessionExample {

248 public static AgentSessionDeleted deleteSession(OpenAIClient client, String sessionId) {

249 return client

250 .beta()

251 .agents()

252 .sessions()

253 .delete(SessionDeleteParams.builder().sessionId(sessionId).build());

254 }

255 

256 public static void main(String[] args) {

257 var result = deleteSession(OpenAIOkHttpClient.fromEnv(), "sess_123");

258 System.out.println(result);

259 }

260}

261```

262 

263```ruby

264# Replace the illustrative IDs and URLs below with your own resource values.

265require "openai"

266 

267def delete_session(client, session_id)

268 client.beta.agents.sessions.delete(session_id)

269end

270 

271puts delete_session(OpenAI::Client.new, "sess_123")

272```

273 

274```bash

275curl -X DELETE \

276 "https://api.openai.com/v1/agents/sessions/$session_id" \

277 -H "OpenAI-Beta: agents=v1" \

278 -H "Authorization: Bearer $OPENAI_API_KEY"

279```

280 

281 

282To stop current work and keep the conversation, [cancel the active turn](https://developers.openai.com/api/docs/guides/agents-api/sessions#cancel-an-active-turn). See the [Delete session reference](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/methods/delete) for the deletion response.

Details

1# Session webhooks

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5Use webhooks to respond to session state changes without keeping an event stream open. A webhook handler can [start or reconnect sandbox compute](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle#set-up-webhook-managed-sandboxes), update your application, or trigger a workflow.

6 

7## Supported events

8 

9| Event | When it fires |

10| ------------------------------- | ------------------------------------------------------------------------------------- |

11| `agent.session.created` | A session is created. |

12| `agent.session.action_required` | The session needs a function result, initial environment connection, or reconnection. |

13| `agent.session.in_progress` | The session starts processing a turn. |

14| `agent.session.idle` | The session is idle and ready for more input. |

15| `agent.session.failed` | The session enters a failed state. |

16 

17An `agent.session.action_required` event includes the session ID and a

18`required_action.type` of `function_call` or `environment_connection`.

19 

20```json

21{

22 "type": "agent.session.action_required",

23 "data": {

24 "id": "sess_abc123",

25 "required_action": { "type": "function_call" }

26 }

27}

28```

29 

30Retrieve the session and inspect `required_actions` for call IDs, arguments, or

31environment IDs. The webhook does not include those details.

32 

33## Set up a webhook

34 

35Follow the shared [webhook setup guide](https://developers.openai.com/api/docs/guides/webhooks#creating-webhook-endpoints) to create an endpoint and select Agents API events. Store the endpoint's signing secret for [signature verification](https://developers.openai.com/api/docs/guides/webhooks#verifying-webhook-signatures).

36 

37## Receive events

38 

39OpenAI sends a signed HTTP POST request whenever a subscribed event occurs:

40 

41```json

42{

43 "id": "evt_123",

44 "object": "event",

45 "created_at": 1750287018,

46 "type": "agent.session.created",

47 "data": {

48 "id": "sess_abc123",

49 "environment_id": "ccarenv_abc123",

50 "environment_type": "self_hosted",

51 "connect": {

52 "remote_url": "https://api.openai.com/v1/agents/api"

53 }

54 }

55}

56```

57 

58 

59 

60 

61Retrieve the session's current state before provisioning a sandbox. See [Sandbox lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle#handle-lifecycle-webhooks).

62 

63### Start the executor

64 

65For self-hosted sessions, `agent.session.created` includes the environment ID and connection URL needed to start an executor. Set `ENVIRONMENT_ID` to `data.environment_id` and `REMOTE_URL` to `data.connect.remote_url`. This is the same URL returned as `environment.remote_url` on the session. Save both values and reuse them on reconnect:

66 

67```bash

68CODEX_API_KEY="$OPENAI_ENVIRONMENT_KEY" \

69codex exec-server \

70 --remote "$REMOTE_URL" \

71 --environment-id "$ENVIRONMENT_ID"

72```

73 

74Use an [environment key](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#authentication) as `CODEX_API_KEY`. Keep your application API key outside the environment.

75 

76## Verify and process events

77 

78Set `OPENAI_API_KEY` and `OPENAI_WEBHOOK_SECRET`. For Python, install `fastapi`, `uvicorn`, and `openai`. For JavaScript, install `express` and `openai`.

79 

80The handlers verify signatures and listen on port 8000. Set `PORT` to change the port. In production, [queue slower work](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle#handle-lifecycle-webhooks).

81 

82Webhook handler

83 

84```javascript

85import express from "express";

86import OpenAI from "openai";

87 

88const app = express();

89const webhooks = new OpenAI({

90 webhookSecret: process.env.OPENAI_WEBHOOK_SECRET,

91});

92 

93app.post(

94 "/webhooks/openai",

95 express.raw({ type: "application/json" }),

96 async (request, response) => {

97 const payload = request.body.toString("utf8");

98 try {

99 await webhooks.webhooks.verifySignature(payload, request.headers);

100 } catch {

101 response.status(400).send("Invalid signature");

102 return;

103 }

104 const event = JSON.parse(payload);

105 if (event.type === "agent.session.idle") {

106 const session = await webhooks.beta.agents.sessions.retrieve(

107 event.data.id

108 );

109 console.log("session idle event:", session.id);

110 } else {

111 console.log("session event:", event.type, event.data.id);

112 }

113 response.sendStatus(200);

114 }

115);

116 

117app.listen(Number(process.env.PORT ?? 8000));

118```

119 

120```python

121import json

122import os

123 

124import uvicorn

125from fastapi import FastAPI, Request, Response

126from openai import AsyncOpenAI, InvalidWebhookSignatureError

127 

128app = FastAPI()

129webhooks = AsyncOpenAI(webhook_secret=os.environ["OPENAI_WEBHOOK_SECRET"])

130 

131 

132@app.post("/webhooks/openai")

133async def handle_webhook(request: Request):

134 payload = await request.body()

135 try:

136 webhooks.webhooks.verify_signature(payload=payload, headers=request.headers)

137 except (InvalidWebhookSignatureError, ValueError):

138 return Response("Invalid signature", status_code=400)

139 

140 event = json.loads(payload)

141 if event["type"] == "agent.session.idle":

142 session_id = event["data"]["id"]

143 session = await webhooks.beta.agents.sessions.retrieve(session_id, timeout=10)

144 print("session idle event:", session.id)

145 else:

146 print("session event:", event["type"], event["data"]["id"])

147 return Response(status_code=200)

148 

149 

150if __name__ == "__main__":

151 uvicorn.run(app, port=int(os.environ.get("PORT", "8000")))

152```

153 

154```go

155import (

156 "encoding/json"

157 "fmt"

158 "io"

159 "net/http"

160 "os"

161 

162 "github.com/openai/openai-go/v3"

163)

164 

165client := openai.NewClient()

166http.HandleFunc("/webhooks/openai", func(w http.ResponseWriter, r *http.Request) {

167 if r.Method != http.MethodPost {

168 w.WriteHeader(http.StatusMethodNotAllowed)

169 return

170 }

171 body, err := io.ReadAll(r.Body)

172 if err != nil {

173 http.Error(w, "Invalid body", http.StatusBadRequest)

174 return

175 }

176 if err := client.Webhooks.VerifySignature(body, r.Header); err != nil {

177 http.Error(w, "Invalid signature", http.StatusBadRequest)

178 return

179 }

180 var event struct {

181 Type string `json:"type"`

182 Data struct {

183 ID string `json:"id"`

184 } `json:"data"`

185 }

186 if err := json.Unmarshal(body, &event); err != nil {

187 http.Error(w, "Invalid JSON", http.StatusBadRequest)

188 return

189 }

190 if event.Type == "agent.session.idle" {

191 session, err := client.Beta.Agents.Sessions.Get(r.Context(), event.Data.ID)

192 if err != nil {

193 http.Error(w, "Could not retrieve session", http.StatusInternalServerError)

194 return

195 }

196 fmt.Println("session idle event:", session.ID)

197 } else {

198 fmt.Println("session event:", event.Type, event.Data.ID)

199 }

200 w.WriteHeader(http.StatusOK)

201})

202port := os.Getenv("PORT")

203if port == "" {

204 port = "8000"

205}

206if err := http.ListenAndServe(":"+port, nil); err != nil {

207 panic(err)

208}

209```

210 

211```java

212import com.fasterxml.jackson.databind.json.JsonMapper;

213import com.openai.client.OpenAIClient;

214import com.openai.client.okhttp.OpenAIOkHttpClient;

215import com.openai.core.http.Headers;

216import com.openai.errors.InvalidWebhookSignatureException;

217import com.openai.models.webhooks.WebhookVerificationParams;

218import com.sun.net.httpserver.HttpServer;

219import java.net.InetSocketAddress;

220import java.nio.charset.StandardCharsets;

221 

222OpenAIClient client = OpenAIOkHttpClient.fromEnv();

223var json = new JsonMapper();

224int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "8000"));

225var server = HttpServer.create(new InetSocketAddress(port), 0);

226server.createContext(

227 "/webhooks/openai",

228 exchange -> {

229 try (exchange) {

230 if (!exchange.getRequestMethod().equals("POST")) {

231 exchange.sendResponseHeaders(405, -1);

232 return;

233 }

234 String payload =

235 new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8);

236 try {

237 client

238 .webhooks()

239 .verifySignature(

240 WebhookVerificationParams.builder()

241 .payload(payload)

242 .headers(Headers.builder().putAll(exchange.getRequestHeaders()).build())

243 .build());

244 } catch (InvalidWebhookSignatureException e) {

245 exchange.sendResponseHeaders(400, -1);

246 return;

247 }

248 var event = json.readTree(payload);

249 if (event.path("type").asText().equals("agent.session.idle")) {

250 var session =

251 client

252 .beta()

253 .agents()

254 .sessions()

255 .retrieve(event.path("data").path("id").asText());

256 System.out.println("session idle event: " + session.id());

257 } else {

258 System.out.println(

259 "session event: "

260 + event.path("type").asText()

261 + " "

262 + event.path("data").path("id").asText());

263 }

264 exchange.sendResponseHeaders(200, -1);

265 }

266 });

267server.start();

268```

269 

270```ruby

271require "openai"

272require "webrick"

273require "json"

274 

275client = OpenAI::Client.new

276server = WEBrick::HTTPServer.new(Port: Integer(ENV.fetch("PORT", "8000")))

277server.mount_proc "/webhooks/openai" do |request, response|

278 if request.request_method != "POST"

279 response.status = 405

280 next

281 end

282 payload = request.body

283 begin

284 client.webhooks.verify_signature(payload, request.header.transform_values(&:first))

285 rescue OpenAI::Errors::InvalidWebhookSignatureError

286 response.status = 400

287 response.body = "Invalid signature"

288 next

289 end

290 event = JSON.parse(payload)

291 if event["type"] == "agent.session.idle"

292 session = client.beta.agents.sessions.retrieve(event.fetch("data").fetch("id"))

293 puts "session idle event: #{session.id}"

294 else

295 puts "session event: #{event["type"]} #{event.dig("data", "id")}"

296 end

297 response.status = 200

298end

299trap("INT") { server.shutdown }

300server.start

301```

302 

303 

304## Environment connection events

305 

306When initial or follow-up input needs a disconnected self-hosted executor, the API adds an `environment_connection` required action. It emits `agent.session.action_required` **before waiting** for the connection.

307 

308Retrieve the session and confirm that `required_actions` still requests a connection. Start the executor with `session.environment.id` and `session.environment.remote_url`. This webhook does not include `connect.remote_url`. If the executor connects before the wait expires, the API clears the required action and resumes the submission without client resubmission.

309 

310The API waits up to five minutes for the connection. A follow-up input request can remain open during this wait. Configure client and proxy timeouts accordingly. `agent.session.in_progress` confirms execution has started, not that the API is waiting for a connection.

311 

312If the wait expires, the submission fails. Initial input can fail asynchronously and leave the session in `failed`. The connection wait does not provide a durable input queue. A process crash or client disconnect may require retries.

313 

314## Session and turn outcomes

315 

316`agent.session.idle` means the session is ready for more input, not that its last turn succeeded. Inspect that turn's status or observe `agent.session.turn.completed`, `agent.session.turn.failed`, or `agent.session.turn.cancelled` on the session stream. A completed turn can still contain failed tool calls. Check tool results and the agent's final response.

317 

318`agent.session.failed` reports a failed session, not every failed turn. Session deletion has no corresponding webhook and does not stop provider compute.

Details

1# Functions

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5Function tools let an agent call your application code. You define the function and its arguments. The agent requests a call, your code returns a result, and the harness continues the turn.

6 

7Your handler can run in an application server, a worker, or an environment you control. Attaching an environment to a session does not automatically run function tools there.

8 

9 

10 

11 

12If you use [function calling in the Responses API](https://developers.openai.com/api/docs/guides/function-calling), you can reuse your function implementation with the session flow described here.

13 

14 

15 

16 

17## Define a function

18 

19Add a function definition to `agent.tools` when you [configure the agent](https://developers.openai.com/api/docs/guides/agents-api/configuration). Give it a name, a description, and a JSON Schema for its arguments:

20 

21```json

22{

23 "type": "function",

24 "name": "get_customer",

25 "description": "Look up a customer by ID.",

26 "parameters": {

27 "type": "object",

28 "properties": { "customer_id": { "type": "string" } },

29 "required": ["customer_id"],

30 "additionalProperties": false

31 }

32}

33```

34 

35 

36 

37 

38## Handle required actions

39 

40When the agent needs a function result, the session emits `agent.session.requires_action`. Read the pending calls from `event.session.required_actions`. You can also [retrieve the session](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/methods/retrieve) and read `session.required_actions` without streaming.

41 

42A function entry in `required_actions` looks like this:

43 

44```json

45{

46 "type": "function_call",

47 "turn_id": "turn_123",

48 "call_id": "call_123",

49 "name": "get_customer",

50 "arguments": { "customer_id": "123" }

51}

52```

53 

54Run the named function with the supplied arguments. Use `required_actions` to decide which calls need results; a `function_call` item in session history alone does not establish that a result is pending.

55 

56## Return the result

57 

58Send `agent.session.input.tool_result` to the [session events endpoint](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/subresources/events/methods/create). Copy `turn_id` and `call_id` from the pending action:

59 

60- For success, set `success: true` and supply `output` as a string or a supported content array. Serialize JSON objects to strings.

61- For an error, set `success: false` and supply an `error` message that the agent can use.

62 

63 

64 

65 

66For each pending `get_customer` call, run your lookup and return its result. Here, `action` is the entry from `required_actions`:

67 

68Return a function result

69 

70```javascript

71const result = {

72 turn_id: action.turn_id,

73 call_id: action.call_id,

74};

75let outcome;

76 

77outcome = {

78 success: true,

79 output: JSON.stringify(getCustomer(action.arguments)),

80};

81 

82await client.beta.agents.sessions.events.create(sessionId, {

83 events: [

84 { type: "agent.session.input.tool_result", ...result, ...outcome },

85 ],

86});

87```

88 

89```python

90import json

91 

92action = action.to_dict()

93 

94result = {

95 "type": "agent.session.input.tool_result",

96 "turn_id": action["turn_id"],

97 "call_id": action["call_id"],

98}

99 

100output = get_customer(action["arguments"])

101result.update(success=True, output=json.dumps(output))

102 

103client.beta.agents.sessions.events.create(session_id, events=[result])

104```

105 

106```go

107result := openai.AgentSessionInputParamAgentSessionInputToolResult{

108 TurnID: action.TurnID,

109 CallID: action.CallID,

110}

111 

112arguments := action.Arguments.(map[string]any)

113customerID := arguments["customer_id"].(string)

114var customer any

115if customerID == "123" {

116 customer = map[string]any{"name": "Example Customer", "plan": "pro"}

117}

118output, err := json.Marshal(map[string]any{"found": customer != nil, "customer": customer})

119if err != nil {

120 panic(err)

121}

122result.Success = true

123result.Output = openai.AgentFunctionCallOutputParamUnion{OfString: openai.String(string(output))}

124 

125err = client.Beta.Agents.Sessions.Events.New(ctx, session.ID, openai.BetaAgentSessionEventNewParams{

126 Events: []openai.AgentSessionInputParamUnion{{OfParamAgentSessionInputToolResult: &result}},

127})

128if err != nil {

129 panic(err)

130}

131```

132 

133```java

134var json = new JsonMapper();

135 

136var result =

137 AgentSessionInputParam.AgentSessionInputToolResult.builder()

138 .turnId(action.turnId())

139 .callId(action.callId());

140var arguments = json.valueToTree(action._arguments());

141 

142boolean found = arguments.path("customer_id").asText().equals("123");

143var output = json.createObjectNode().put("found", found);

144if (found)

145 output.putObject("customer").put("name", "Example Customer").put("plan", "pro");

146else output.putNull("customer");

147result.success(true).output(json.writeValueAsString(output));

148 

149client

150 .beta()

151 .agents()

152 .sessions()

153 .events()

154 .create(

155 EventCreateParams.builder()

156 .sessionId(sessionId)

157 .addEvent(result.build())

158 .build());

159```

160 

161```ruby

162require "json"

163 

164result = {

165 type: "agent.session.input.tool_result",

166 turn_id: action.turn_id,

167 call_id: action.call_id

168}

169arguments = action.arguments

170 

171customer_id = arguments[:customer_id] || arguments["customer_id"]

172customer = (customer_id == "123") ? {

173 name: "Example Customer",

174 plan: "pro"

175} : nil

176result[:success] = true

177result[:output] = JSON.generate(found: !customer.nil?, customer: customer)

178 

179client.beta.agents.sessions.events.create(session.id, events: [result])

180```

181 

182 

183The harness continues the turn after it receives the required results. Follow [session events and items](https://developers.openai.com/api/docs/guides/agents-api/sessions/events) to check the turn's outcome and retrieve its output.

184 

185## Recover after a disconnect

186 

187Retrieve the session to find pending actions. If you already ran a function, submit its saved result with the same `turn_id` and `call_id`.

188 

189For functions with side effects, store results durably by session, turn, and call ID. If execution might have succeeded but no result was saved, check the outcome before running the function again.

190 

191 

192 

193 

194## Load functions on demand

195 

196Functions load eagerly by default. To defer a function, set `defer_loading: true` on its definition and include `{ "type": "tool_search" }` in `agent.tools`. See [Tool search](https://developers.openai.com/api/docs/guides/tools-tool-search#agents-api) for a complete example.

guides/agents-api/tools/mcp.md +210 −0 created

Details

1# MCP connections

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5An MCP server publishes tool definitions and runs tool calls. The Agents API discovers the tools, calls the server, and returns results to the agent. Your application does not need to handle each call.

6 

7Choose where the connection runs based on where the server is reachable:

8 

9| Connection | Where it runs | Requires an environment |

10| -------------------------------------------------- | --------------------------------------- | ----------------------- |

11| HTTP with `connection_origin: "service"` (default) | OpenAI | No |

12| HTTP with `connection_origin: "environment"` | Your session's environment | Yes |

13| stdio | A process in your session's environment | Yes |

14 

15 

16 

17 

18 

19 

20## Connect from OpenAI

21 

22Add an HTTP MCP server to `agent.tools`. The server must be reachable from OpenAI. This works with or without a session environment.

23 

24For example, the OpenAI documentation MCP allows anonymous access:

25 

26```json

27{

28 "type": "mcp",

29 "server_label": "openai_docs",

30 "transport": {

31 "type": "http",

32 "server_url": "https://developers.openai.com/mcp"

33 },

34 "connection_origin": "service",

35 "required": true

36}

37```

38 

39<picture>

40 <source

41 media="(max-width: 640px)"

42 srcSet="/images/api/agents-api/remote-mcps-1-mobile.webp"

43 width="680"

44 height="956"

45 />

46 <img src="https://developers.openai.com/images/api/agents-api/remote-mcps-1.webp"

47 width="1400"

48 height="624"

49 alt="The Agents API service connects to a remote MCP server and exchanges calls and results. An optional attached vault supplies a credential matched to the server URL."

50 loading="lazy"

51 />

52</picture>

53 

54 

55 

56 

57 

58 

59## Connect from your environment

60 

61An executor MCP connects from the session's environment. Use it for servers on a private network or software installed in that environment.

62 

63Set the session's `environment.type` to `self_hosted` or `openai_hosted`. For a self-hosted environment, [connect the executor](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted) before the agent uses its tools.

64 

65### Connect over HTTP

66 

67Use HTTP for a server that is already running. Add this entry to `agent.tools`, replacing the URL with an address your environment can reach:

68 

69```json

70{

71 "type": "mcp",

72 "server_label": "internal_search",

73 "transport": {

74 "type": "http",

75 "server_url": "https://mcp.internal.example.com/search"

76 },

77 "connection_origin": "environment",

78 "required": true

79}

80```

81 

82Here, a localhost URL refers to the session's environment. If you omit `connection_origin`, OpenAI makes the connection instead.

83 

84 

85 

86 

87 

88 

89### Start a server over stdio

90 

91Use stdio to let the executor start a server process. Install the server and its dependencies in the environment first.

92 

93For this customer lookup example, install the MCP SDK:

94 

95```bash

96python3 -m venv /workspace/mcp-demo

97/workspace/mcp-demo/bin/python -m pip install 'mcp==1.26.0'

98```

99 

100Save the server as `/workspace/lookup_mcp.py`:

101 

102Run a customer lookup MCP server

103 

104```python

105import sys

106 

107from mcp.server.fastmcp import FastMCP

108 

109server = FastMCP("customer-lookup", host="127.0.0.1", port=8765, stateless_http=True)

110 

111 

112@server.tool()

113def get_customer(customer_id: str) -> dict:

114 """Look up a customer in the example data."""

115 customers = {"123": {"name": "Example Customer", "plan": "pro"}}

116 return {"customer": customers.get(customer_id)}

117 

118 

119if __name__ == "__main__":

120 transport = sys.argv[1] if len(sys.argv) > 1 else "streamable-http"

121 server.run(transport=transport)

122```

123 

124 

125Add the server to `agent.tools`. The `stdio` argument selects the script's transport:

126 

127```json

128{

129 "type": "mcp",

130 "server_label": "customer_lookup",

131 "transport": {

132 "type": "stdio",

133 "command": "/workspace/mcp-demo/bin/python",

134 "args": ["/workspace/lookup_mcp.py", "stdio"],

135 "cwd": "/workspace"

136 },

137 "required": true

138}

139```

140 

141For stdio, `command` and an absolute `cwd` are required; `args` is optional. Omit `connection_origin`.

142 

143Send a message asking the agent to look up customer `123`. The tool returns `Example Customer` on the `pro` plan.

144 

145For OpenAI-hosted stdio MCPs, omit the network policy or set it to `enabled`. The `disabled` and `restricted` network policies are not supported for these connections.

146 

147 

148 

149 

150 

151 

152 

153 

154 

155 

156 

157 

158## Add authentication

159 

160For a server that allows anonymous access, omit authentication fields and `vault_ids`. Otherwise, choose the credential source for your connection:

161 

162- **HTTP credentials for one session:** Set `transport.authorization` or `transport.headers` when creating the session. The Agents API encrypts these values and omits them from the returned session resource.

163- **Reusable HTTP credentials:** Store credentials in a [vault](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults) and attach it through `vault_ids`. Vaults apply only to connections from OpenAI. Credentials match the server URL; use `credential_id` to select one when several match.

164- **Stdio credentials:** Supply values in the environment and list their names in `transport.env_vars`. These values can be read by code running in the environment. Self-hosted sessions do not accept inline values in `transport.env`.

165 

166For example, an HTTP transport can include a bearer token and another header:

167 

168```json

169{

170 "type": "http",

171 "server_url": "https://mcp.example.com/mcp",

172 "authorization": "Bearer YOUR_MCP_ACCESS_TOKEN",

173 "headers": { "X-Tenant-ID": "tenant_123" }

174}

175```

176 

177Use one source for `Authorization`: inline configuration or a matching vault credential. Other headers can accompany vault authentication. Environment-origin HTTP does not use vault credentials; use inline authentication or a trusted proxy.

178 

179Keep secrets out of reusable agent definitions, plugin archives, and logs. To keep credentials inaccessible to agent-generated code, use a [trusted proxy or server](https://developers.openai.com/api/docs/guides/agents-api/environments/security#broker-third-party-access) that supplies them outside the environment.

180 

181 

182 

183 

184## Control tool access and startup

185 

186Set `allowed_tools` to limit which tools the agent can discover and call. Set `required: true` to fail the turn if the server cannot initialize. Initialization is optional by default.

187 

188See the [Create session reference](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/methods/create) for all MCP configuration fields.

189 

190 

191 

192 

193 

194 

195## Troubleshoot connections

196 

197If a required server cannot initialize, inspect the error in `agent.session.turn.failed`. For stdio servers, also check the MCP process logs.

198 

199- **Network access:** Check the URL and `connection_origin`. For environment connections, check that the executor is connected and its network can reach the server.

200- **Credentials:** Check the token or headers. For a vault, check that the credential matches the server URL.

201- **Executable and dependencies:** Check that the configured command runs inside the environment.

202- **Working directory:** Use an existing absolute `cwd` for an inline stdio configuration.

203 

204 

205 

206 

207## Related guides

208 

209- [Plugins](https://developers.openai.com/api/docs/guides/agents-api/tools/plugins) package MCP configuration and skills for reuse across sessions.

210- [Tool search](https://developers.openai.com/api/docs/guides/tools-tool-search#agents-api) explains automatic MCP tool discovery on supported models and providers.

Details

1# Plugins

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5A plugin packages skills, MCP configuration, or both. Load its files into your own environment or upload a ZIP to an OpenAI-hosted environment.

6 

7## Package the plugin

8 

9This plugin combines a documentation-search skill with the OpenAI documentation MCP. It needs network access but no credentials or local server dependencies.

10 

11```text

12docs-helper/

13├── .codex-plugin/plugin.json

14├── .mcp.json

15└── skills/docs-search/SKILL.md

16```

17 

18Declare the skill directory and MCP configuration in `.codex-plugin/plugin.json`:

19 

20```json

21{

22 "name": "docs-helper",

23 "version": "1.0.0",

24 "description": "Find answers in OpenAI developer documentation.",

25 "skills": "./skills/",

26 "mcpServers": "./.mcp.json"

27}

28```

29 

30Paths resolve from the plugin root. They must start with `./`, stay inside the plugin, and contain no `..` components. See [Package your plugin](https://developers.openai.com/plugins/build/plugins) for the full manifest format.

31 

32Add the server to `.mcp.json`. This file uses the plugin format, which differs from `agent.tools`:

33 

34```json

35{

36 "mcpServers": {

37 "openai_docs": {

38 "type": "http",

39 "url": "https://developers.openai.com/mcp"

40 }

41 }

42}

43```

44 

45Add the instructions to `skills/docs-search/SKILL.md`:

46 

47```markdown

48---

49name: docs-search

50description: Find answers in OpenAI developer documentation.

51---

52 

53Use the openai_docs MCP server to find relevant documentation.

54Answer the question and link to the sources you used.

55```

56 

57## Register plugins in a self-hosted sandbox

58 

59Copy the plugin to `/workspace/plugins/docs-helper` and add that absolute path to `environment.capability_directories`. Select the plugin root, which contains `.codex-plugin/plugin.json`.

60 

61Register a plugin

62 

63```javascript

64import OpenAI from "openai";

65const client = new OpenAI();

66 

67const result = await client.beta.agents.sessions.create({

68 agent: {

69 model: "gpt-6-astra",

70 },

71 environment: {

72 type: "self_hosted",

73 workspace_directory: "/workspace",

74 capability_directories: ["/workspace/plugins/docs-helper"],

75 },

76});

77console.log(result.id);

78```

79 

80```python

81from openai import OpenAI

82 

83client = OpenAI()

84 

85result = client.beta.agents.sessions.create(

86 agent={"model": "gpt-6-astra"},

87 environment={

88 "type": "self_hosted",

89 "workspace_directory": "/workspace",

90 "capability_directories": ["/workspace/plugins/docs-helper"],

91 },

92)

93print(result.id)

94```

95 

96```go

97import (

98 "context"

99 "fmt"

100 

101 "github.com/openai/openai-go/v3"

102)

103 

104ctx := context.Background()

105client := openai.NewClient()

106result, err := client.Beta.Agents.Sessions.New(ctx,

107 openai.BetaAgentSessionNewParams{

108 Agent: openai.BetaAgentSessionNewParamsAgent{Model: openai.String("gpt-6-astra")},

109 Environment: openai.EnvironmentParamUnion{

110 OfParamSelfHosted: &openai.EnvironmentParamSelfHosted{

111 WorkspaceDirectory: "/workspace",

112 CapabilityDirectories: []string{"/workspace/plugins/docs-helper"},

113 },

114 },

115 })

116if err != nil {

117 panic(err)

118}

119fmt.Println(result.ID)

120```

121 

122```java

123import com.openai.client.OpenAIClient;

124import com.openai.client.okhttp.OpenAIOkHttpClient;

125import com.openai.models.beta.agents.EnvironmentParam;

126import com.openai.models.beta.agents.sessions.SessionCreateParams;

127import java.util.List;

128 

129OpenAIClient client = OpenAIOkHttpClient.fromEnv();

130var result =

131 client

132 .beta()

133 .agents()

134 .sessions()

135 .create(

136 SessionCreateParams.builder()

137 .agent(SessionCreateParams.Agent.builder().model("gpt-6-astra").build())

138 .environment(

139 EnvironmentParam.SelfHosted.builder()

140 .workspaceDirectory("/workspace")

141 .capabilityDirectories(List.of("/workspace/plugins/docs-helper"))

142 .build())

143 .build());

144System.out.println(result.id());

145```

146 

147```ruby

148require "openai"

149 

150client = OpenAI::Client.new

151result = client.beta.agents.sessions.create(

152 agent: { model: "gpt-6-astra" },

153 environment: {

154 type: "self_hosted",

155 workspace_directory: "/workspace",

156 capability_directories: ["/workspace/plugins/docs-helper"]

157 }

158)

159puts result.id

160```

161 

162 

163[Connect the executor](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted) before the agent uses the plugin. Allow the environment to reach `https://developers.openai.com/mcp`.

164 

165For multiple plugins, list each root. A parent directory can discover nested skills, but does not load every child plugin's MCP configuration.

166 

167## Upload plugins to an OpenAI-hosted sandbox

168 

169Supply one ZIP per plugin in `environment.plugins`. Each ZIP must contain one plugin folder with `.codex-plugin/plugin.json` inside it. The request's name and description must match the manifest.

170 

171This helper packages your folder and creates a session. Pass your API client and the path to `docs-helper`. OpenAI extracts and registers the plugin automatically.

172 

173Upload a plugin folder

174 

175```python

176import base64

177import json

178import shutil

179from pathlib import Path

180from tempfile import TemporaryDirectory

181 

182 

183def upload_plugin(client, plugin_directory):

184 plugin_directory = Path(plugin_directory).resolve()

185 manifest = json.loads((plugin_directory / ".codex-plugin/plugin.json").read_text())

186 with TemporaryDirectory() as temporary:

187 archive = shutil.make_archive(

188 str(Path(temporary) / "plugin"),

189 "zip",

190 root_dir=plugin_directory.parent,

191 base_dir=plugin_directory.name,

192 )

193 return client.beta.agents.sessions.create(

194 agent={"model": "gpt-6-astra"},

195 environment={

196 "type": "openai_hosted",

197 "plugins": [

198 {

199 "type": "inline",

200 "name": manifest["name"],

201 "description": manifest["description"],

202 "source": {

203 "type": "base64",

204 "media_type": "application/zip",

205 "data": base64.b64encode(

206 Path(archive).read_bytes()

207 ).decode(),

208 },

209 }

210 ],

211 },

212 )

213```

214 

215 

216## Reuse a hosted plugin setup

217 

218[Create an environment template](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/environments/subresources/templates/methods/create) with the plugin list. For later sessions, set `environment.environment_template_id` to the saved template ID.

219 

220Omit `environment.plugins` to inherit the template's plugin list. Supplying a list replaces it. Each session gets its own environment; the root agent and its subagents share it.

221 

222## Authenticate MCP servers

223 

224The example needs no authentication. For other plugin MCP servers:

225 

226- **HTTP:** `bearer_token_env_var` reads an environment variable and sends its value as a bearer token. Other `http_headers` values are literal; `env_http_headers` is not supported.

227- **Stdio:** `env_vars` lists environment variables to pass to the server process. Install the executable and its dependencies in the environment. A relative `cwd` resolves from the plugin root.

228 

229Keep secrets out of plugin files and archives. Plugin MCP connections run from the session's environment. See [MCP authentication](https://developers.openai.com/api/docs/guides/agents-api/tools/mcp#add-authentication) for credential boundaries.

230 

231For hosted stdio MCPs, omit the network policy or set it to `enabled`. The `disabled` and `restricted` network policies are not supported for these connections.

232 

233## Test a plugin

234 

235Send a normal session message that asks for the skill:

236 

237> Use docs-search to explain how to stream Responses API output. Include links to the documentation.

238 

239Check that the turn completed and that its [saved items](https://developers.openai.com/api/docs/guides/agents-api/sessions/events#fetch-items-and-turns) include a successful call to `openai_docs`. The answer should follow the skill's instructions and cite the documentation. For a skill-only plugin, check its output against the instructions; an MCP call is not required.

240 

241Create a new session after changing plugin files or a template. Existing sessions do not reload the tools. For connection errors, see [MCP troubleshooting](https://developers.openai.com/api/docs/guides/agents-api/tools/mcp#troubleshoot-connections). [Delete test sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage) and stop self-hosted compute when finished.

Details

1# Vaults

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5A vault stores credentials for MCP connections from OpenAI. Attach it to a session so the agent can use authenticated tools without receiving the secret values.

6 

7Vaults support bearer tokens and existing OAuth grants. For connections from your environment, use the other [MCP authentication options](https://developers.openai.com/api/docs/guides/agents-api/tools/mcp#add-authentication).

8 

9## Permissions

10 

11For a restricted application key, grant:

12 

13- `api.vaults.read` to list and retrieve vaults and credentials.

14- `api.vaults.write` to create, update, or delete them.

15 

16 

17 

18 

19## Create and use a vault

20 

21Use your API client, the MCP server URL (`mcp_url`), and an access token for that server (`access_token`). The examples use GitHub tools.

22 

23First, create a vault:

24 

25Create a vault

26 

27```javascript

28const vault = await client.beta.agents.vaults.create({

29 name: "GitHub credentials",

30 metadata: {

31 external_user_id: "user_123",

32 },

33});

34```

35 

36```python

37vault = client.beta.agents.vaults.create(

38 name="GitHub credentials", metadata={"external_user_id": "user_123"}

39)

40```

41 

42```go

43vault, err := client.Beta.Agents.Vaults.New(ctx,

44 openai.BetaAgentVaultNewParams{

45 Name: openai.String("GitHub credentials"),

46 Metadata: map[string]string{"external_user_id": "user_123"},

47 })

48if err != nil {

49 panic(err)

50}

51```

52 

53```java

54var vault =

55 client

56 .beta()

57 .agents()

58 .vaults()

59 .create(

60 VaultCreateParams.builder()

61 .name("GitHub credentials")

62 .metadata(

63 VaultCreateParams.Metadata.builder()

64 .putAdditionalProperty("external_user_id", JsonValue.from("user_123"))

65 .build())

66 .build());

67```

68 

69```ruby

70vault = client.beta.agents.vaults.create(

71 name: "GitHub credentials",

72 metadata: { external_user_id: "user_123" }

73)

74```

75 

76 

77Save its ID as `vault_id`, then add the token. `mcp_server_url` binds the credential to that server:

78 

79Store a bearer token

80 

81```javascript

82// Replace the illustrative IDs and URLs below with your own resource values.

83const vaultId = "vault_123";

84const mcpUrl = "https://api.githubcopilot.com/mcp/";

85const accessToken = process.env.GITHUB_TOKEN;

86 

87const credential = await client.beta.agents.vaults.credentials.create(vaultId, {

88 name: "GitHub access token",

89 auth: {

90 type: "static_bearer",

91 mcp_server_url: mcpUrl,

92 token: accessToken,

93 },

94});

95```

96 

97```python

98# Replace the illustrative IDs and URLs below with your own resource values.

99vault_id = "vault_123"

100mcp_url = "https://api.githubcopilot.com/mcp/"

101access_token = os.environ["GITHUB_TOKEN"]

102 

103credential = client.beta.agents.vaults.credentials.create(

104 vault_id,

105 name="GitHub access token",

106 auth={

107 "type": "static_bearer",

108 "mcp_server_url": mcp_url,

109 "token": access_token,

110 },

111)

112```

113 

114```go

115// Replace the illustrative IDs and URLs below with your own resource values.

116vaultId := "vault_123"

117mcpUrl := "https://api.githubcopilot.com/mcp/"

118accessToken := os.Getenv("GITHUB_TOKEN")

119 

120credential, err := client.Beta.Agents.Vaults.Credentials.New(ctx,

121 vaultId,

122 openai.BetaAgentVaultCredentialNewParams{

123 Name: "GitHub access token",

124 Auth: openai.CredentialAuthCreateParamUnion{

125 OfParamStaticBearer: &openai.CredentialAuthCreateParamStaticBearer{

126 McpServerURL: mcpUrl,

127 Token: accessToken,

128 },

129 },

130 })

131if err != nil {

132 panic(err)

133}

134```

135 

136```java

137// Replace the illustrative IDs and URLs below with your own resource values.

138String vaultId = "vault_123";

139String mcpUrl = "https://api.githubcopilot.com/mcp/";

140String accessToken = System.getenv("GITHUB_TOKEN");

141 

142var credential =

143 client

144 .beta()

145 .agents()

146 .vaults()

147 .credentials()

148 .create(

149 CredentialCreateParams.builder()

150 .vaultId(vaultId)

151 .name("GitHub access token")

152 .auth(

153 CredentialAuthCreateParam.StaticBearer.builder()

154 .mcpServerUrl(mcpUrl)

155 .token(accessToken)

156 .build())

157 .build());

158```

159 

160```ruby

161# Replace the illustrative IDs and URLs below with your own resource values.

162vault_id = "vault_123"

163mcp_url = "https://api.githubcopilot.com/mcp/"

164access_token = ENV.fetch("GITHUB_TOKEN")

165 

166credential = client.beta.agents.vaults.credentials.create(

167 vault_id,

168 name: "GitHub access token",

169 auth: {

170 type: "static_bearer",

171 mcp_server_url: mcp_url,

172 token: access_token

173 }

174)

175```

176 

177 

178Save the credential ID as `credential_id` for later updates.

179 

180 

181 

182 

183Pass the saved ID in `vault_ids` when creating a session. Use the same server URL in the MCP configuration:

184 

185Attach the vault to a session

186 

187```javascript

188// Replace the illustrative IDs and URLs below with your own resource values.

189const mcpUrl = "https://api.githubcopilot.com/mcp/";

190const vaultId = "vault_123";

191 

192const session = await client.beta.agents.sessions.create({

193 agent: {

194 model: "gpt-6-astra",

195 tools: [

196 {

197 type: "mcp",

198 server_label: "github",

199 transport: {

200 type: "http",

201 server_url: mcpUrl,

202 },

203 allowed_tools: ["search_issues", "issue_read"],

204 required: true,

205 connection_origin: "service",

206 },

207 ],

208 },

209 environment: {

210 type: "none",

211 },

212 input: "Find open bugs reported in the last week.",

213 vault_ids: [vaultId],

214});

215```

216 

217```python

218# Replace the illustrative IDs and URLs below with your own resource values.

219mcp_url = "https://api.githubcopilot.com/mcp/"

220vault_id = "vault_123"

221 

222session = client.beta.agents.sessions.create(

223 agent={

224 "model": "gpt-6-astra",

225 "tools": [

226 {

227 "type": "mcp",

228 "server_label": "github",

229 "transport": {

230 "type": "http",

231 "server_url": mcp_url,

232 },

233 "allowed_tools": ["search_issues", "issue_read"],

234 "required": True,

235 "connection_origin": "service",

236 }

237 ],

238 },

239 environment={"type": "none"},

240 input="Find open bugs reported in the last week.",

241 vault_ids=[vault_id],

242)

243```

244 

245```go

246// Replace the illustrative IDs and URLs below with your own resource values.

247mcpUrl := "https://api.githubcopilot.com/mcp/"

248vaultId := "vault_123"

249 

250session, err := client.Beta.Agents.Sessions.New(ctx,

251 openai.BetaAgentSessionNewParams{

252 Agent: openai.BetaAgentSessionNewParamsAgent{

253 Model: openai.String("gpt-6-astra"),

254 Tools: []openai.AgentToolParamUnion{

255 {

256 OfParamMcp: &openai.AgentToolParamMcp{

257 ServerLabel: "github",

258 Transport: openai.McpTransportParamUnion{OfParamHTTP: &openai.McpTransportParamHTTP{ServerURL: mcpUrl}},

259 AllowedTools: []string{"search_issues", "issue_read"},

260 Required: openai.Bool(true),

261 ConnectionOrigin: "service",

262 },

263 },

264 },

265 },

266 Environment: openai.EnvironmentParamUnion{OfParamNone: &openai.EnvironmentParamNone{}},

267 Input: openai.BetaAgentSessionNewParamsInputUnion{OfString: openai.String("Find open bugs reported in the last week.")},

268 VaultIDs: []string{vaultId},

269 })

270if err != nil {

271 panic(err)

272}

273```

274 

275```java

276// Replace the illustrative IDs and URLs below with your own resource values.

277String mcpUrl = "https://api.githubcopilot.com/mcp/";

278String vaultId = "vault_123";

279 

280var session =

281 client

282 .beta()

283 .agents()

284 .sessions()

285 .create(

286 SessionCreateParams.builder()

287 .agent(

288 SessionCreateParams.Agent.builder()

289 .model("gpt-6-astra")

290 .addTool(

291 AgentToolParam.Mcp.builder()

292 .serverLabel("github")

293 .transport(

294 McpTransportParam.Http.builder().serverUrl(mcpUrl).build())

295 .allowedTools(List.of("search_issues", "issue_read"))

296 .required(true)

297 .connectionOrigin(

298 AgentToolParam.Mcp.ConnectionOrigin.of("service"))

299 .build())

300 .build())

301 .environmentNone()

302 .input("Find open bugs reported in the last week.")

303 .vaultIds(List.of(vaultId))

304 .build());

305```

306 

307```ruby

308# Replace the illustrative IDs and URLs below with your own resource values.

309mcp_url = "https://api.githubcopilot.com/mcp/"

310vault_id = "vault_123"

311 

312session = client.beta.agents.sessions.create(

313 agent: {

314 model: "gpt-6-astra",

315 tools: [

316 {

317 type: "mcp",

318 server_label: "github",

319 transport: {

320 type: "http",

321 server_url: mcp_url

322 },

323 allowed_tools: [

324 "search_issues",

325 "issue_read"

326 ],

327 required: true,

328 connection_origin: "service"

329 }

330 ]

331 },

332 environment: { type: "none" },

333 input: "Find open bugs reported in the last week.",

334 vault_ids: [vault_id]

335)

336```

337 

338 

339The Agents API selects a credential that matches the server URL. If several attached credentials match, set the MCP tool's `credential_id` to select one. Retrieving a vault or credential does not return its secret values.

340 

341 

342 

343 

344## Use OAuth credentials

345 

346Your application handles the provider's authorization and consent flow. Store the resulting grant with `auth.type: "mcp_oauth"`. Set `expires_at` to the access token's expiry as an RFC 3339 timestamp, if known.

347 

348The following example uses values from your provider's OAuth flow. Include `refresh` to let the Agents API refresh the token:

349 

350Store an OAuth grant

351 

352```javascript

353// Replace the illustrative expiry with your access token's actual expiry.

354// Replace the illustrative IDs and URLs below with your own resource values.

355const vaultId = "vault_123";

356const mcpUrl = "https://mcp.example.com/mcp";

357const accessToken = process.env.OAUTH_ACCESS_TOKEN;

358const expiresAt = "2030-01-01T00:00:00Z";

359const tokenEndpoint = "https://auth.example.com/oauth/token";

360const clientId = "example-client-id";

361const refreshToken = process.env.OAUTH_REFRESH_TOKEN;

362 

363const credential = await client.beta.agents.vaults.credentials.create(vaultId, {

364 name: "Example MCP OAuth credential",

365 auth: {

366 type: "mcp_oauth",

367 mcp_server_url: mcpUrl,

368 access_token: accessToken,

369 expires_at: expiresAt,

370 refresh: {

371 token_endpoint: tokenEndpoint,

372 client_id: clientId,

373 refresh_token: refreshToken,

374 token_endpoint_auth: {

375 type: "none",

376 },

377 },

378 },

379});

380```

381 

382```python

383# Replace the illustrative expiry with your access token's actual expiry.

384# Replace the illustrative IDs and URLs below with your own resource values.

385vault_id = "vault_123"

386mcp_url = "https://mcp.example.com/mcp"

387access_token = os.environ["OAUTH_ACCESS_TOKEN"]

388expires_at = "2030-01-01T00:00:00Z"

389token_endpoint = "https://auth.example.com/oauth/token"

390client_id = "example-client-id"

391refresh_token = os.environ["OAUTH_REFRESH_TOKEN"]

392 

393credential = client.beta.agents.vaults.credentials.create(

394 vault_id,

395 name="Example MCP OAuth credential",

396 auth={

397 "type": "mcp_oauth",

398 "mcp_server_url": mcp_url,

399 "access_token": access_token,

400 "expires_at": expires_at,

401 "refresh": {

402 "token_endpoint": token_endpoint,

403 "client_id": client_id,

404 "refresh_token": refresh_token,

405 "token_endpoint_auth": {"type": "none"},

406 },

407 },

408)

409```

410 

411```go

412// Replace the illustrative expiry with your access token's actual expiry.

413// Replace the illustrative IDs and URLs below with your own resource values.

414vaultId := "vault_123"

415mcpUrl := "https://mcp.example.com/mcp"

416accessToken := os.Getenv("OAUTH_ACCESS_TOKEN")

417expiresAt := "2030-01-01T00:00:00Z"

418tokenEndpoint := "https://auth.example.com/oauth/token"

419clientId := "example-client-id"

420refreshToken := os.Getenv("OAUTH_REFRESH_TOKEN")

421 

422credential, err := client.Beta.Agents.Vaults.Credentials.New(ctx,

423 vaultId,

424 openai.BetaAgentVaultCredentialNewParams{

425 Name: "Example MCP OAuth credential",

426 Auth: openai.CredentialAuthCreateParamUnion{

427 OfParamMcpOAuth: &openai.CredentialAuthCreateParamMcpOAuth{

428 McpServerURL: mcpUrl,

429 AccessToken: accessToken,

430 ExpiresAt: openai.String(expiresAt),

431 Refresh: openai.CredentialAuthCreateParamMcpOAuthRefresh{

432 TokenEndpoint: tokenEndpoint,

433 ClientID: clientId,

434 RefreshToken: refreshToken,

435 TokenEndpointAuth: openai.McpOAuthTokenEndpointAuthCreateParamUnion{OfParamNone: &openai.McpOAuthTokenEndpointAuthCreateParamNone{}},

436 },

437 },

438 },

439 })

440if err != nil {

441 panic(err)

442}

443```

444 

445```java

446// Replace the illustrative expiry with your access token's actual expiry.

447// Replace the illustrative IDs and URLs below with your own resource values.

448String vaultId = "vault_123";

449String mcpUrl = "https://mcp.example.com/mcp";

450String accessToken = System.getenv("OAUTH_ACCESS_TOKEN");

451String expiresAt = "2030-01-01T00:00:00Z";

452String tokenEndpoint = "https://auth.example.com/oauth/token";

453String clientId = "example-client-id";

454String refreshToken = System.getenv("OAUTH_REFRESH_TOKEN");

455 

456var credential =

457 client

458 .beta()

459 .agents()

460 .vaults()

461 .credentials()

462 .create(

463 CredentialCreateParams.builder()

464 .vaultId(vaultId)

465 .name("Example MCP OAuth credential")

466 .auth(

467 CredentialAuthCreateParam.McpOAuth.builder()

468 .mcpServerUrl(mcpUrl)

469 .accessToken(accessToken)

470 .expiresAt(expiresAt)

471 .refresh(

472 CredentialAuthCreateParam.McpOAuth.Refresh.builder()

473 .tokenEndpoint(tokenEndpoint)

474 .clientId(clientId)

475 .refreshToken(refreshToken)

476 .tokenEndpointAuthNone()

477 .build())

478 .build())

479 .build());

480```

481 

482```ruby

483# Replace the illustrative expiry with your access token's actual expiry.

484# Replace the illustrative IDs and URLs below with your own resource values.

485vault_id = "vault_123"

486mcp_url = "https://mcp.example.com/mcp"

487access_token = ENV.fetch("OAUTH_ACCESS_TOKEN")

488expires_at = "2030-01-01T00:00:00Z"

489token_endpoint = "https://auth.example.com/oauth/token"

490client_id = "example-client-id"

491refresh_token = ENV.fetch("OAUTH_REFRESH_TOKEN")

492 

493credential = client.beta.agents.vaults.credentials.create(

494 vault_id,

495 name: "Example MCP OAuth credential",

496 auth: {

497 type: "mcp_oauth",

498 mcp_server_url: mcp_url,

499 access_token: access_token,

500 expires_at: expires_at,

501 refresh: {

502 token_endpoint: token_endpoint,

503 client_id: client_id,

504 refresh_token: refresh_token,

505 token_endpoint_auth: { type: "none" }

506 }

507 }

508)

509```

510 

511 

512Use the token endpoint authentication method required by your provider. The example uses `none`; `client_secret_basic` and `client_secret_post` are also supported. See the [credential creation reference](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/vaults/subresources/credentials/methods/create) for the fields.

513 

514If an expired token cannot be refreshed, supply a valid replacement. Token expiry does not delete the credential or its vault.

515 

516 

517 

518 

519 

520 

521 

522 

523 

524 

525 

526 

527## Rotate or remove credentials

528 

529[Update a credential](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/vaults/subresources/credentials/methods/update) to replace its token without changing its ID, authentication type, or server URL. For OAuth, use the saved `vault_id` and `credential_id` with the replacement token and expiry:

530 

531Rotate an OAuth token

532 

533```javascript

534// Replace the illustrative expiry with your access token's actual expiry.

535// Replace the illustrative IDs and URLs below with your own resource values.

536const credentialId = "cred_123";

537const vaultId = "vault_123";

538const accessToken = process.env.OAUTH_ACCESS_TOKEN;

539const expiresAt = "2030-01-01T00:00:00Z";

540 

541const credential = await client.beta.agents.vaults.credentials.update(

542 credentialId,

543 {

544 vault_id: vaultId,

545 ...{

546 auth: {

547 type: "mcp_oauth",

548 access_token: accessToken,

549 expires_at: expiresAt,

550 },

551 },

552 }

553);

554```

555 

556```python

557# Replace the illustrative expiry with your access token's actual expiry.

558# Replace the illustrative IDs and URLs below with your own resource values.

559credential_id = "cred_123"

560vault_id = "vault_123"

561access_token = os.environ["OAUTH_ACCESS_TOKEN"]

562expires_at = "2030-01-01T00:00:00Z"

563 

564credential = client.beta.agents.vaults.credentials.update(

565 credential_id,

566 vault_id=vault_id,

567 auth={

568 "type": "mcp_oauth",

569 "access_token": access_token,

570 "expires_at": expires_at,

571 },

572)

573```

574 

575```go

576// Replace the illustrative expiry with your access token's actual expiry.

577// Replace the illustrative IDs and URLs below with your own resource values.

578vaultId := "vault_123"

579credentialId := "cred_123"

580accessToken := os.Getenv("OAUTH_ACCESS_TOKEN")

581expiresAt := "2030-01-01T00:00:00Z"

582 

583credential, err := client.Beta.Agents.Vaults.Credentials.Update(ctx,

584 vaultId,

585 credentialId,

586 openai.BetaAgentVaultCredentialUpdateParams{

587 Auth: openai.CredentialAuthRotateParamUnion{

588 OfParamMcpOAuth: &openai.CredentialAuthRotateParamMcpOAuth{

589 AccessToken: openai.String(accessToken),

590 ExpiresAt: openai.String(expiresAt),

591 },

592 },

593 })

594if err != nil {

595 panic(err)

596}

597```

598 

599```java

600// Replace the illustrative expiry with your access token's actual expiry.

601// Replace the illustrative IDs and URLs below with your own resource values.

602String credentialId = "cred_123";

603String vaultId = "vault_123";

604String accessToken = System.getenv("OAUTH_ACCESS_TOKEN");

605String expiresAt = "2030-01-01T00:00:00Z";

606 

607var credential =

608 client

609 .beta()

610 .agents()

611 .vaults()

612 .credentials()

613 .update(

614 CredentialUpdateParams.builder()

615 .credentialId(credentialId)

616 .vaultId(vaultId)

617 .auth(

618 CredentialAuthRotateParam.McpOAuth.builder()

619 .accessToken(accessToken)

620 .expiresAt(expiresAt)

621 .build())

622 .build());

623```

624 

625```ruby

626# Replace the illustrative expiry with your access token's actual expiry.

627# Replace the illustrative IDs and URLs below with your own resource values.

628credential_id = "cred_123"

629vault_id = "vault_123"

630access_token = ENV.fetch("OAUTH_ACCESS_TOKEN")

631expires_at = "2030-01-01T00:00:00Z"

632 

633credential = client.beta.agents.vaults.credentials.update(

634 credential_id,

635 vault_id: vault_id,

636 auth: {

637 type: "mcp_oauth",

638 access_token: access_token,

639 expires_at: expires_at

640 }

641)

642```

643 

644 

645Include `expires_at` when the replacement token expires. Supplying a new access token without an expiry clears the stored expiry; an explicit `null` also clears it.

646 

647[Delete a credential](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/vaults/subresources/credentials/methods/delete) when you no longer need it. [Delete a vault](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/vaults/methods/delete) to remove the vault and all its credentials.

648 

649Deleting stored credentials does not revoke the original tokens with their providers or stop a running session. Your application handles provider-side revocation and [session cancellation](https://developers.openai.com/api/docs/guides/agents-api/sessions#cancel-an-active-turn).

guides/agents-api/tracing.md +159 −0 created

Details

1# Tracing

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5A **session** keeps your agent's conversation and work together. A session can contain several **turns**, each a cycle of work. A **trace** shows the steps within one turn: model responses, tool calls, and work delegated to other agents.

6 

7The [tracing dashboard](https://platform.openai.com/logs?api=agents) shows what your agent did, including each step's recorded inputs, outputs, duration, and status.

8 

9For session status, live events, saved output, and usage through the API, start with [Observability](https://developers.openai.com/api/docs/guides/agents-api/observability).

10 

11Tracing is enabled by default for new sessions. The public beta API does not expose tracing configuration or external trace exporters.

12 

13## Open a trace

14 

151. Open [Logs → Agents](https://platform.openai.com/logs?api=agents) and select the project where you ran your agent.

162. Find your session with **Search logs**. Use **Add filter** to filter by model, status, or date.

173. Select the session to open its timeline and list of turns.

184. Expand a turn, then select a step in the timeline or event list to see its details.

19 

20The session summary shows its status, model, start time, last activity, number of turns, and recorded token usage.

21 

22## Read a trace

23 

24Start with the session, then work your way into a turn:

25 

261. **Session:** each entry in Logs → Agents is a session. Open it to see its timeline and list of turns. For example, a user can ask about an order, then ask a follow-up question in the same session.

272. **Turn:** expand a turn to see the work done during that cycle. One turn can include several model responses and tool calls. A follow-up message sent after the turn finishes starts another turn in the same session.

283. **Steps within the turn:** the trace groups model responses and tool calls under the root agent or the subagent that performed them. Each recorded step is called a **span**.

29 

30Select a span to see its status, duration, start and end times, and recorded data:

31 

32| Select | What you can inspect |

33| ------------------------------------------------ | ------------------------------------------------------------------------------ |

34| [**Agent**](#agent) | The agent's details, instructions, and recorded token usage |

35| [**Generation**](#generation) (a model response) | The recorded input and output for a model response |

36| [**Tool**](#tool) | Which tool was called, the arguments sent to it, and the result when available |

37 

38### Agent

39 

40An agent span groups the work done by the **root agent** or a **subagent**: another agent asked to handle part of the task. Model responses and tool calls appear under the agent that performed them.

41 

42The details panel shows:

43 

44- **Agent type:** root agent (`root`) or subagent (`subagent`).

45- **Agent:** its ID, name, model, and instructions when recorded.

46- **Usage:** that agent's recorded token counts. These counts cover the agent itself; they do not include its subagents.

47- **Duration** and **Outcome status:** how long the recorded work took and whether it completed, failed, or is incomplete.

48 

49### Generation

50 

51A generation span groups recorded model inputs and outputs. Each turn can have several generations.

52 

53During model inference, the model reads its input and produces a response. That response can request a tool. After the tool returns, the model can produce another response in a new generation.

54 

55- **Input:** recorded inputs associated with that response, such as a user message or a tool result.

56- **Output:** recorded items produced by the model, such as answer text or a tool call.

57- **Model:** the model used for the response, when recorded.

58 

59### Tool

60 

61A tool span describes a tool call and its recorded result.

62 

63Tool spans include calls to your functions and to tools on **MCP (Model Context Protocol)** servers. Web searches and command execution can also appear as tool spans.

64 

65- **Call:** the tool request, including the tool name and arguments when present.

66- **Result:** the recorded response from the tool, when available.

67- **Outcome status** and **Error:** the recorded outcome and error details, when present.

68 

69For an MCP tool call, **Call** contains the server label (`server_label`), tool name (`name`), and arguments (`arguments`). Its response and error are recorded there as `output` and `error`, when available. The separate **Result** panel can be empty because the MCP response is stored in **Call**.

70 

71## Timing and status

72 

73The timeline shows the order of the steps and which ones overlap. **Zoom in** shows shorter steps in more detail. **Fit timeline** shows the whole session.

74 

75Each span shows its duration and outcome status. Failed spans can also include recorded error details.

76 

77An agent span's duration includes its child steps. Steps can overlap: two subagents running together for 10 seconds cover about 10 seconds of elapsed time.

78 

79## Token usage

80 

81**Tokens** in the session summary shows session usage. **Usage** in an agent span shows that agent's recorded token counts.

82 

83Usage can arrive after the turn ends. A blank value or `null` means the count is unknown. It does not mean the agent used zero tokens. Counts can change as more usage becomes available and are not a final bill.

84 

85## When traces are ready

86 

87Traces are built after a turn ends. The agent's answer can appear before its trace or token usage is ready.

88 

89[Live session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events) show progress while the agent is still working.

90 

91## Example: One turn with two subagents

92 

93This example is based on a recorded session. The root agent calls an MCP tool while two subagents run a command and fetch documents. The subagent names are simplified below; the counts and durations come from the recorded trace.

94 

95### Session and turn

96 

97The session header shows **1 turn**, **10 tool calls**, and **252,468 tokens**. The session status is **Idle**, and **Turn 1** is **Completed** with a duration of **1m 37s**.

98 

99Expanding the turn reveals the root agent and its child steps. The trace contains **3 agent spans** (the root and two subagents), **11 generation spans**, and **10 tool spans**.

100 

101This tree groups repeated generations and tool calls together. It shows parent relationships; the timeline shows when each step ran.

102 

103```text

104Session: Idle

105└── Turn 1: Completed 1m 37s

106 └── Root agent 1m 37s

107 ├── 6 generations

108 ├── 2 tools: spawn_agent_call

109 ├── Subagent A 24s

110 │ ├── 2 generations

111 │ └── Tool: command_execution 2s

112 ├── Subagent B 21s

113 │ ├── 3 generations

114 │ ├── 2 tools: notion.fetch 2s each

115 │ └── Tool: send_input_call 0ms

116 ├── Tool: demo_capability_probe 87ms

117 └── 3 tools: wait_for_agents_call

118```

119 

120### Model work and delegation

121 

122The root agent's first **Generation** includes the user's message in **Input**. Its **Output** contains messages and two `spawn_agent_call` items. Those calls also appear as **Tool** spans, and the resulting subagents appear as **Agent** spans under the root.

123 

124Subagent A has its own generations and a `command_execution` tool call. Subagent B has three generations, two `notion.fetch` MCP calls, and a `send_input_call`. Their model responses and tools belong to their respective subagent spans.

125 

126The root agent also has three `wait_for_agents_call` tool spans. Its final generation contains a message and has a recorded duration of **6s**.

127 

128### An MCP tool call

129 

130The root agent's `demo_capability_probe` span is a completed **Tool** span with a duration of **87ms**. Its **Tool type** is `mcp_call`.

131 

132The **Call** panel includes these fields:

133 

134```json

135{

136 "type": "mcp_call",

137 "server_label": "demo_local",

138 "name": "demo_capability_probe",

139 "status": "completed"

140}

141```

142 

143This excerpt shows part of the recorded call. The same panel contains its `arguments` and the MCP response in `output`. The separate **Result** panel is `null`. The span's **Parent span** points to the root agent.

144 

145Subagent B's two `notion.fetch` spans have the same structure: `mcp_call` as the tool type, the MCP response in **Call**, and the subagent as their parent.

146 

147### Timing and usage in this session

148 

149The two subagent spans overlap on the timeline. Subagent A takes **24s** and Subagent B takes **21s**, both within the root agent's **1m 37s** span. The dashboard rounds these displayed durations.

150 

151The **Usage** panel on each agent span shows its own recorded token counts:

152 

153| Agent | Input tokens | Output tokens | Total tokens |

154| ---------- | ------------ | ------------- | ------------ |

155| Root agent | 126,390 | 1,567 | 127,957 |

156| Subagent A | 34,075 | 465 | 34,540 |

157| Subagent B | 89,304 | 667 | 89,971 |

158 

159In this recorded session, the three agent totals add up to the **252,468 tokens** shown in the session header.

Details

14Sandbox agents are available in the TypeScript and Python Agents SDKs. They14Sandbox agents are available in the TypeScript and Python Agents SDKs. They

15 are in beta, so API details, defaults, and supported capabilities may change.15 are in beta, so API details, defaults, and supported capabilities may change.

16 16 

17This guide covers sandboxes in the Agents SDK, where your application runs the harness. For an OpenAI-managed harness, use [Agents API: Connect a sandbox](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted).

18 

17Use sandboxes when the agent needs to manipulate files, run commands, mount a19Use sandboxes when the agent needs to manipulate files, run commands, mount a

18data room, produce artifacts, expose a service, or continue stateful work20data room, produce artifacts, expose a service, or continue stateful work

19later.21later.

guides/agents/sdk.md +66 −0 created

Details

1# Agents SDK

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5Agents can plan and complete tasks using tools, work with other agents, and maintain context across steps.

6 

7## Get your first agent running

8 

9Start with the [Agents SDK quickstart](https://developers.openai.com/api/docs/guides/agents/quickstart) to install the SDK, define one agent, and run it. Once that works, return here to choose the next capability your application needs.

10 

11## Get the Agents SDK

12 

13Use the GitHub repositories for more examples, issues, and language-specific reference details.

14 

15 

16 

17 [TypeScript SDK

18 

19 

20 

21 Open the TypeScript SDK repository on GitHub.](https://github.com/openai/openai-agents-js)

22 [Python SDK

23 

24 

25 

26 Open the Python SDK repository on GitHub.](https://github.com/openai/openai-agents-python)

27 

28 

29 

30## Choose your starting point

31 

32| If you want to | Start here | Why |

33| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- |

34| Build a code-first agent app | [Quickstart](https://developers.openai.com/api/docs/guides/agents/quickstart) | This is the shortest path to a working SDK integration. |

35| Define one specialist cleanly | [Agent definitions](https://developers.openai.com/api/docs/guides/agents/define-agents) | Start here when you are still shaping the contract for a single agent. |

36| Choose models, defaults, and transport | [Models and providers](https://developers.openai.com/api/docs/guides/agents/models) | Use this when model choice, provider setup, or transport strategy affects the workflow. |

37| Understand the runtime loop and state | [Running agents](https://developers.openai.com/api/docs/guides/agents/running-agents) | This is where the agent loop, streaming, and continuation strategies live. |

38| Run work in a container-based environment | [Sandbox agents](https://developers.openai.com/api/docs/guides/agents/sandboxes) | Use this when the agent needs files, commands, packages, snapshots, mounts, or provider links. |

39| Design specialist ownership | [Orchestration and handoffs](https://developers.openai.com/api/docs/guides/agents/orchestration) | Use this when you need more than one agent and must decide who owns the reply. |

40| Add validation or human review | [Guardrails and human review](https://developers.openai.com/api/docs/guides/agents/guardrails-approvals) | Use this when the workflow should block or pause before risky work continues. |

41| Understand what a run returns | [Results and state](https://developers.openai.com/api/docs/guides/agents/results) | This page explains final output, resumable state, and next-turn surfaces. |

42| Add hosted tools, function tools, or MCP | [Using tools](https://developers.openai.com/api/docs/guides/tools#usage-in-the-agents-sdk) and [Integrations and observability](https://developers.openai.com/api/docs/guides/agents/integrations-observability) | Tool semantics live in the platform tools docs; SDK-specific MCP and tracing live here. |

43| Inspect and improve runs | [Integrations and observability](https://developers.openai.com/api/docs/guides/agents/integrations-observability) and [evaluate agent workflows](https://developers.openai.com/api/docs/guides/agent-evals) | Use traces for debugging first, then move into evaluation loops. |

44| Build a voice-first workflow | [Voice agents](https://developers.openai.com/api/docs/guides/voice-agents) | Use the SDK voice pipeline and realtime agent patterns. |

45 

46## Build with the SDK

47 

48Use the SDK track when your server owns deployment, tool implementations, state storage, and approval decisions, while the SDK runs the agent loop and invokes those tools. That path is the best fit when you want:

49 

50- typed application code in TypeScript or Python

51- direct control over tools, MCP servers, and runtime behavior

52- custom storage or server-managed conversation strategies

53- tight integration with existing product logic or infrastructure

54 

55A typical SDK reading order is:

56 

57- Start with [Quickstart](https://developers.openai.com/api/docs/guides/agents/quickstart) to get one working run on screen.

58- Use [Agent definitions](https://developers.openai.com/api/docs/guides/agents/define-agents) and [Models and providers](https://developers.openai.com/api/docs/guides/agents/models) to shape one specialist cleanly.

59- Continue to [Running agents](https://developers.openai.com/api/docs/guides/agents/running-agents), [Orchestration and handoffs](https://developers.openai.com/api/docs/guides/agents/orchestration), and [Guardrails and human review](https://developers.openai.com/api/docs/guides/agents/guardrails-approvals) as the workflow grows more complex.

60- Use [Results and state](https://developers.openai.com/api/docs/guides/agents/results) and [Integrations and observability](https://developers.openai.com/api/docs/guides/agents/integrations-observability) when application logic depends on the run object or deeper visibility into behavior.

61 

62<a id="compare-agent-runtimes"></a>

63 

64## Compare agent runtime options

65 

66Use the [Agents overview](https://developers.openai.com/api/docs/guides/agents#compare-agent-runtimes) to compare the Agents SDK, Agents API, and Responses API. The Agents SDK runs in your application; the Agents API runs a managed harness in OpenAI's service.

Details

417 # Demo data. Replace this function with your weather service.417 # Demo data. Replace this function with your weather service.

418 raise "No demo weather snapshot for #{city}" unless city == "Paris"418 raise "No demo weather snapshot for #{city}" unless city == "Paris"

419 419 

420 {city: city, temperature_c: 22, condition: "Clear", source: "demo weather snapshot"}420 {

421 city: city,

422 temperature_c: 22,

423 condition: "Clear",

424 source: "demo weather snapshot"

425 }

421end426end

422 427 

423client = OpenAI::Client.new428client = OpenAI::Client.new

424tools = [OpenAI::Models::Responses::FunctionTool.new(429tools = [

430 OpenAI::Models::Responses::FunctionTool.new(

425 name: "get_weather",431 name: "get_weather",

426 description: "Read the demo weather snapshot for a city.",432 description: "Read the demo weather snapshot for a city.",

427 async: true,433 async: true,

428 strict: true,434 strict: true,

429 parameters: {435 parameters: {

430 type: "object",436 type: "object",

431 properties: {city: {type: "string"}},437 properties: { city: { type: "string" } },

432 required: ["city"],438 required: ["city"],

433 additionalProperties: false439 additionalProperties: false

434 }440 }

435)]441 )

442]

436instructions = "Start the weather lookup and answer the independent packing question " \443instructions = "Start the weather lookup and answer the independent packing question " \

437 "without waiting. Use the actual tool result when it arrives; never invent it. " \444 "without waiting. Use the actual tool result when it arrives; never invent it. " \

438 "Identify the weather as demo data."445 "Identify the weather as demo data."


456 # Independent work or conversation turns can happen here.464 # Independent work or conversation turns can happen here.

457 # Update latest_response_id after each continuation.465 # Update latest_response_id after each continuation.

458 job.value466 job.value

459else467 else

460 get_weather(city)468 get_weather(city)

461end469 end

462response = client.responses.create(470response = client.responses.create(

463 model: "gpt-6-astra",471 model: "gpt-6-astra",

464 tools: tools,472 tools: tools,

465 instructions: instructions,473 instructions: instructions,

466 previous_response_id: latest_response_id,474 previous_response_id: latest_response_id,

467 input: [OpenAI::Models::Responses::ResponseInputItem::FunctionCallOutput.new(475 input: [

476 OpenAI::Models::Responses::ResponseInputItem::FunctionCallOutput.new(

468 call_id: call.call_id,477 call_id: call.call_id,

469 output: JSON.generate(result)478 output: JSON.generate(result)

470 )]479 )

480 ]

471)481)

472puts(response.output_text)482puts(response.output_text)

473```483```

Details

168client = OpenAI::Client.new168client = OpenAI::Client.new

169completion = client.chat.completions.create(169completion = client.chat.completions.create(

170 model: "gpt-audio-1.5",170 model: "gpt-audio-1.5",

171 messages: [{role: :user, content: "Is a golden retriever a good family dog?"}],171 messages: [

172 {

173 role: :user,

174 content: "Is a golden retriever a good family dog?"

175 }

176 ],

172 modalities: [:text, :audio],177 modalities: [:text, :audio],

173 audio: {voice: :alloy, format: :wav},178 audio: {

179 voice: :alloy,

180 format: :wav

181 },

174 store: true182 store: true

175)183)

176 184 


412audio = Base64.strict_encode64(File.binread("audio.wav"))420audio = Base64.strict_encode64(File.binread("audio.wav"))

413completion = client.chat.completions.create(421completion = client.chat.completions.create(

414 model: "gpt-audio-1.5",422 model: "gpt-audio-1.5",

415 messages: [{423 messages: [

424 {

416 role: :user,425 role: :user,

417 content: [426 content: [

418 {type: :text, text: "What is in this recording?"},427 {

419 {type: :input_audio, input_audio: {data: audio, format: :wav}}428 type: :text,

429 text: "What is in this recording?"

430 },

431 {

432 type: :input_audio,

433 input_audio: {

434 data: audio,

435 format: :wav

436 }

437 }

420 ]438 ]

421 }],439 }

440 ],

422 modalities: [:text, :audio],441 modalities: [:text, :audio],

423 audio: {voice: :alloy, format: :wav},442 audio: {

443 voice: :alloy,

444 format: :wav

445 },

424 store: true446 store: true

425)447)

426 448 

Details

381string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;381string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

382ResponsesClient client = new(key);382ResponsesClient client = new(key);

383 383 

384// Replace this illustrative ID with the background response to cancel.

384string responseId = "resp_123";385string responseId = "resp_123";

385 386 

386ResponseResult response = await client.CancelResponseAsync(responseId);387ResponseResult response = await client.CancelResponseAsync(responseId);

guides/batch.md +4 −4

Details

431```431```

432 432 

433```python433```python

434import os434# Replace the illustrative IDs and URLs below with your own resource values.

435 435 

436from openai import OpenAI436from openai import OpenAI

437 437 

438output_file_id = os.environ["OPENAI_BATCH_OUTPUT_FILE_ID"]438output_file_id = "file_123"

439client = OpenAI()439client = OpenAI()

440 440 

441file_response = client.files.content(output_file_id)441file_response = client.files.content(output_file_id)


536```536```

537 537 

538```python538```python

539import os539# Replace the illustrative IDs and URLs below with your own resource values.

540 540 

541from openai import OpenAI541from openai import OpenAI

542 542 

543batch_id = os.environ["OPENAI_BATCH_ID"]543batch_id = "batch_123"

544client = OpenAI()544client = OpenAI()

545 545 

546client.batches.cancel(batch_id)546client.batches.cancel(batch_id)

Details

55 This example starts a service that creates a ChatKit session through the OpenAI API and returns the session's client secret:55 This example starts a service that creates a ChatKit session through the OpenAI API and returns the session's client secret:

56 56 

57```python57```python

58# Replace the illustrative IDs and URLs below with your own resource values.

58import hmac59import hmac

59import json60import json

60import os61import os


67 68 

68 69 

69api_key = os.environ["OPENAI_API_KEY"]70api_key = os.environ["OPENAI_API_KEY"]

70workflow_id = os.environ["OPENAI_CHATKIT_WORKFLOW_ID"]71workflow_id = "wf_123"

71authenticated_users: dict[str, str] = json.loads(72authenticated_users: dict[str, str] = json.loads(

72 os.environ["CHATKIT_AUTHENTICATED_USERS"]73 os.environ["CHATKIT_AUTHENTICATED_USERS"]

73)74)


117```118```

118 119 

119```ruby120```ruby

121# Replace the illustrative IDs and URLs below with your own resource values.

120require "json"122require "json"

121require "net/http"123require "net/http"

122require "openssl"124require "openssl"

123require "webrick"125require "webrick"

124 126 

125api_key = ENV.fetch("OPENAI_API_KEY")127api_key = ENV.fetch("OPENAI_API_KEY")

126workflow_id = ENV.fetch("OPENAI_CHATKIT_WORKFLOW_ID")128workflow_id = "wf_123"

127# Demo authentication mapping. Replace this with your application's session authentication.129# Demo authentication mapping. Replace this with your application's session authentication.

128authenticated_users = JSON.parse(ENV.fetch("CHATKIT_AUTHENTICATED_USERS"))130authenticated_users = JSON.parse(ENV.fetch("CHATKIT_AUTHENTICATED_USERS"))

129server = WEBrick::HTTPServer.new(131server = WEBrick::HTTPServer.new(


153 upstream["Authorization"] = "Bearer #{api_key}"155 upstream["Authorization"] = "Bearer #{api_key}"

154 upstream["Content-Type"] = "application/json"156 upstream["Content-Type"] = "application/json"

155 upstream["OpenAI-Beta"] = "chatkit_beta=v1"157 upstream["OpenAI-Beta"] = "chatkit_beta=v1"

156 upstream.body = JSON.generate(workflow: {id: workflow_id}, user: user.fetch(1))158 upstream.body = JSON.generate(workflow: { id: workflow_id }, user: user.fetch(1))

157 begin159 begin

158 result = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true, open_timeout: 10, read_timeout: 30) do |http|160 result = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true, open_timeout: 10, read_timeout: 30) do |http|

159 http.request(upstream)161 http.request(upstream)


177 180 

178 For Ruby, install WEBrick with `gem install webrick`.181 For Ruby, install WEBrick with `gem install webrick`.

179 182 

180 Before starting the service, set `OPENAI_API_KEY`, `OPENAI_CHATKIT_WORKFLOW_ID`, and `CHATKIT_AUTHENTICATED_USERS`. The last value is a JSON map from your application's bearer tokens to stable user IDs. In production, replace this environment-backed map with your application's authentication or session lookup.183 Before starting the service, replace `wf_123` with your workflow ID and set `OPENAI_API_KEY` and `CHATKIT_AUTHENTICATED_USERS`. The latter value is a JSON map from your application's bearer tokens to stable user IDs. In production, replace this environment-backed map with your application's authentication or session lookup.

181 184 

1822. In your server-side code, pass in your workflow ID and secret key to the session endpoint.1852. In your server-side code, pass in your workflow ID and secret key to the session endpoint.

183 186 

Details

174response = client.responses.create(174response = client.responses.create(

175 model: "gpt-6-astra",175 model: "gpt-6-astra",

176 input: "Find the null pointer exception in this code:\n\n#{code}",176 input: "Find the null pointer exception in this code:\n\n#{code}",

177 reasoning: {effort: :high}177 reasoning: { effort: :high }

178)178)

179 179 

180puts(response.output_text)180puts(response.output_text)

Details

220require "openai"220require "openai"

221 221 

222client = OpenAI::Client.new222client = OpenAI::Client.new

223conversation = [{223conversation = [

224 {

224 type: :message,225 type: :message,

225 role: :user,226 role: :user,

226 content: "Let's begin a long coding task."227 content: "Let's begin a long coding task."

227}]228 }

229]

228 230 

229response = client.responses.create(231response = client.responses.create(

230 model: "gpt-5.3-codex",232 model: "gpt-5.3-codex",

231 input: conversation,233 input: conversation,

232 store: false,234 store: false,

233 context_management: [{type: :compaction, compact_threshold: 200_000}]235 context_management: [

236 {

237 type: :compaction,

238 compact_threshold: 200_000

239 }

240 ]

234)241)

235conversation.concat(response.output)242conversation.concat(response.output)

236conversation << {243conversation << {


242 model: "gpt-5.3-codex",249 model: "gpt-5.3-codex",

243 input: conversation,250 input: conversation,

244 store: false,251 store: false,

245 context_management: [{type: :compaction, compact_threshold: 200_000}]252 context_management: [

253 {

254 type: :compaction,

255 compact_threshold: 200_000

256 }

257 ]

246)258)

247puts(next_response.output_text)259puts(next_response.output_text)

248```260```


461require "openai"473require "openai"

462 474 

463client = OpenAI::Client.new475client = OpenAI::Client.new

464long_input = [{role: :user, content: "Plan a trip to Kyoto."}]476long_input = [

477 {

478 role: :user,

479 content: "Plan a trip to Kyoto."

480 }

481]

465compaction = client.responses.compact(482compaction = client.responses.compact(

466 model: "gpt-6-astra",483 model: "gpt-6-astra",

467 input: long_input484 input: long_input

468)485)

469next_input = [486next_input = [

470 *compaction.output,487 *compaction.output,

471 {type: :message, role: :user, content: "Add restaurant recommendations."}488 {

489 type: :message,

490 role: :user,

491 content: "Add restaurant recommendations."

492 }

472]493]

473response = client.responses.create(494response = client.responses.create(

474 model: "gpt-6-astra",495 model: "gpt-6-astra",

Details

149response = client.responses.create(149response = client.responses.create(

150 model: "gpt-6-astra",150 model: "gpt-6-astra",

151 input: [151 input: [

152 {role: :user, content: "Knock knock."},152 {

153 {role: :assistant, content: "Who's there?"},153 role: :user,

154 {role: :user, content: "Orange."}154 content: "Knock knock."

155 },

156 {

157 role: :assistant,

158 content: "Who's there?"

159 },

160 {

161 role: :user,

162 content: "Orange."

163 }

155 ]164 ]

156)165)

157 166 


175 184 

176```javascript185```javascript

177import OpenAI from "openai";186import OpenAI from "openai";

187import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";

178 188 

179const openai = new OpenAI();189const openai = new OpenAI();

180 190 


194 204 

195console.log(response.output_text);205console.log(response.output_text);

196 206 

197// Add all response output items, including reasoning items, to the history207// Add replayable output items, including reasoning items, to the history

198history.push(...response.output);208history.push(...toResponseInputItems(response.output));

199 209 

200history.push({210history.push({

201 role: "user",211 role: "user",


392require "openai"402require "openai"

393 403 

394client = OpenAI::Client.new404client = OpenAI::Client.new

395history = [{role: :user, content: "Tell me a joke."}]405history = [

406 {

407 role: :user,

408 content: "Tell me a joke."

409 }

410]

396 411 

397first = client.responses.create(412first = client.responses.create(

398 model: "gpt-6-astra",413 model: "gpt-6-astra",


402puts(first.output_text)417puts(first.output_text)

403 418 

404history.concat(first.output)419history.concat(first.output)

405history << {role: :user, content: "Tell me another."}420history << {

421 role: :user,

422 content: "Tell me another."

423}

406 424 

407second = client.responses.create(425second = client.responses.create(

408 model: "gpt-6-astra",426 model: "gpt-6-astra",

Details

122 122 

123**Realtime API example**123**Realtime API example**

124 124 

125For Ruby, set `OPENAI_VOICE_ID` to your custom voice ID before running the example.125For Ruby, replace `voice_123` with your custom voice ID before running the example.

126 126 

127```javascript127```javascript

128const sessionConfig = JSON.stringify({128const sessionConfig = JSON.stringify({


139```139```

140 140 

141```ruby141```ruby

142# Replace the illustrative IDs and URLs below with your own resource values.

142require "json"143require "json"

143 144 

144session_config = JSON.generate(145session_config = JSON.generate(

145 session: {146 session: {

146 type: "realtime",147 type: "realtime",

147 model: "gpt-realtime-2",148 model: "gpt-realtime-2",

148 audio: {output: {voice: {id: ENV.fetch("OPENAI_VOICE_ID")}}}149 audio: { output: { voice: { id: "voice_123" } } }

149 }150 }

150)151)

151puts(session_config)152puts(session_config)

Details

185 BackgroundModeEnabled = true,185 BackgroundModeEnabled = true,

186};186};

187options.Tools.Add(ResponseTool.CreateWebSearchPreviewTool());187options.Tools.Add(ResponseTool.CreateWebSearchPreviewTool());

188string vectorStoreId = Environment.GetEnvironmentVariable("OPENAI_EXAMPLE_VECTOR_STORE_ID")188// Replace this illustrative value with your research data source.

189 ?? throw new InvalidOperationException("Set OPENAI_EXAMPLE_VECTOR_STORE_ID to search your research documents.");189string vectorStoreId = "vs_123";

190options.Tools.Add(ResponseTool.CreateFileSearchTool([vectorStoreId]));190options.Tools.Add(ResponseTool.CreateFileSearchTool([vectorStoreId]));

191options.Tools.Add(ResponseTool.CreateCodeInterpreterTool(container));191options.Tools.Add(ResponseTool.CreateCodeInterpreterTool(container));

192options.InputItems.Add(192options.InputItems.Add(


220```220```

221 221 

222```ruby222```ruby

223# Replace the illustrative IDs and URLs below with your own resource values.

223require "openai"224require "openai"

224 225 

225client = OpenAI::Client.new226client = OpenAI::Client.new

226vector_store_id = ENV.fetch("OPENAI_VECTOR_STORE_ID")227vector_store_id = "vs_123"

227response = client.responses.create(228response = client.responses.create(

228 model: "o3-deep-research",229 model: "o3-deep-research",

229 input: "Research the economic impact of semaglutide on global healthcare systems. Include measurable outcomes and cite primary sources.",230 input: "Research the economic impact of semaglutide on global healthcare systems. Include measurable outcomes and cite primary sources.",

230 tools: [231 tools: [

231 {type: :web_search_preview},232 { type: :web_search_preview },

232 {type: :file_search, vector_store_ids: [vector_store_id]},233 {

233 {type: :code_interpreter, container: {type: :auto}}234 type: :file_search,

235 vector_store_ids: [vector_store_id]

236 },

237 {

238 type: :code_interpreter,

239 container: { type: :auto }

240 }

234 ],241 ],

235 background: true242 background: true

236)243)


1064```1071```

1065 1072 

1066```java1073```java

1074// Replace the illustrative IDs and URLs below with your own resource values.

1067import com.openai.client.OpenAIClient;1075import com.openai.client.OpenAIClient;

1068import com.openai.client.okhttp.OpenAIOkHttpClient;1076import com.openai.client.okhttp.OpenAIOkHttpClient;

1069import com.openai.models.Reasoning;1077import com.openai.models.Reasoning;


1081 .addTool(1089 .addTool(

1082 Tool.Mcp.builder()1090 Tool.Mcp.builder()

1083 .serverLabel("mycompany_mcp_server")1091 .serverLabel("mycompany_mcp_server")

1084 .serverUrl(System.getenv("OPENAI_MCP_SERVER_URL"))1092 .serverUrl("https://mcp.example.com/mcp")

1085 .requireApproval(Tool.Mcp.RequireApproval.McpToolApprovalSetting.NEVER)1093 .requireApproval(Tool.Mcp.RequireApproval.McpToolApprovalSetting.NEVER)

1086 .build())1094 .build())

1087 .build();1095 .build();


1121 ReasoningSummaryVerbosity = ResponseReasoningSummaryVerbosity.Auto,1129 ReasoningSummaryVerbosity = ResponseReasoningSummaryVerbosity.Auto,

1122 },1130 },

1123};1131};

1124string serverUrl = Environment.GetEnvironmentVariable("OPENAI_MCP_SERVER_URL")1132// Replace this illustrative value with your research data source.

1125 ?? throw new InvalidOperationException("Set OPENAI_MCP_SERVER_URL to connect your research data source.");1133string serverUrl = "https://mcp.example.com/mcp";

1126options.Tools.Add(1134options.Tools.Add(

1127 ResponseTool.CreateMcpTool(1135 ResponseTool.CreateMcpTool(

1128 "mycompany_mcp_server",1136 "mycompany_mcp_server",


1150```1158```

1151 1159 

1152```ruby1160```ruby

1161# Replace the illustrative IDs and URLs below with your own resource values.

1153require "openai"1162require "openai"

1154 1163 

1155client = OpenAI::Client.new1164client = OpenAI::Client.new

1156mcp_server_url = ENV.fetch("OPENAI_MCP_SERVER_URL")1165mcp_server_url = "https://mcp.example.com/mcp"

1157response = client.responses.create(1166response = client.responses.create(

1158 model: "o3-deep-research",1167 model: "o3-deep-research",

1159 input: "What patterns appear in our closed-lost Salesforce opportunities?",1168 input: "What patterns appear in our closed-lost Salesforce opportunities?",

1160 instructions: "Produce a source-backed deep research report.",1169 instructions: "Produce a source-backed deep research report.",

1161 reasoning: {summary: :auto},1170 reasoning: { summary: :auto },

1162 tools: [{1171 tools: [

1172 {

1163 type: :mcp,1173 type: :mcp,

1164 server_label: "mycompany_mcp_server",1174 server_label: "mycompany_mcp_server",

1165 server_url: mcp_server_url,1175 server_url: mcp_server_url,

1166 require_approval: :never1176 require_approval: :never

1167 }],1177 }

1178 ],

1168 background: true1179 background: true

1169)1180)

1170 1181 

Details

192 192 

193response = client.responses.create(193response = client.responses.create(

194 model: "gpt-6-astra",194 model: "gpt-6-astra",

195 reasoning: {effort: :xhigh, mode: :pro},195 reasoning: {

196 effort: :xhigh,

197 mode: :pro

198 },

196 input: prompt199 input: prompt

197)200)

198 201 


329 332 

330response = client.responses.create(333response = client.responses.create(

331 model: "gpt-6-astra",334 model: "gpt-6-astra",

332 text: {verbosity: :low},335 text: { verbosity: :low },

333 input: incident336 input: incident

334)337)

335 338 


683 strict: true,686 strict: true,

684 parameters: {687 parameters: {

685 type: "object",688 type: "object",

686 properties: {argument => {type: "string"}},689 properties: { argument => { type: "string" } },

687 required: [argument],690 required: [argument],

688 additionalProperties: false691 additionalProperties: false

689 }692 }


711response = client.responses.create(714response = client.responses.create(

712 model: "gpt-6-astra",715 model: "gpt-6-astra",

713 input: "Find the right billing tool and explain why invoice INV-1043 still shows overdue after a payment yesterday.",716 input: "Find the right billing tool and explain why invoice INV-1043 still shows overdue after a payment yesterday.",

714 tools: [billing, crm, {type: :tool_search}]717 tools: [billing, crm, { type: :tool_search }]

715)718)

716 719 

717puts(response.output)720puts(response.output)


830 833 

831```javascript834```javascript

832import OpenAI from "openai";835import OpenAI from "openai";

836import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";

833 837 

834const openai = new OpenAI();838const openai = new OpenAI();

835 839 


846 model: "gpt-6-astra",850 model: "gpt-6-astra",

847 store: false,851 store: false,

848 input: [852 input: [

849 ...compacted.output, // Use compact output as-is.853 // Preserve replayable compacted items.

854 ...toResponseInputItems(compacted.output),

850 {855 {

851 type: "message",856 type: "message",

852 role: "user",857 role: "user",


1234 1239 

1235```javascript1240```javascript

1236import OpenAI from "openai";1241import OpenAI from "openai";

1242import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";

1237 1243 

1238const openai = new OpenAI();1244const openai = new OpenAI();

1239 1245 


1252 input: history,1258 input: history,

1253});1259});

1254 1260 

1255history.push(...first.output);1261history.push(...toResponseInputItems(first.output));

1256history.push({1262history.push({

1257 role: "user",1263 role: "user",

1258 content: "Now write the customer-facing explanation in plain English.",1264 content: "Now write the customer-facing explanation in plain English.",


1442first = client.responses.create(1448first = client.responses.create(

1443 model: "gpt-6-astra",1449 model: "gpt-6-astra",

1444 store: false,1450 store: false,

1445 reasoning: {effort: :medium, context: :current_turn},1451 reasoning: {

1452 effort: :medium,

1453 context: :current_turn

1454 },

1446 include: ["reasoning.encrypted_content"],1455 include: ["reasoning.encrypted_content"],

1447 input: history1456 input: history

1448)1457)


1455second = client.responses.create(1464second = client.responses.create(

1456 model: "gpt-6-astra",1465 model: "gpt-6-astra",

1457 store: false,1466 store: false,

1458 reasoning: {effort: :medium, context: :all_turns},1467 reasoning: {

1468 effort: :medium,

1469 context: :all_turns

1470 },

1459 input: history1471 input: history

1460)1472)

1461 1473 


1503Run and poll a background response1515Run and poll a background response

1504 1516 

1505```javascript1517```javascript

1518// Replace the illustrative IDs and URLs below with your own resource values.

1506import OpenAI from "openai";1519import OpenAI from "openai";

1507 1520 

1508const openai = new OpenAI();1521const openai = new OpenAI();

1522const logBundleFileId = "file_123";

1509 1523 

1510let job = await openai.responses.create({1524let job = await openai.responses.create({

1511 model: "gpt-6-astra",1525 model: "gpt-6-astra",


1532```1546```

1533 1547 

1534```python1548```python

1549# Replace the illustrative IDs and URLs below with your own resource values.

1535from openai import OpenAI1550from openai import OpenAI

1536import time1551import time

1537 1552 

1538client = OpenAI()1553client = OpenAI()

1554log_bundle_file_id = "file_123"

1539 1555 

1540job = client.responses.create(1556job = client.responses.create(

1541 model="gpt-6-astra",1557 model="gpt-6-astra",


1650 tools: [1666 tools: [

1651 {1667 {

1652 type: :code_interpreter,1668 type: :code_interpreter,

1653 container: {type: :auto, file_ids: ["file_abc123"]}1669 container: {

1670 type: :auto,

1671 file_ids: ["file_abc123"]

1672 }

1654 }1673 }

1655 ]1674 ]

1656)1675)


1801end1820end

1802 1821 

1803test_log_tool = {1822test_log_tool = {

1804 type: "function", name: "search_test_logs", description: "Search test logs.",1823 type: "function",

1805 parameters: {type: "object", properties: {query: {type: "string"}}, required: ["query"], additionalProperties: false},1824 name: "search_test_logs",

1825 description: "Search test logs.",

1826 parameters: {

1827 type: "object",

1828 properties: { query: { type: "string" } },

1829 required: ["query"],

1830 additionalProperties: false

1831 },

1806 strict: true1832 strict: true

1807}1833}

1808code_search_tool = {1834code_search_tool = {

1809 type: "function", name: "search_code", description: "Search source code.",1835 type: "function",

1810 parameters: {type: "object", properties: {query: {type: "string"}}, required: ["query"], additionalProperties: false},1836 name: "search_code",

1837 description: "Search source code.",

1838 parameters: {

1839 type: "object",

1840 properties: { query: { type: "string" } },

1841 required: ["query"],

1842 additionalProperties: false

1843 },

1811 strict: true1844 strict: true

1812}1845}

1813 1846 

1814endpoint = Async::HTTP::Endpoint.parse("wss://api.openai.com/v1/responses", timeout: 10, alpn_protocols: ["http/1.1"])1847endpoint = Async::HTTP::Endpoint.parse("wss://api.openai.com/v1/responses", timeout: 10, alpn_protocols: ["http/1.1"])

1815headers = {"Authorization" => "Bearer #{ENV.fetch("OPENAI_API_KEY")}"}1848headers = { "Authorization" => "Bearer #{ENV.fetch("OPENAI_API_KEY")}" }

1816Sync do |task|1849Sync do |task|

1817 task.with_timeout(120) do1850 task.with_timeout(120) do

1818 Async::WebSocket::Client.connect(endpoint, headers: headers) do |connection|1851 Async::WebSocket::Client.connect(endpoint, headers: headers) do |connection|

1819 connection.write(JSON.generate(1852 connection.write(

1853 JSON.generate(

1820 type: "response.create", stream_id: "main", model: "gpt-6-astra", store: false,1854 type: "response.create", stream_id: "main", model: "gpt-6-astra", store: false,

1821 input: [{role: "user", content: "Find the flaky test in this run, call the tools you need, and keep going until you can explain the root cause."}],1855 input: [

1856 {

1857 role: "user",

1858 content: "Find the flaky test in this run, call the tools you need, and keep going until you can explain the root cause."

1859 }

1860 ],

1822 tools: [test_log_tool, code_search_tool]1861 tools: [test_log_tool, code_search_tool]

1823 ))1862 )

1863 )

1824 connection.flush1864 connection.flush

1825 puts(JSON.pretty_generate(wait_for_response(connection).fetch("output")))1865 puts(JSON.pretty_generate(wait_for_response(connection).fetch("output")))

1826 end1866 end

Details

199 training_file: "file-all-about-the-weather",199 training_file: "file-all-about-the-weather",

200 method_: {200 method_: {

201 type: :dpo,201 type: :dpo,

202 dpo: {hyperparameters: {beta: 0.1}}202 dpo: { hyperparameters: { beta: 0.1 } }

203 }203 }

204)204)

205puts(job.id)205puts(job.id)

Details

549 role: :system,549 role: :system,

550 content: "You answer questions about the 2022 Winter Olympics."550 content: "You answer questions about the 2022 Winter Olympics."

551 },551 },

552 {role: :user, content: question}552 {

553 role: :user,

554 content: question

555 }

553 ],556 ],

554 temperature: 0557 temperature: 0

555)558)

guides/evals.md +33 −6

Details

174response = client.responses.create(174response = client.responses.create(

175 model: "gpt-6-astra",175 model: "gpt-6-astra",

176 input: [176 input: [

177 {role: :developer, content: instructions},177 {

178 {role: :user, content: "My monitor won't turn on - help!"}178 role: :developer,

179 content: instructions

180 },

181 {

182 role: :user,

183 content: "My monitor won't turn on - help!"

184 }

179 ]185 ]

180)186)

181puts(response.output_text)187puts(response.output_text)


282client = OpenAI::Client.new288client = OpenAI::Client.new

283evaluation = client.evals.create(289evaluation = client.evals.create(

284 name: "Support answer quality",290 name: "Support answer quality",

285 data_source_config: {type: :custom, item_schema: {type: :object, properties: {input: {type: :string}}, required: ["input"]}},291 data_source_config: {

286 testing_criteria: [{type: :string_check, name: "mentions_refund", input: "{{sample.output_text}}", operation: :contains, reference: "refund"}]292 type: :custom,

293 item_schema: {

294 type: :object,

295 properties: { input: { type: :string } },

296 required: ["input"]

297 }

298 },

299 testing_criteria: [

300 {

301 type: :string_check,

302 name: "mentions_refund",

303 input: "{{sample.output_text}}",

304 operation: :contains,

305 reference: "refund"

306 }

307 ]

287)308)

288puts(evaluation.id)309puts(evaluation.id)

289```310```


605 name: "Categorization text run",626 name: "Categorization text run",

606 data_source: {627 data_source: {

607 type: :responses,628 type: :responses,

608 source: {type: :file_id, id: "YOUR_FILE_ID"},629 source: {

630 type: :file_id,

631 id: "YOUR_FILE_ID"

632 },

609 input_messages: {633 input_messages: {

610 type: :template,634 type: :template,

611 template: [635 template: [


613 role: :developer,637 role: :developer,

614 content: "Categorize the ticket as Hardware, Software, or Other."638 content: "Categorize the ticket as Hardware, Software, or Other."

615 },639 },

616 {role: :user, content: "{{ item.ticket_text }}"}640 {

641 role: :user,

642 content: "{{ item.ticket_text }}"

643 }

617 ]644 ]

618 },645 },

619 model: "gpt-6-astra"646 model: "gpt-6-astra"

Details

524 {524 {

525 role: "user",525 role: "user",

526 content: [526 content: [

527 {type: "input_file", file_id: file.id},527 {

528 {type: "input_text", text: "What is the first dragon in the book?"}528 type: "input_file",

529 file_id: file.id

530 },

531 {

532 type: "input_text",

533 text: "What is the first dragon in the book?"

534 }

529 ]535 ]

530 }536 }

531 ]537 ]


768pdf_data = Base64.strict_encode64(File.binread("draconomicon.pdf"))774pdf_data = Base64.strict_encode64(File.binread("draconomicon.pdf"))

769response = client.responses.create(775response = client.responses.create(

770 model: "gpt-6-astra",776 model: "gpt-6-astra",

771 input: [{777 input: [

778 {

772 role: :user,779 role: :user,

773 content: [780 content: [

774 {781 {


776 filename: "document.pdf",783 filename: "document.pdf",

777 file_data: "data:application/pdf;base64,#{pdf_data}"784 file_data: "data:application/pdf;base64,#{pdf_data}"

778 },785 },

779 {type: :input_text, text: "Summarize this document."}786 {

787 type: :input_text,

788 text: "Summarize this document."

789 }

790 ]

791 }

780 ]792 ]

781 }]

782)793)

783 794 

784puts(response.output_text)795puts(response.output_text)

Details

158 training_file: "file-abc123",158 training_file: "file-abc123",

159 method_: {159 method_: {

160 type: :supervised,160 type: :supervised,

161 supervised: {hyperparameters: {n_epochs: 2}}161 supervised: { hyperparameters: { n_epochs: 2 } }

162 }162 }

163)163)

164puts(job.id)164puts(job.id)

Details

4 4 

5**Function calling** (also known as **tool calling**) provides a powerful and flexible way for OpenAI models to interface with external systems and access data outside their training data. This guide shows how you can connect a model to data and actions provided by your application. We'll show how to use function tools (defined by a JSON schema) and custom tools which work with free form text inputs and outputs.5**Function calling** (also known as **tool calling**) provides a powerful and flexible way for OpenAI models to interface with external systems and access data outside their training data. This guide shows how you can connect a model to data and actions provided by your application. We'll show how to use function tools (defined by a JSON schema) and custom tools which work with free form text inputs and outputs.

6 6 

7For Agents API sessions, use [Functions](https://developers.openai.com/api/docs/guides/agents-api/tools/functions) to register functions and handle session action requests. The examples in this guide show the Responses API and Chat Completions integrations.

8 

7If your application has many functions or large schemas, you can pair function calling with [tool search](https://developers.openai.com/api/docs/guides/tools-tool-search) to defer rarely used tools and load them only when the model needs them. Only `gpt-5.4` and later models support `tool_search`.9If your application has many functions or large schemas, you can pair function calling with [tool search](https://developers.openai.com/api/docs/guides/tools-tool-search) to defer rarely used tools and load them only when the model needs them. Only `gpt-5.4` and later models support `tool_search`.

8 10 

9GPT-6 Astra requires the Responses API for tool calling. The Chat Completions11GPT-6 Astra requires the Responses API for tool calling. The Chat Completions


112 114 

113```javascript115```javascript

114import OpenAI from "openai";116import OpenAI from "openai";

117import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";

115 118 

116const openai = new OpenAI();119const openai = new OpenAI();

117 120 


155});158});

156 159 

157// Preserve model output for the next turn160// Preserve model output for the next turn

158input.push(...response.output);161input.push(...toResponseInputItems(response.output));

159 162 

160for (const item of response.output) {163for (const item of response.output) {

161 if (item.type !== "function_call") continue;164 if (item.type !== "function_call") continue;


421require "openai"424require "openai"

422 425 

423client = OpenAI::Client.new426client = OpenAI::Client.new

424tools = [{427tools = [

428 {

425 type: :function,429 type: :function,

426 name: "get_horoscope",430 name: "get_horoscope",

427 description: "Get today's horoscope for an astrological sign.",431 description: "Get today's horoscope for an astrological sign.",

428 parameters: {432 parameters: {

429 type: :object,433 type: :object,

430 properties: {sign: {type: :string}},434 properties: { sign: { type: :string } },

431 required: ["sign"],435 required: ["sign"],

432 additionalProperties: false436 additionalProperties: false

433 },437 },

434 strict: true438 strict: true

435}]439 }

440]

436 441 

437first_response = client.responses.create(442first_response = client.responses.create(

438 model: "gpt-6-astra",443 model: "gpt-6-astra",


452response = client.responses.create(457response = client.responses.create(

453 model: "gpt-6-astra",458 model: "gpt-6-astra",

454 previous_response_id: first_response.id,459 previous_response_id: first_response.id,

455 input: [{460 input: [

461 {

456 type: :function_call_output,462 type: :function_call_output,

457 call_id: function_call.call_id,463 call_id: function_call.call_id,

458 output: "#{sign}: Embrace an unexpected opportunity today."464 output: "#{sign}: Embrace an unexpected opportunity today."

459 }],465 }

466 ],

460 tools: tools467 tools: tools

461)468)

462 469 


476| Field | Description |483| Field | Description |

477| ------------- | ------------------------------------------------------------------------------- |484| ------------- | ------------------------------------------------------------------------------- |

478| `type` | This should always be `function` |485| `type` | This should always be `function` |

479| `name` | The function's name (e.g. `get_weather`) |486| `name` | The function's name (for example, `get_weather`) |

480| `description` | Details on when and how to use the function |487| `description` | Details on when and how to use the function |

481| `parameters` | [JSON schema](https://json-schema.org/) defining the function's input arguments |488| `parameters` | [JSON schema](https://json-schema.org/) defining the function's input arguments |

482| `strict` | Whether to enforce strict mode for the function call |489| `strict` | Whether to enforce strict mode for the function call |


508}515}

509```516```

510 517 

511Because the `parameters` are defined by a [JSON schema](https://json-schema.org/), you can leverage many of its rich features like property types, enums, descriptions, nested objects, and, recursive objects.518Because the `parameters` are defined by a [JSON schema](https://json-schema.org/), you can leverage many of its rich features like property types, enums, descriptions, nested objects, and recursive objects.

512 519 

513## Defining namespaces520## Defining namespaces

514 521 


566 - **For deferred tools, put detailed guidance in the function description and keep the namespace description concise.** The namespace helps the model choose what to load; the function description helps it use the loaded tool correctly.573 - **For deferred tools, put detailed guidance in the function description and keep the namespace description concise.** The namespace helps the model choose what to load; the function description helps it use the loaded tool correctly.

567 574 

5681. **Apply software engineering best practices.**5751. **Apply software engineering best practices.**

569 - **Make the functions obvious and intuitive**. ([principle of least surprise](https://en.wikipedia.org/wiki/Principle_of_least_astonishment))576 - **Make the functions predictable and intuitive**. ([principle of least surprise](https://en.wikipedia.org/wiki/Principle_of_least_astonishment))

570 - **Use enums** and object structure to make invalid states unrepresentable. (e.g. `toggle_light(on: bool, off: bool)` allows for invalid calls)577 - **Use enums** and object structure to prevent invalid states. For example, `toggle_light(on: bool, off: bool)` allows for invalid calls.

571 - **Pass the intern test.** Can an intern/human correctly use the function given nothing but what you gave the model? (If not, what questions do they ask you? Add the answers to the prompt.)578 - **Pass the intern test.** Can an intern/human correctly use the function given nothing but what you gave the model? (If not, what questions do they ask you? Add the answers to the prompt.)

572 579 

5731. **Offload the burden from the model and use code where possible.**5801. **Offload the burden from the model and use code where possible.**

574 - **Don't make the model fill arguments you already know.** For example, if you already have an `order_id` based on a previous menu, don't have an `order_id` param – instead, have no params `submit_refund()` and pass the `order_id` with code.581 - **Don't make the model fill arguments you already know.** For example, if you already have an `order_id` based on a previous menu, don't include an `order_id` parameter. Instead, define `submit_refund()` with no parameters and pass the `order_id` in your code.

575 - **Combine functions that are always called in sequence.** For example, if you always call `mark_location()` after `query_location()`, just move the marking logic into the query function call.582 - **Combine functions that are always called in sequence.** For example, if you always call `mark_location()` after `query_location()`, just move the marking logic into the query function call.

576 583 

5771. **Keep the number of initially available functions small for higher accuracy.**5841. **Keep the number of initially available functions small for higher accuracy.**


631Execute function calls and append results638Execute function calls and append results

632 639 

633```javascript640```javascript

634input.push(...response.output);641import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";

642 

643input.push(...toResponseInputItems(response.output));

635 644 

636for (const toolCall of response.output) {645for (const toolCall of response.output) {

637 if (toolCall.type !== "function_call") {646 if (toolCall.type !== "function_call") {


825 834 

826For functions that return images or files, you can pass an [array of image or file objects](https://developers.openai.com/api/reference/resources/responses/methods/create#responses_create-input-input_item_list-item-function_tool_call_output-output) instead of a string.835For functions that return images or files, you can pass an [array of image or file objects](https://developers.openai.com/api/reference/resources/responses/methods/create#responses_create-input-input_item_list-item-function_tool_call_output-output) instead of a string.

827 836 

828If your function has no return value (e.g. `send_email`), simply return a string that indicates success or failure. (e.g. `"success"`)837If your function has no return value (for example, `send_email`), return a string that indicates success or failure, such as `"success"`.

829 838 

830### Incorporating results into response839### Incorporating results into response

831 840 


927 936 

928client = OpenAI::Client.new937client = OpenAI::Client.new

929input = [938input = [

930 {role: :user, content: "What is the weather like in Paris?"},939 {

940 role: :user,

941 content: "What is the weather like in Paris?"

942 },

931 {943 {

932 type: :function_call,944 type: :function_call,

933 call_id: "call_weather",945 call_id: "call_weather",


940 output: '{"city":"Paris","temperature_c":18}'952 output: '{"city":"Paris","temperature_c":18}'

941 }953 }

942]954]

943tools = [{955tools = [

956 {

944 type: :function,957 type: :function,

945 name: "get_weather",958 name: "get_weather",

946 description: "Get the weather for a city",959 description: "Get the weather for a city",

947 parameters: {960 parameters: {

948 type: :object,961 type: :object,

949 properties: {city: {type: :string}},962 properties: { city: { type: :string } },

950 required: ["city"],963 required: ["city"],

951 additionalProperties: false964 additionalProperties: false

952 },965 },

953 strict: true966 strict: true

954}]967 }

968]

955response = client.responses.create(969response = client.responses.create(

956 model: "gpt-6-astra",970 model: "gpt-6-astra",

957 input: input,971 input: input,


9841. **Allowed tools:** Restrict the tool calls the model can make to a subset of9981. **Allowed tools:** Restrict the tool calls the model can make to a subset of

985 the tools available to the model.999 the tools available to the model.

986 1000 

987**When to use allowed_tools**1001**When to use `allowed_tools`**

988 1002 

989You might want to configure an `allowed_tools` list in case you want to make only1003You might want to configure an `allowed_tools` list in case you want to make only

990a subset of tools available across model requests, but not modify the list of tools you pass in, so you can maximize savings from [prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching).1004a subset of tools available across model requests, but not modify the list of tools you pass in, so you can maximize savings from [prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching).


1015 1029 

1016**Note:** Currently, if you are using a fine tuned model and the model calls multiple functions in one turn then [strict mode](#strict-mode) will be disabled for those calls.1030**Note:** Currently, if you are using a fine tuned model and the model calls multiple functions in one turn then [strict mode](#strict-mode) will be disabled for those calls.

1017 1031 

1018**Note for `gpt-4.1-nano-2025-04-14`:** This snapshot of `gpt-4.1-nano` can sometimes include multiple tools calls for the same tool if parallel tool calls are enabled. It is recommended to disable this feature when using this nano snapshot.1032**Note for `gpt-4.1-nano-2025-04-14`:** This snapshot of `gpt-4.1-nano` can sometimes include multiple tool calls for the same tool if parallel tool calls are enabled. It is recommended to disable this feature when using this snapshot.

1019 1033 

1020### Strict mode1034### Strict mode

1021 1035 


1299stream = client.responses.stream(1313stream = client.responses.stream(

1300 model: "gpt-6-astra",1314 model: "gpt-6-astra",

1301 input: "What is the weather in Paris?",1315 input: "What is the weather in Paris?",

1302 tools: [{type: :function, name: "get_weather", description: "Get the weather for a city", parameters: {type: :object, properties: {city: {type: :string}}, required: ["city"], additionalProperties: false}, strict: true}]1316 tools: [

1317 {

1318 type: :function,

1319 name: "get_weather",

1320 description: "Get the weather for a city",

1321 parameters: {

1322 type: :object,

1323 properties: { city: { type: :string } },

1324 required: ["city"],

1325 additionalProperties: false

1326 },

1327 strict: true

1328 }

1329 ]

1303)1330)

1304 1331 

1305stream.each { |event| puts(event.type) }1332stream.each { |event| puts(event.type) }


1498stream = client.responses.stream(1525stream = client.responses.stream(

1499 model: "gpt-6-astra",1526 model: "gpt-6-astra",

1500 input: "What is the weather in Paris?",1527 input: "What is the weather in Paris?",

1501 tools: [{1528 tools: [

1529 {

1502 type: :function,1530 type: :function,

1503 name: "get_weather",1531 name: "get_weather",

1504 parameters: {1532 parameters: {

1505 type: :object,1533 type: :object,

1506 properties: {location: {type: :string}},1534 properties: { location: { type: :string } },

1507 required: ["location"],1535 required: ["location"],

1508 additionalProperties: false1536 additionalProperties: false

1509 },1537 },

1510 strict: true1538 strict: true

1511 }]1539 }

1540 ]

1512)1541)

1513 1542 

1514final_tool_calls = {}1543final_tool_calls = {}


1659response = client.responses.create(1688response = client.responses.create(

1660 model: "gpt-6-astra",1689 model: "gpt-6-astra",

1661 input: "Use code_exec to print hello world.",1690 input: "Use code_exec to print hello world.",

1662 tools: [{1691 tools: [

1692 {

1663 type: :custom,1693 type: :custom,

1664 name: "code_exec",1694 name: "code_exec",

1665 description: "Executes arbitrary Python code."1695 description: "Executes arbitrary Python code."

1666 }]1696 }

1697 ]

1667)1698)

1668 1699 

1669puts(response.output)1700puts(response.output)


1695 1726 

1696A [context-free grammar](https://en.wikipedia.org/wiki/Context-free_grammar) (CFG) is a set of rules that define how to produce valid text in a given format. For custom tools, you can provide a CFG that will constrain the model's text input for a custom tool.1727A [context-free grammar](https://en.wikipedia.org/wiki/Context-free_grammar) (CFG) is a set of rules that define how to produce valid text in a given format. For custom tools, you can provide a CFG that will constrain the model's text input for a custom tool.

1697 1728 

1698You can provide a custom CFG using the `grammar` parameter when configuring a custom tool. Currently, we support two CFG syntaxes when defining grammars: `lark` and `regex`.1729You can provide a custom CFG using the `grammar` parameter when configuring a custom tool. Currently, we support two forms of CFG syntax when defining grammars: `lark` and `regex`.

1699 1730 

1700#### Lark CFG1731#### Lark CFG

1701 1732 


1866response = client.responses.create(1897response = client.responses.create(

1867 model: "gpt-6-astra",1898 model: "gpt-6-astra",

1868 input: "Use math_exp to add four plus four.",1899 input: "Use math_exp to add four plus four.",

1869 tools: [{1900 tools: [

1901 {

1870 type: :custom,1902 type: :custom,

1871 name: "math_exp",1903 name: "math_exp",

1872 description: "Creates valid mathematical expressions.",1904 description: "Creates valid mathematical expressions.",

1873 format: {type: :grammar, syntax: :lark, definition: grammar}1905 format: {

1874 }]1906 type: :grammar,

1907 syntax: :lark,

1908 definition: grammar

1909 }

1910 }

1911 ]

1875)1912)

1876 1913 

1877puts(response.output)1914puts(response.output)


1910 1947 

1911We recommend using the [Lark IDE](https://www.lark-parser.org/ide/) to experiment with custom grammars.1948We recommend using the [Lark IDE](https://www.lark-parser.org/ide/) to experiment with custom grammars.

1912 1949 

1913### Keep grammars simple1950<a id="keep-grammars-simple"></a>

1951 

1952### Limit grammar complexity

1914 1953 

1915Try to make your grammar as simple as possible. The OpenAI API may return an error if the grammar is too complex, so you should ensure that your desired grammar is compatible before using it in the API.1954Limit your grammar to the rules and patterns your tool needs. The OpenAI API may return an error if the grammar is too complex, so you should ensure that your desired grammar is compatible before using it in the API.

1916 1955 

1917Lark grammars can be tricky to perfect. While simple grammars perform most reliably, complex grammars often require iteration on the grammar definition itself, the prompt, and the tool description to ensure that the model does not go out of distribution.1956Lark grammars can be tricky to perfect. While less complex grammars perform most reliably, complex grammars often require iteration on the grammar definition itself, the prompt, and the tool description to ensure that the model does not go out of distribution.

1918 1957 

1919### Correct versus incorrect patterns1958### Correct versus incorrect patterns

1920 1959 


1936 1975 

1937### Terminals versus rules1976### Terminals versus rules

1938 1977 

1939Lark uses terminals for lexer tokens (by convention, `UPPERCASE`) and rules for parser productions (by convention, `lowercase`). The most practical way to stay within the supported subset and avoid surprises is to keep your grammar simple and explicit, and to use terminals and rules with a clear separation of concerns.1978Lark uses terminals for lexer tokens (by convention, `UPPERCASE`) and rules for parser productions (by convention, `lowercase`). The most practical way to stay within the supported subset and avoid surprises is to keep your grammar explicit and avoid unnecessary complexity, and to use terminals and rules with a clear separation of concerns.

1940 1979 

1941The regex syntax used by terminals is the [Rust regex crate syntax](https://docs.rs/regex/latest/regex/#syntax), not Python's `re` [module](https://docs.python.org/3/library/re.html).1980The regex syntax used by terminals is the [Rust regex crate syntax](https://docs.rs/regex/latest/regex/#syntax), not Python's `re` [module](https://docs.python.org/3/library/re.html).

1942 1981 


1948 1987 

1949**Prefer one terminal when you're carving text out of freeform spans**1988**Prefer one terminal when you're carving text out of freeform spans**

1950 1989 

1951If you need to recognize a pattern embedded in arbitrary text (e.g., natural language with “anything” between anchors), express that as a single terminal. Do not try to interleave free‑text terminals with parser rules; the greedy lexer will not respect your intended boundaries and it is highly likely the model will go out of distribution.1990If you need to recognize a pattern embedded in arbitrary text (for example, natural language with “anything” between anchors), express that as a single terminal. Do not try to interleave free‑text terminals with parser rules; the greedy lexer will not respect your intended boundaries and it is highly likely the model will go out of distribution.

1952 1991 

1953**Use rules to compose discrete tokens**1992**Use rules to compose discrete tokens**

1954 1993 

1955Rules are ideal when you're combining clearly delimited terminals (numbers, keywords, punctuation) into larger structures. They're not the right tool for constraining "the stuff in between" two terminals.1994Rules are ideal when you're combining explicitly delimited terminals (numbers, keywords, punctuation) into larger structures. They're not the right tool for constraining "the stuff in between" two terminals.

1956 1995 

1957**Keep terminals simple, bounded, and self-contained**1996**Keep terminals focused, bounded, and self-contained**

1958 1997 

1959Favor explicit character classes and bounded quantifiers (`{0,10}`, not unbounded `*` everywhere). If you need "any text up to a period", prefer something like `/[^.\n]{0,10}*\./` rather than `/.+\./` to avoid runaway growth.1998Favor explicit character classes and bounded quantifiers (`{0,10}`, not unbounded `*` everywhere). If you need "any text up to a period," prefer something like `/[^.\n]{0,10}*\./` rather than `/.+\./` to avoid runaway growth.

1960 1999 

1961**Use rules to combine tokens, not to steer regex internals**2000**Use rules to combine tokens, not to steer regex internals**

1962 2001 


2111response = client.responses.create(2150response = client.responses.create(

2112 model: "gpt-6-astra",2151 model: "gpt-6-astra",

2113 input: "Use timestamp to save August 7th 2025 at 10AM.",2152 input: "Use timestamp to save August 7th 2025 at 10AM.",

2114 tools: [{2153 tools: [

2154 {

2115 type: :custom,2155 type: :custom,

2116 name: "timestamp",2156 name: "timestamp",

2117 description: "Saves a timestamp in date and time format.",2157 description: "Saves a timestamp in date and time format.",

2118 format: {type: :grammar, syntax: :regex, definition: grammar}2158 format: {

2119 }]2159 type: :grammar,

2160 syntax: :regex,

2161 definition: grammar

2162 }

2163 }

2164 ]

2120)2165)

2121 2166 

2122puts(response.output)2167puts(response.output)

Details

242grader = {242grader = {

243 "type" => "score_model",243 "type" => "score_model",

244 "name" => "my_score_model",244 "name" => "my_score_model",

245 "input" => [{245 "input" => [

246 {

246 "role" => "system",247 "role" => "system",

247 "content" => "You are an expert grader. If the reference and model answer are exact matches, output a score of 1. If they are somewhat similar in meaning, output a score in 0.5. Otherwise, give a score of 0."248 "content" => "You are an expert grader. If the reference and model answer are exact matches, output a score of 1. If they are somewhat similar in meaning, output a score in 0.5. Otherwise, give a score of 0."

248 }, {249 }, {

249 "role" => "user",250 "role" => "user",

250 "content" => "Reference: {{ item.reference_answer }}. Model answer: {{ sample.output_text }}"251 "content" => "Reference: {{ item.reference_answer }}. Model answer: {{ sample.output_text }}"

251 }],252 }

253 ],

252 "pass_threshold" => 0.5,254 "pass_threshold" => 0.5,

253 "model" => "o4-mini-2025-04-16",255 "model" => "o4-mini-2025-04-16",

254 "range" => [0, 1],256 "range" => [0, 1],


258 "reasoning_effort" => "medium"260 "reasoning_effort" => "medium"

259 }261 }

260}262}

261item = {reference_answer: 1.0}263item = { reference_answer: 1.0 }

262model_sample = "0.9"264model_sample = "0.9"

263 265 

264pp(client.fine_tuning.alpha.graders.validate(grader: grader))266pp(client.fine_tuning.alpha.graders.validate(grader: grader))


374}376}

375```377```

376 378 

377Here's a working example. For Ruby, save the `grade` function shown above, including its import, as `grader.py`. Set `OPENAI_GRADER_SOURCE_PATH` to that file's path before running the example. The supplied function returns `1.0`; replace its body with your grading logic.379Here's a working example. For Ruby, save the `grade` function shown above, including its import, as `grader.py`. Place `grader.py` in the directory where you run the example. The supplied function returns `1.0`; replace its body with your grading logic.

378 380 

379```python381```python

380import os382import os


425require "openai"427require "openai"

426 428 

427client = OpenAI::Client.new429client = OpenAI::Client.new

428# Set OPENAI_GRADER_SOURCE_PATH to the Python grader file to upload.430# Save your Python grading function as grader.py before running this example.

429grader = {type: :python, source: File.read(ENV.fetch("OPENAI_GRADER_SOURCE_PATH"))}431grading_function = File.read("grader.py")

430item = {reference_answer: "fuzzy wuzzy had no hair"}432grader = {

433 type: :python,

434 source: grading_function

435}

436item = { reference_answer: "fuzzy wuzzy had no hair" }

431model_sample = "fuzzy wuzzy was a bear"437model_sample = "fuzzy wuzzy was a bear"

432 438 

433pp(client.fine_tuning.alpha.graders.validate(grader: grader))439pp(client.fine_tuning.alpha.graders.validate(grader: grader))

Details

133. Select an image detail level supported by the model.133. Select an image detail level supported by the model.

144. Read the image input tokens and estimated cost. If the processed image exceeds the [30,000-patch limit](https://developers.openai.com/api/docs/guides/images-vision#image-input-requirements), the calculator shows a rejection message instead of an estimate. Expand **Calculation details** to see the resized dimensions and token calculation.144. Read the image input tokens and estimated cost. If the processed image exceeds the [30,000-patch limit](https://developers.openai.com/api/docs/guides/images-vision#image-input-requirements), the calculator shows a rejection message instead of an estimate. Expand **Calculation details** to see the resized dimensions and token calculation.

15 15 

16For example, a 6000 × 6000 image on GPT-5.6 exceeds the limit with `original` detail (35,344 patches), but fits after resizing with `high` detail (2,500 patches). Choose `high` only when your task does not require original resolution or precise image coordinates.16For example, a 6000 × 6000 image on `gpt-6-astra` exceeds the limit with `original` detail (35,344 patches), but fits after resizing with `high` detail (2,500 patches). Choose `high` only when your task does not require original resolution or precise image coordinates.

17 17 

18## Understand the estimate18## Understand the estimate

19 19 

Details

369response = client.responses.create(369response = client.responses.create(

370 model: "gpt-6-astra",370 model: "gpt-6-astra",

371 input: "Generate an image of a gray tabby cat hugging an otter with an orange scarf.",371 input: "Generate an image of a gray tabby cat hugging an otter with an orange scarf.",

372 tools: [{type: :image_generation, model: "gpt-image-2.5-sunburst"}]372 tools: [

373 {

374 type: :image_generation,

375 model: "gpt-image-2.5-sunburst"

376 }

377 ]

373)378)

374 379 

375image_call = response.output.find do |item|380image_call = response.output.find do |item|


552response = client.responses.create(557response = client.responses.create(

553 model: "gpt-6-astra",558 model: "gpt-6-astra",

554 input: "Generate an image of a gray tabby cat hugging an otter with an orange scarf.",559 input: "Generate an image of a gray tabby cat hugging an otter with an orange scarf.",

555 tools: [{type: :image_generation, model: "gpt-image-2.5-sunburst", action: :generate}]560 tools: [

561 {

562 type: :image_generation,

563 model: "gpt-image-2.5-sunburst",

564 action: :generate

565 }

566 ]

556)567)

557 568 

558image_call = response.output.find do |item|569image_call = response.output.find do |item|


829first = client.responses.create(840first = client.responses.create(

830 model: "gpt-6-astra",841 model: "gpt-6-astra",

831 input: "Generate an image of a gray tabby cat hugging an otter with an orange scarf.",842 input: "Generate an image of a gray tabby cat hugging an otter with an orange scarf.",

832 tools: [{type: :image_generation, model: "gpt-image-2.5-sunburst"}]843 tools: [

844 {

845 type: :image_generation,

846 model: "gpt-image-2.5-sunburst"

847 }

848 ]

833)849)

834 850 

835first_image = first.output.find do |item|851first_image = first.output.find do |item|


846 model: "gpt-6-astra",862 model: "gpt-6-astra",

847 input: "Now make it look realistic.",863 input: "Now make it look realistic.",

848 previous_response_id: first.id,864 previous_response_id: first.id,

849 tools: [{type: :image_generation, model: "gpt-image-2.5-sunburst"}]865 tools: [

866 {

867 type: :image_generation,

868 model: "gpt-image-2.5-sunburst"

869 }

870 ]

850)871)

851 872 

852follow_up_image = follow_up.output.find do |item|873follow_up_image = follow_up.output.find do |item|


1164first = client.responses.create(1185first = client.responses.create(

1165 model: "gpt-6-astra",1186 model: "gpt-6-astra",

1166 input: "Generate an image of a gray tabby cat hugging an otter with an orange scarf.",1187 input: "Generate an image of a gray tabby cat hugging an otter with an orange scarf.",

1167 tools: [{type: :image_generation, model: "gpt-image-2.5-sunburst"}]1188 tools: [

1189 {

1190 type: :image_generation,

1191 model: "gpt-image-2.5-sunburst"

1192 }

1193 ]

1168)1194)

1169 1195 

1170first_image = first.output.find do |item|1196first_image = first.output.find do |item|


1182 input: [1208 input: [

1183 {1209 {

1184 role: :user,1210 role: :user,

1185 content: [{type: :input_text, text: "Now make it look realistic."}]1211 content: [

1212 {

1213 type: :input_text,

1214 text: "Now make it look realistic."

1215 }

1216 ]

1186 },1217 },

1187 {type: :image_generation_call, id: first_image.id}1218 {

1219 type: :image_generation_call,

1220 id: first_image.id

1221 }

1188 ],1222 ],

1189 tools: [{type: :image_generation, model: "gpt-image-2.5-sunburst"}]1223 tools: [

1224 {

1225 type: :image_generation,

1226 model: "gpt-image-2.5-sunburst"

1227 }

1228 ]

1190)1229)

1191 1230 

1192follow_up_image = follow_up.output.find do |item|1231follow_up_image = follow_up.output.find do |item|


1438stream = client.responses.stream(1477stream = client.responses.stream(

1439 model: "gpt-6-astra",1478 model: "gpt-6-astra",

1440 input: "Generate an image of a river made of white owl feathers.",1479 input: "Generate an image of a river made of white owl feathers.",

1441 tools: [{type: :image_generation, model: "gpt-image-2.5-sunburst", partial_images: 2}]1480 tools: [

1481 {

1482 type: :image_generation,

1483 model: "gpt-image-2.5-sunburst",

1484 partial_images: 2

1485 }

1486 ]

1442)1487)

1443 1488 

1444stream.each do |event|1489stream.each do |event|


2113PROMPT2158PROMPT

2114response = client.responses.create(2159response = client.responses.create(

2115 model: "gpt-6-astra",2160 model: "gpt-6-astra",

2116 input: [{2161 input: [

2162 {

2117 role: :user,2163 role: :user,

2118 content: [2164 content: [

2119 {type: :input_text, text: prompt},2165 {

2166 type: :input_text,

2167 text: prompt

2168 },

2120 *base64_images.map do |image|2169 *base64_images.map do |image|

2121 {type: :input_image, image_url: "data:image/png;base64,#{image}"}2170 {

2171 type: :input_image,

2172 image_url: "data:image/png;base64,#{image}"

2173 }

2122 end,2174 end,

2123 *file_ids.map do |file_id|2175 *file_ids.map do |file_id|

2124 {type: :input_image, file_id: file_id}2176 {

2177 type: :input_image,

2178 file_id: file_id

2179 }

2125 end2180 end

2126 ]2181 ]

2127 }],2182 }

2128 tools: [{type: :image_generation, model: "gpt-image-2.5-sunburst"}]2183 ],

2184 tools: [

2185 {

2186 type: :image_generation,

2187 model: "gpt-image-2.5-sunburst"

2188 }

2189 ]

2129)2190)

2130 2191 

2131image_call = response.output.find do |item|2192image_call = response.output.find do |item|


2664mask = client.files.create(file: Pathname("mask.png"), purpose: :vision)2725mask = client.files.create(file: Pathname("mask.png"), purpose: :vision)

2665response = client.responses.create(2726response = client.responses.create(

2666 model: "gpt-6-astra",2727 model: "gpt-6-astra",

2667 input: [{2728 input: [

2729 {

2668 role: :user,2730 role: :user,

2669 content: [2731 content: [

2670 {type: :input_text, text: "Add a flamingo to the pool."},2732 {

2671 {type: :input_image, file_id: image.id}2733 type: :input_text,

2734 text: "Add a flamingo to the pool."

2735 },

2736 {

2737 type: :input_image,

2738 file_id: image.id

2739 }

2740 ]

2741 }

2742 ],

2743 tools: [

2744 {

2745 type: :image_generation,

2746 model: "gpt-image-2.5-sunburst",

2747 input_image_mask: { file_id: mask.id }

2748 }

2672 ]2749 ]

2673 }],

2674 tools: [{

2675 type: :image_generation, model: "gpt-image-2.5-sunburst",

2676 input_image_mask: {file_id: mask.id}

2677 }]

2678)2750)

2679 2751 

2680image_call = response.output.find do |item|2752image_call = response.output.find do |item|

Details

192response = client.responses.create(192response = client.responses.create(

193 model: "gpt-6-astra",193 model: "gpt-6-astra",

194 input: "Generate an image of a gray tabby cat hugging an otter with an orange scarf.",194 input: "Generate an image of a gray tabby cat hugging an otter with an orange scarf.",

195 tools: [{type: :image_generation}]195 tools: [{ type: :image_generation }]

196)196)

197 197 

198image_call = response.output.find do |item|198image_call = response.output.find do |item|


412 {412 {

413 role: :user,413 role: :user,

414 content: [414 content: [

415 {type: :input_text, text: "What's in this image?"},415 {

416 type: :input_text,

417 text: "What's in this image?"

418 },

416 {419 {

417 type: :input_image,420 type: :input_image,

418 detail: :auto,421 detail: :auto,


690 {693 {

691 role: :user,694 role: :user,

692 content: [695 content: [

693 {type: :input_text, text: "What's in this image?"},696 {

697 type: :input_text,

698 text: "What's in this image?"

699 },

694 {700 {

695 type: :input_image,701 type: :input_image,

696 detail: :auto,702 detail: :auto,


946 {952 {

947 role: :user,953 role: :user,

948 content: [954 content: [

949 {type: :input_text, text: "What's in this image?"},955 {

950 {type: :input_image, detail: :auto, file_id: uploaded.id}956 type: :input_text,

957 text: "What's in this image?"

958 },

959 {

960 type: :input_image,

961 detail: :auto,

962 file_id: uploaded.id

963 }

951 ]964 ]

952 }965 }

953 ]966 ]


1005 1018 

1006### Model sizing behavior1019### Model sizing behavior

1007 1020 

1008The following table covers the general-purpose vision models available in the [image input cost calculator](https://developers.openai.com/api/docs/guides/image-cost-calculator). Other models and specialized variants can use different limits. All resizing preserves aspect ratio without enlarging smaller images.1021The following table summarizes sizing behavior for general-purpose vision models. Other models and specialized variants can use different limits. All resizing preserves aspect ratio without enlarging smaller images.

1009 1022 

1010<table>1023<table>

1011 <tr>1024 <tr>


1013 <th>Supported detail levels</th>1026 <th>Supported detail levels</th>

1014 <th>Patch and resizing behavior</th>1027 <th>Patch and resizing behavior</th>

1015 </tr>1028 </tr>

1029 <tr>

1030 <td>

1031 `gpt-6-astra`

1032 </td>

1033 <td>

1034 `low`, `high`, `original`,

1035 `auto`

1036 </td>

1037 <td>

1038 `low` fits within 512 × 512 pixels. `high` allows up

1039 to 2,500 patches and a 65,535-pixel maximum dimension. Both limits apply.

1040 `original` preserves the image's dimensions, except that images

1041 larger than 65,535 pixels on either side are scaled down to fit that

1042 limit. If the resulting image requires more than

1043 [30,000 patches](#image-input-requirements), the API rejects

1044 the request; the image is not resized to fit the patch limit.

1045 `auto` uses the same sizing behavior as `original`.

1046 </td>

1047 </tr>

1016 <tr>1048 <tr>

1017 <td>1049 <td>

1018 `gpt-5.6-sol`, `gpt-5.6-terra`, 1050 `gpt-5.6-sol`, `gpt-5.6-terra`,


1137 1169 

1138| Model | Multiplier |1170| Model | Multiplier |

1139| -------------------------------------- | ---------- |1171| -------------------------------------- | ---------- |

1172| `gpt-6-astra` | 1.2 |

1140| `gpt-5.6-sol` | 1.2 |1173| `gpt-5.6-sol` | 1.2 |

1141| `gpt-5.6-terra` | 1.2 |1174| `gpt-5.6-terra` | 1.2 |

1142| `gpt-5.6-luna` | 1.2 |1175| `gpt-5.6-luna` | 1.2 |


1155 1188 

1156\* Deprecated and scheduled for shutdown. See the [deprecation schedule](https://developers.openai.com/api/docs/deprecations) for dates and replacements. These models aren't included in the calculator or the model sizing table above.1189\* Deprecated and scheduled for shutdown. See the [deprecation schedule](https://developers.openai.com/api/docs/deprecations) for dates and replacements. These models aren't included in the calculator or the model sizing table above.

1157 1190 

1158**Cost calculation examples for `gpt-5.4` with `detail: high`**1191**Image token calculation examples for `gpt-6-astra` with `detail: high`**

1159 1192 

1160This combination uses a 2048-pixel maximum dimension, a 2,500-patch budget, and a 1.2× multiplier.1193This combination uses a 65,535-pixel maximum dimension, a 2,500-patch budget, and a 1.2× multiplier.

1161 1194 

1162- A 1024 × 1024 image needs `32 × 32 = 1024` patches. No resizing is needed. The billable image input is `ceil(1024 × 1.2) = 1229` tokens.1195- A 1024 × 1024 image needs `32 × 32 = 1024` patches. No resizing is needed. The billable image input is `ceil(1024 × 1.2) = 1229` tokens.

1163- A 2048 × 2048 image initially needs `64 × 64 = 4096` patches. The patch budget reduces it to 1600 × 1600 pixels, or `50 × 50 = 2500` patches. The estimate is `ceil(2500 × 1.2) = 3000` tokens.1196- A 2048 × 2048 image initially needs `64 × 64 = 4096` patches. The patch budget reduces it to 1600 × 1600 pixels, or `50 × 50 = 2500` patches. The estimate is `ceil(2500 × 1.2) = 3000` tokens.

1197- A 4096 × 512 image stays at its original size: `128 × 16 = 2048` patches and `ceil(2048 × 1.2) = 2458` tokens.

1164 1198 

1165Floating-point rounding in billing can make the final count differ from the estimate by one token.1199Floating-point rounding in billing can make the final count differ from the estimate by one token.

1166 1200 

Details

139 139 

140### Migrate with Codex140### Migrate with Codex

141 141 

142Codex can apply the recommended changes in this guide with the [OpenAI Docs skill](https://github.com/openai/skills/tree/main/skills/.curated/openai-docs).142Codex can apply the recommended changes in this guide with the [OpenAI Docs skill](https://github.com/openai/codex/tree/main/codex-rs/skills/src/assets/samples/openai-docs).

143 143 

144```text144```text

145$openai-docs migrate this project to GPT-6 Astra145$openai-docs migrate this project to GPT-6 Astra

146```146```

147 147 

148To use this skill in other coding agents, download it from the [OpenAI skills repository](https://github.com/openai/skills/tree/main/skills/.curated/openai-docs).148To use this skill in other coding agents, download it from the [Codex repository](https://github.com/openai/codex/tree/main/codex-rs/skills/src/assets/samples/openai-docs).

149 149 

150### Update API and model parameters150### Update API and model parameters

151 151 

Details

1016 description: APPLY_PATCH_TOOL_DESC,1016 description: APPLY_PATCH_TOOL_DESC,

1017 parameters: {1017 parameters: {

1018 type: "object",1018 type: "object",

1019 properties: {input: {type: "string", description: "The apply_patch command to execute."}},1019 properties: {

1020 input: {

1021 type: "string",

1022 description: "The apply_patch command to execute."

1023 }

1024 },

1020 required: ["input"]1025 required: ["input"]

1021 }1026 }

1022}1027}

Details

342 342 

343```ruby343```ruby

344response = client.responses.create(344response = client.responses.create(

345 model: "gpt-5.1", input: response_input, tools: [{type: :apply_patch}]345 model: "gpt-5.1", input: response_input, tools: [{ type: :apply_patch }]

346)346)

347```347```

348 348 


403```403```

404 404 

405```ruby405```ruby

406tools = [{type: :shell}]406tools = [{ type: :shell }]

407```407```

408 408 

409 409 

Details

164client = OpenAI::Client.new164client = OpenAI::Client.new

165response = client.responses.create(165response = client.responses.create(

166 model: "gpt-5.2",166 model: "gpt-5.2",

167 reasoning: {effort: :minimal},167 reasoning: { effort: :minimal },

168 input: "Explain the bug and propose a fix."168 input: "Explain the bug and propose a fix."

169)169)

170puts(response.output_text)170puts(response.output_text)


279client = OpenAI::Client.new279client = OpenAI::Client.new

280response = client.responses.create(280response = client.responses.create(

281 model: "gpt-5.2",281 model: "gpt-5.2",

282 text: {verbosity: :low},282 text: { verbosity: :low },

283 input: "Explain the bug and propose a fix."283 input: "Explain the bug and propose a fix."

284)284)

285puts(response.output_text)285puts(response.output_text)


811client = OpenAI::Client.new811client = OpenAI::Client.new

812response = client.responses.create(812response = client.responses.create(

813 model: "gpt-5.2",813 model: "gpt-5.2",

814 input: [{role: :user, content: "Write a very long poem about a dog."}]814 input: [

815 {

816 role: :user,

817 content: "Write a very long poem about a dog."

818 }

819 ]

815)820)

816compaction = client.responses.compact(821compaction = client.responses.compact(

817 model: "gpt-5.2",822 model: "gpt-5.2",

818 input: [823 input: [

819 {role: :user, content: "Write a very long poem about a dog."},824 {

825 role: :user,

826 content: "Write a very long poem about a dog."

827 },

820 *response.output828 *response.output

821 ]829 ]

822)830)

Details

404PROMPT404PROMPT

405response = client.responses.create(405response = client.responses.create(

406 model: "gpt-5.3-codex", input: input,406 model: "gpt-5.3-codex", input: input,

407 tools: [{type: :apply_patch}], parallel_tool_calls: false407 tools: [{ type: :apply_patch }], parallel_tool_calls: false

408)408)

409response.output.each do |item|409response.output.each do |item|

410 pp(item.operation) if item.is_a?(OpenAI::Responses::ResponseApplyPatchToolCall)410 pp(item.operation) if item.is_a?(OpenAI::Responses::ResponseApplyPatchToolCall)


436 436 

437response = client.responses.create(437response = client.responses.create(

438 model: "gpt-5.3-codex", input: input,438 model: "gpt-5.3-codex", input: input,

439 tools: [{439 tools: [

440 type: :custom, name: "apply_patch",440 {

441 type: :custom,

442 name: "apply_patch",

441 description: "Apply a patch to update files.",443 description: "Apply a patch to update files.",

442 format: {type: :grammar, syntax: :lark, definition: APPLY_PATCH_GRAMMAR}444 format: {

443 }],445 type: :grammar,

446 syntax: :lark,

447 definition: APPLY_PATCH_GRAMMAR

448 }

449 }

450 ],

444 parallel_tool_calls: false451 parallel_tool_calls: false

445)452)

446response.output.each do |item|453response.output.each do |item|

Details

167client = OpenAI::Client.new167client = OpenAI::Client.new

168response = client.responses.create(168response = client.responses.create(

169 model: "gpt-5.4",169 model: "gpt-5.4",

170 reasoning: {effort: :minimal},170 reasoning: { effort: :minimal },

171 input: "Explain the bug and propose a fix."171 input: "Explain the bug and propose a fix."

172)172)

173puts(response.output_text)173puts(response.output_text)


282client = OpenAI::Client.new282client = OpenAI::Client.new

283response = client.responses.create(283response = client.responses.create(

284 model: "gpt-5.4",284 model: "gpt-5.4",

285 text: {verbosity: :low},285 text: { verbosity: :low },

286 input: "Explain the bug and propose a fix."286 input: "Explain the bug and propose a fix."

287)287)

288puts(response.output_text)288puts(response.output_text)


579client = OpenAI::Client.new579client = OpenAI::Client.new

580response = client.responses.create(580response = client.responses.create(

581 model: "gpt-5.4",581 model: "gpt-5.4",

582 reasoning: {effort: :medium},582 reasoning: { effort: :medium },

583 input: "Explain the bug and propose a fix."583 input: "Explain the bug and propose a fix."

584)584)

585puts(response.output_text)585puts(response.output_text)

Details

139 139 

140### Migrate with Codex140### Migrate with Codex

141 141 

142Codex can apply the recommended changes in this guide with the [OpenAI Docs skill](https://github.com/openai/skills/tree/main/skills/.curated/openai-docs).142Codex can apply the recommended changes in this guide with the [OpenAI Docs skill](https://github.com/openai/codex/tree/main/codex-rs/skills/src/assets/samples/openai-docs).

143 143 

144```text144```text

145$openai-docs migrate this project to GPT-6 Astra145$openai-docs migrate this project to GPT-6 Astra

146```146```

147 147 

148To use this skill in other coding agents, download it from the [OpenAI skills repository](https://github.com/openai/skills/tree/main/skills/.curated/openai-docs).148To use this skill in other coding agents, download it from the [Codex repository](https://github.com/openai/codex/tree/main/codex-rs/skills/src/assets/samples/openai-docs).

149 149 

150### Update API and model parameters150### Update API and model parameters

151 151 

Details

357 357 

358### Start a WebSocket fork358### Start a WebSocket fork

359 359 

360Set `OPENAI_API_KEY` and `OPENAI_LIVE_SESSION_ID` to your API key and stored source session ID. These examples confirm startup and then close the fork. To continue the conversation, send and receive audio after `session.started` using the [WebSocket connection flow](https://developers.openai.com/api/docs/guides/voice-websockets?api=live). See the [fork WebSocket reference](https://developers.openai.com/api/reference/resources/live/fork-websocket) for the startup fields and events.360Set `OPENAI_API_KEY`. The examples use the stored source session ID saved by your application. They confirm startup and then close the fork. To continue the conversation, send and receive audio after `session.started` using the [WebSocket connection flow](https://developers.openai.com/api/docs/guides/voice-websockets?api=live). See the [fork WebSocket reference](https://developers.openai.com/api/reference/resources/live/fork-websocket) for the startup fields and events.

361 361 

362```javascript362```javascript

363import OpenAI from "openai";363import OpenAI from "openai";

364import { ForksWS } from "openai/resources/live/forks/ws";364import { ForksWS } from "openai/resources/live/forks/ws";

365 365 

366const sourceSessionId = process.env.OPENAI_LIVE_SESSION_ID;366async function forkSession(sourceSessionId) {

367if (!sourceSessionId) throw new Error("Set OPENAI_LIVE_SESSION_ID");367 const ws = new ForksWS(new OpenAI(), { session_id: sourceSessionId });

368 368 let finalized = false;

369const ws = new ForksWS(new OpenAI(), { session_id: sourceSessionId });369 try {

370let finalized = false;

371try {

372 for await (const event of ws) {370 for await (const event of ws) {

373 if (event.type === "open") {371 if (event.type === "open") {

374 ws.send({ type: "session.start", session: {} });372 ws.send({ type: "session.start", session: {} });


387 }385 }

388 }386 }

389 if (!finalized) throw new Error("Connection closed before session.closed");387 if (!finalized) throw new Error("Connection closed before session.closed");

390} finally {388 } finally {

391 ws.close();389 ws.close();

390 }

392}391}

393```392```

394 393 

395```python394```python

396import os

397 

398from openai import OpenAI395from openai import OpenAI

399 396 

400client = OpenAI()

401source_session_id = os.environ["OPENAI_LIVE_SESSION_ID"]

402 397 

403with client.live.forks.connect(session_id=source_session_id) as connection:398def fork_session(source_session_id: str) -> None:

399 client = OpenAI()

400 with client.live.forks.connect(session_id=source_session_id) as connection:

404 connection.session.start(session={})401 connection.session.start(session={})

405 finalized = False402 finalized = False

406 for event in connection:403 for event in connection:


421 418 

422### Start a WebRTC fork419### Start a WebRTC fork

423 420 

424Create a new SDP offer in your frontend and send it to your backend. The following backend examples read that offer from the file named by `OPENAI_LIVE_SDP_OFFER_FILE` and fork the stored `OPENAI_LIVE_SESSION_ID`:421Create a new SDP offer in your frontend and send it to your backend. The following backend examples use that offer and the stored source session ID from your application:

425 422 

426```javascript423```javascript

427import OpenAI from "openai";424import OpenAI from "openai";

428import { readFile } from "node:fs/promises";

429 425 

430const sourceSessionId = process.env.OPENAI_LIVE_SESSION_ID;426async function forkSession(sourceSessionId, offerSdp) {

431const offerFile = process.env.OPENAI_LIVE_SDP_OFFER_FILE;427 const client = new OpenAI();

432if (!sourceSessionId || !offerFile) {428 const fork = await client.live.sessions.fork(sourceSessionId, {

433 throw new Error("Set OPENAI_LIVE_SESSION_ID and OPENAI_LIVE_SDP_OFFER_FILE");

434}

435const offerSdp = await readFile(offerFile, "utf8");

436 

437const client = new OpenAI();

438const fork = await client.live.sessions.fork(sourceSessionId, {

439 transport: { type: "webrtc", sdp: offerSdp },429 transport: { type: "webrtc", sdp: offerSdp },

440});430 });

441console.log(JSON.stringify(fork));431 console.log(JSON.stringify(fork));

432}

442```433```

443 434 

444```python435```python

445import os

446from pathlib import Path

447 

448from openai import OpenAI436from openai import OpenAI

449 437 

450client = OpenAI()

451source_session_id = os.environ["OPENAI_LIVE_SESSION_ID"]

452offer_sdp = Path(os.environ["OPENAI_LIVE_SDP_OFFER_FILE"]).read_bytes().decode()

453 438 

454fork = client.live.sessions.fork(439def fork_session(source_session_id: str, offer_sdp: str) -> None:

440 client = OpenAI()

441 fork = client.live.sessions.fork(

455 source_session_id,442 source_session_id,

456 transport={"type": "webrtc", "sdp": offer_sdp},443 transport={"type": "webrtc", "sdp": offer_sdp},

457)444 )

458print(fork.model_dump_json())445 print(fork.model_dump_json())

459```446```

460 447 

461 448 


465 452 

466### Download a recording453### Download a recording

467 454 

468After the stored recording is finalized, download its audio with `GET /v1/live/sessions/{session_id}/content`. The response is binary stereo WAV, with input audio in the left channel and output audio in the right channel. Set `OPENAI_LIVE_SESSION_ID` to the stored session ID. These examples stream the response to `recording.wav`:455After the stored recording is finalized, download its audio with `GET /v1/live/sessions/{session_id}/content`. The response is binary stereo WAV, with input audio in the left channel and output audio in the right channel. The examples use the stored session ID from your application and stream the response to `recording.wav`:

469 456 

470```javascript457```javascript

471import OpenAI from "openai";458import OpenAI from "openai";

472import { createWriteStream } from "node:fs";459import { createWriteStream } from "node:fs";

473import { pipeline } from "node:stream/promises";460import { pipeline } from "node:stream/promises";

474 461 

475const sessionId = process.env.OPENAI_LIVE_SESSION_ID;462async function downloadRecording(sessionId) {

476if (!sessionId) throw new Error("Set OPENAI_LIVE_SESSION_ID");463 const client = new OpenAI();

477 464 const response = await client.live.sessions.downloadRecording(sessionId);

478const client = new OpenAI();465 if (!response.body) throw new Error("Recording response has no body");

479const response = await client.live.sessions.downloadRecording(sessionId);466 await pipeline(response.body, createWriteStream("recording.wav"));

480if (!response.body) throw new Error("Recording response has no body");467}

481await pipeline(response.body, createWriteStream("recording.wav"));

482```468```

483 469 

484```python470```python

485import os

486 

487from openai import OpenAI471from openai import OpenAI

488 472 

489client = OpenAI()

490session_id = os.environ["OPENAI_LIVE_SESSION_ID"]

491 473 

492with client.live.sessions.with_streaming_response.download_recording(474def download_recording(session_id: str) -> None:

475 client = OpenAI()

476 with client.live.sessions.with_streaming_response.download_recording(

493 session_id477 session_id

494) as response:478 ) as response:

495 response.stream_to_file("recording.wav")479 response.stream_to_file("recording.wav")

496```480```

497 481 

Details

88client = OpenAI::Client.new88client = OpenAI::Client.new

89completion = client.chat.completions.create(89completion = client.chat.completions.create(

90 model: "gpt-6-astra",90 model: "gpt-6-astra",

91 messages: [{role: :user, content: "Write a one-sentence bedtime story about a unicorn."}]91 messages: [

92 {

93 role: :user,

94 content: "Write a one-sentence bedtime story about a unicorn."

95 }

96 ]

92)97)

93puts(completion.choices.fetch(0).message.content)98puts(completion.choices.fetch(0).message.content)

94```99```


366 371 

367client = OpenAI::Client.new372client = OpenAI::Client.new

368messages = [373messages = [

369 {role: :system, content: "You are a helpful assistant."},374 {

370 {role: :user, content: "Hello!"}375 role: :system,

376 content: "You are a helpful assistant."

377 },

378 {

379 role: :user,

380 content: "Hello!"

381 }

371]382]

372 383 

373completion = client.chat.completions.create(384completion = client.chat.completions.create(


513completion = client.chat.completions.create(524completion = client.chat.completions.create(

514 model: "gpt-6-astra",525 model: "gpt-6-astra",

515 messages: [526 messages: [

516 {role: :system, content: "You are a helpful assistant."},527 {

517 {role: :user, content: "Hello!"}528 role: :system,

529 content: "You are a helpful assistant."

530 },

531 {

532 role: :user,

533 content: "Hello!"

534 }

518 ]535 ]

519)536)

520 537 


810 827 

811client = OpenAI::Client.new828client = OpenAI::Client.new

812messages = [829messages = [

813 {role: :system, content: "You are a helpful assistant."},830 {

814 {role: :user, content: "What is the capital of France?"}831 role: :system,

832 content: "You are a helpful assistant."

833 },

834 {

835 role: :user,

836 content: "What is the capital of France?"

837 }

815]838]

816 839 

817first = client.chat.completions.create(840first = client.chat.completions.create(

818 model: "gpt-6-astra",841 model: "gpt-6-astra",

819 messages: messages842 messages: messages

820)843)

821messages << {role: :assistant, content: first.choices.fetch(0).message.content}844messages << {

822messages << {role: :user, content: "And its population?"}845 role: :assistant,

846 content: first.choices.fetch(0).message.content

847}

848messages << {

849 role: :user,

850 content: "And its population?"

851}

823 852 

824second = client.chat.completions.create(853second = client.chat.completions.create(

825 model: "gpt-6-astra",854 model: "gpt-6-astra",


842 Multi-turn conversation871 Multi-turn conversation

843 872 

844```javascript873```javascript

874import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";

875 

845/** @type {OpenAI.Responses.ResponseInput} */876/** @type {OpenAI.Responses.ResponseInput} */

846let context = [{ role: "user", content: "What is the capital of France?" }];877let context = [{ role: "user", content: "What is the capital of France?" }];

847 878 


851});882});

852 883 

853// Append the first response’s output to context884// Append the first response’s output to context

854context = context.concat(res1.output);885context = context.concat(toResponseInputItems(res1.output));

855 886 

856// Add the next user message887// Add the next user message

857context.push({ role: "user", content: "And its population?" });888context.push({ role: "user", content: "And its population?" });


997require "openai"1028require "openai"

998 1029 

999client = OpenAI::Client.new1030client = OpenAI::Client.new

1000context = [{role: :user, content: "What is the capital of France?"}]1031context = [

1032 {

1033 role: :user,

1034 content: "What is the capital of France?"

1035 }

1036]

1001 1037 

1002first = client.responses.create(1038first = client.responses.create(

1003 model: "gpt-6-astra",1039 model: "gpt-6-astra",

1004 input: context1040 input: context

1005)1041)

1006context.concat(first.output)1042context.concat(first.output)

1007context << {role: :user, content: "And its population?"}1043context << {

1044 role: :user,

1045 content: "And its population?"

1046}

1008 1047 

1009second = client.responses.create(1048second = client.responses.create(

1010 model: "gpt-6-astra",1049 model: "gpt-6-astra",


1452schema = {1491schema = {

1453 type: "object",1492 type: "object",

1454 properties: {1493 properties: {

1455 name: {type: "string", minLength: 1},1494 name: {

1456 age: {type: "number", minimum: 0, maximum: 130}1495 type: "string",

1496 minLength: 1

1497 },

1498 age: {

1499 type: "number",

1500 minimum: 0,

1501 maximum: 130

1502 }

1457 },1503 },

1458 required: ["name", "age"],1504 required: ["name", "age"],

1459 additionalProperties: false1505 additionalProperties: false


1462completion = client.chat.completions.create(1508completion = client.chat.completions.create(

1463 model: "gpt-6-astra",1509 model: "gpt-6-astra",

1464 reasoning_effort: :medium,1510 reasoning_effort: :medium,

1465 messages: [{role: :user, content: "Jane, 54 years old"}],1511 messages: [

1512 {

1513 role: :user,

1514 content: "Jane, 54 years old"

1515 }

1516 ],

1466 response_format: {1517 response_format: {

1467 type: :json_schema,1518 type: :json_schema,

1468 json_schema: {name: "person", strict: true, schema: schema}1519 json_schema: {

1520 name: "person",

1521 strict: true,

1522 schema: schema

1523 }

1469 }1524 }

1470)1525)

1471 1526 


1710schema = {1765schema = {

1711 type: "object",1766 type: "object",

1712 properties: {1767 properties: {

1713 name: {type: "string", minLength: 1},1768 name: {

1714 age: {type: "number", minimum: 0, maximum: 130}1769 type: "string",

1770 minLength: 1

1771 },

1772 age: {

1773 type: "number",

1774 minimum: 0,

1775 maximum: 130

1776 }

1715 },1777 },

1716 required: ["name", "age"],1778 required: ["name", "age"],

1717 additionalProperties: false1779 additionalProperties: false


1936 model: "gpt-5.6",1998 model: "gpt-5.6",

1937 reasoning_effort: :none,1999 reasoning_effort: :none,

1938 messages: [2000 messages: [

1939 {role: :system, content: "You are a helpful assistant."},2001 {

1940 {role: :user, content: "Who is the current president of France?"}2002 role: :system,

2003 content: "You are a helpful assistant."

2004 },

2005 {

2006 role: :user,

2007 content: "Who is the current president of France?"

2008 }

1941 ],2009 ],

1942 functions: [2010 functions: [

1943 {2011 {


1945 description: "Search the web for information",2013 description: "Search the web for information",

1946 parameters: {2014 parameters: {

1947 type: "object",2015 type: "object",

1948 properties: {query: {type: "string"}},2016 properties: { query: { type: "string" } },

1949 required: ["query"]2017 required: ["query"]

1950 }2018 }

1951 }2019 }


2064response = client.responses.create(2132response = client.responses.create(

2065 model: "gpt-6-astra",2133 model: "gpt-6-astra",

2066 input: "Who is the current president of France?",2134 input: "Who is the current president of France?",

2067 tools: [{type: :web_search}]2135 tools: [{ type: :web_search }]

2068)2136)

2069 2137 

2070puts(response.output_text)2138puts(response.output_text)

Details

189response = client.responses.create(189response = client.responses.create(

190 model: "gpt-6-astra",190 model: "gpt-6-astra",

191 input: "A user asks for instructions to make a harmful weapon. Draft a brief refusal and offer a safer alternative.",191 input: "A user asks for instructions to make a harmful weapon. Draft a brief refusal and offer a safer alternative.",

192 moderation: {model: "omni-moderation-latest"}192 moderation: { model: "omni-moderation-latest" }

193)193)

194 194 

195puts(response.moderation)195puts(response.moderation)


488moderation = client.moderations.create(488moderation = client.moderations.create(

489 model: OpenAI::Models::ModerationModel::OMNI_MODERATION_LATEST,489 model: OpenAI::Models::ModerationModel::OMNI_MODERATION_LATEST,

490 input: [490 input: [

491 {type: :text, text: "Text to classify goes here."},491 {

492 type: :text,

493 text: "Text to classify goes here."

494 },

492 {495 {

493 type: :image_url,496 type: :image_url,

494 image_url: {497 image_url: {

Details

238completion = client.chat.completions.create(238completion = client.chat.completions.create(

239 model: "gpt-4.1",239 model: "gpt-4.1",

240 messages: [240 messages: [

241 {role: :user, content: refactor_prompt},241 {

242 {role: :user, content: code}242 role: :user,

243 content: refactor_prompt

244 },

245 {

246 role: :user,

247 content: code

248 }

243 ],249 ],

244 prediction: {type: :content, content: code},250 prediction: {

251 type: :content,

252 content: code

253 },

245 store: true254 store: true

246)255)

247 256 


540stream = client.chat.completions.stream(549stream = client.chat.completions.stream(

541 model: "gpt-4.1",550 model: "gpt-4.1",

542 messages: [551 messages: [

543 {role: :user, content: refactor_prompt},552 {

544 {role: :user, content: code}553 role: :user,

554 content: refactor_prompt

555 },

556 {

557 role: :user,

558 content: code

559 }

545 ],560 ],

546 prediction: {type: :content, content: code},561 prediction: {

562 type: :content,

563 content: code

564 },

547 store: true565 store: true

548)566)

549 567 

Details

48 48 

49This is a relatively straightforward way to control access, but you must be vigilant about securing these keys. Avoid exposing the API keys in your code or in public repositories; instead, store them in a secure location. You should expose your keys to your application using environment variables or secret management service, so that you don't need to hard-code them in your codebase. Read more in our [Best practices for API key safety](https://help.openai.com/en/articles/5112595-best-practices-for-api-key-safety).49This is a relatively straightforward way to control access, but you must be vigilant about securing these keys. Avoid exposing the API keys in your code or in public repositories; instead, store them in a secure location. You should expose your keys to your application using environment variables or secret management service, so that you don't need to hard-code them in your codebase. Read more in our [Best practices for API key safety](https://help.openai.com/en/articles/5112595-best-practices-for-api-key-safety).

50 50 

51We strongly recommend setting an expiration date when you create a project API key and establishing a regular key rotation process. Before a key expires, create a replacement, update your applications to use it, and revoke the old key once you've verified that the replacement works.

52 

53Administrators can enforce a maximum API key lifetime at the organization or project level in [Platform settings](https://platform.openai.com/settings/organization/general). New keys must expire within the configured limit, preventing them from remaining valid indefinitely. Project limits cannot exceed the organization limit.

54 

51API key usage can be monitored on the [Usage page](https://platform.openai.com/usage) once tracking is enabled. If you are using an API key generated prior to Dec 20, 2023 tracking will not be enabled by default. You can enable tracking going forward on the [API key management dashboard](https://platform.openai.com/api-keys). All API keys generated past Dec 20, 2023 have tracking enabled. Any previous untracked usage will be displayed as `Untracked` in the dashboard.55API key usage can be monitored on the [Usage page](https://platform.openai.com/usage) once tracking is enabled. If you are using an API key generated prior to Dec 20, 2023 tracking will not be enabled by default. You can enable tracking going forward on the [API key management dashboard](https://platform.openai.com/api-keys). All API keys generated past Dec 20, 2023 have tracking enabled. Any previous untracked usage will be displayed as `Untracked` in the dashboard.

52 56 

53### Staging projects57### Staging projects


56 60 

57## Scaling your solution architecture61## Scaling your solution architecture

58 62 

59When designing your application or service for production that uses our API, it's important to consider how you will scale to meet traffic demands. There are a few key areas you will need to consider regardless of the cloud service provider of your choice:63When designing your application or service for production that uses our API, it's important to consider how you will scale to meet traffic demands. You will need to consider a few key areas regardless of the cloud service provider of your choice:

60 64 

61- **Horizontal scaling**: You may want to scale your application out horizontally to accommodate requests to your application that come from multiple sources. This could involve deploying additional servers or containers to distribute the load. If you opt for this type of scaling, make sure that your architecture is designed to handle multiple nodes and that you have mechanisms in place to balance the load between them.65- **Horizontal scaling**: You may want to scale your application out horizontally to accommodate requests to your application that come from multiple sources. This could involve deploying additional servers or containers to distribute the load. If you opt for this type of scaling, make sure that your architecture is designed to handle multiple nodes and that you have mechanisms in place to balance the load between them.

62- **Vertical scaling**: Another option is to scale your application up vertically, meaning you can beef up the resources available to a single node. This would involve upgrading your server's capabilities to handle the additional load. If you opt for this type of scaling, make sure your application is designed to take advantage of these additional resources.66- **Vertical scaling**: Another option is to scale your application up vertically, meaning you can beef up the resources available to a single node. This would involve upgrading your server's capabilities to handle the additional load. If you opt for this type of scaling, make sure your application is designed to take advantage of these additional resources.

63- **Caching**: By storing frequently accessed data, you can improve response times without needing to make repeated calls to our API. Your application will need to be designed to use cached data whenever possible and invalidate the cache when new information is added. There are a few different ways you could do this. For example, you could store data in a database, filesystem, or in-memory cache, depending on what makes the most sense for your application.67- **Caching**: By storing frequently accessed data, you can improve response times without needing to make repeated calls to our API. Your application will need to be designed to use cached data whenever possible and invalidate the cache when new information is added. For example, you could store data in a database, filesystem, or in-memory cache, depending on what makes the most sense for your application.

64- **Load balancing**: Finally, consider load-balancing techniques to ensure requests are distributed evenly across your available servers. This could involve using a load balancer in front of your servers or using DNS round-robin. Balancing the load will help improve performance and reduce bottlenecks.68- **Load balancing**: Finally, consider load-balancing techniques to ensure requests are distributed evenly across your available servers. This could involve using a load balancer in front of your servers or using DNS round-robin. Balancing the load will help improve performance and reduce bottlenecks.

65 69 

66### Managing rate limits70### Managing rate limits


85 89 

86The bulk of the latency typically arises from the token generation step.90The bulk of the latency typically arises from the token generation step.

87 91 

88> **Intuition**: Prompt tokens add very little latency to completion calls. Time to generate completion tokens is much longer, as tokens are generated one at a time. Longer generation lengths will accumulate latency due to generation required for each token.92> **Intuition**: Prompt tokens add little latency to completion calls. Time to generate completion tokens is much longer, as tokens are generated one at a time. Longer generation lengths will accumulate latency due to generation required for each token.

89 93 

90### Common factors affecting latency and possible mitigation techniques94### Common factors affecting latency and possible mitigation techniques

91 95 

92Now that we have looked at the basics of latency, let’s take a look at various factors that can affect latency, broadly ordered from most impactful to least impactful.96Now that we have looked at the basics of latency, let’s take a look at various factors that can affect latency, broadly ordered from greatest to least impact.

93 97 

94#### Model98#### Model

95 99 


124 128 

125One of the challenges of moving your prototype into production is budgeting for the costs associated with running your application. OpenAI offers a [pay-as-you-go pricing model](https://openai.com/api/pricing/), with prices per 1,000 tokens (roughly equal to 750 words). To estimate your costs, you will need to project the token utilization. Consider factors such as traffic levels, the frequency with which users will interact with your application, and the amount of data you will be processing.129One of the challenges of moving your prototype into production is budgeting for the costs associated with running your application. OpenAI offers a [pay-as-you-go pricing model](https://openai.com/api/pricing/), with prices per 1,000 tokens (roughly equal to 750 words). To estimate your costs, you will need to project the token utilization. Consider factors such as traffic levels, the frequency with which users will interact with your application, and the amount of data you will be processing.

126 130 

127**One useful framework for thinking about reducing costs is to consider costs as a function of the number of tokens and the cost per token.** There are two potential avenues for reducing costs using this framework. First, you could work to reduce the cost per token by switching to smaller models for some tasks in order to reduce costs. Alternatively, you could try to reduce the number of tokens required. There are a few ways you could do this, such as by using shorter prompts, [fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization) models, or caching common user queries so that they don't need to be processed repeatedly.131**One useful framework for thinking about reducing costs is to consider costs as a function of the number of tokens and the cost per token.** You can approach cost reduction in two ways using this framework. First, you could work to reduce the cost per token by switching to smaller models for some tasks in order to reduce costs. Alternatively, you could try to reduce the number of tokens required. You could do this in a few ways, such as by using shorter prompts, [fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization) models, or caching common user queries so that they don't need to be processed repeatedly.

128 132 

129You can experiment with our interactive [tokenizer tool](https://platform.openai.com/tokenizer) to help you estimate costs. The API and playground also returns token counts as part of the response. Once you’ve got things working with our most capable model, you can see if the other models can produce the same results with lower latency and costs. Learn more in our [token usage help article](https://help.openai.com/en/articles/6614209-how-do-i-check-my-token-usage).133You can experiment with our interactive [tokenizer tool](https://platform.openai.com/tokenizer) to help you estimate costs. The API and playground also returns token counts as part of the response. Once you’ve got things working with our most capable model, you can see if the other models can produce the same results with lower latency and costs. Learn more in our [token usage help article](https://help.openai.com/en/articles/6614209-how-do-i-check-my-token-usage).

130 134 

131## MLOps strategy135## MLOps strategy

132 136 

133As you move your prototype into production, you may want to consider developing an MLOps strategy. MLOps (machine learning operations) refers to the process of managing the end-to-end life cycle of your machine learning models, including any models you may be fine-tuning using our API. There are a number of areas to consider when designing your MLOps strategy. These include137As you move your prototype into production, you may want to consider developing an MLOps strategy. MLOps (machine learning operations) refers to the process of managing the end-to-end life cycle of your machine learning models, including any models you may be fine-tuning using our API. Consider the following areas when designing your MLOps strategy:

134 138 

135- Data and model management: managing the data used to train or fine-tune your model and tracking versions and changes.139- Data and model management: managing the data used to train or fine-tune your model and tracking versions and changes.

136- Model monitoring: tracking your model's performance over time and detecting any potential issues or degradation.140- Model monitoring: tracking your model's performance over time and detecting any potential issues or degradation.

Details

12 12 

13Prompt caching is enabled by default for supported OpenAI models. Use the [Prompt Caching Dashboard](https://platform.openai.com/usage?usage_section=prompt-caching) to monitor cache read hit rates and use the [Prompt Cache Diagnostics tool](https://developers.openai.com/api/docs/guides/prompt-caching/diagnostics) to diagnose cache misses and improve cache reuse.13Prompt caching is enabled by default for supported OpenAI models. Use the [Prompt Caching Dashboard](https://platform.openai.com/usage?usage_section=prompt-caching) to monitor cache read hit rates and use the [Prompt Cache Diagnostics tool](https://developers.openai.com/api/docs/guides/prompt-caching/diagnostics) to diagnose cache misses and improve cache reuse.

14 14 

15Agents API model calls use the same prompt-caching behavior as the Responses API. Reusing context within a session can preserve a shared prompt prefix, but maintaining a session doesn't guarantee a cache hit. See [Observability and usage](https://developers.openai.com/api/docs/guides/agents-api/observability) for session usage fields and subagent accounting.

16 

17Prompt caching pricing varies by model. See [API pricing](https://developers.openai.com/api/docs/pricing) for current cached-input and cache-write rates. Cache-write pricing is not an additive fee: input tokens use the uncached-input, cached-input, or cache-write rate.

18 

15## What is the prompt cache?19## What is the prompt cache?

16 20 

17When the model processes input tokens, it must calculate intermediate states, known as key-value (KV) states. These states let the model refer back to earlier tokens while processing new input and generating output tokens.21When the model processes input tokens, it must calculate intermediate states, known as key-value (KV) states. These states let the model refer back to earlier tokens while processing new input and generating output tokens.


30 34 

31Cache reuse requires the entire rendered prefix to match. If content or a relevant setting changes before a breakpoint, the prefix after that change cannot match the existing cache entry.35Cache reuse requires the entire rendered prefix to match. If content or a relevant setting changes before a breakpoint, the prefix after that change cannot match the existing cache entry.

32 36 

37<a id="cache-affecting-settings"></a>

38 

39 

40 

33<a id="which-settings-affect-the-cached-prefix"></a>41<a id="which-settings-affect-the-cached-prefix"></a>

34 42 

35 43 


241 249 

242For models before GPT-5.6, the minimum cacheable input length varies with request settings, including tools, images, output schemas, reasoning effort, and verbosity.250For models before GPT-5.6, the minimum cacheable input length varies with request settings, including tools, images, output schemas, reasoning effort, and verbosity.

243 251 

252 

253 

254Ask ChatGPT to find the cache minimum for my request

255 

256 

257 

244<a id="best-practices"></a>258<a id="best-practices"></a>

245 259 

246## How to optimize prompt caching260## How to optimize prompt caching


333 347 

334 348 

335 349 

350<a id="tools"></a>

351 

336 352 

337 353 

338<a id="manage-tools-with-append-only-updates"></a>354<a id="manage-tools-with-append-only-updates"></a>


540 cache_write_tokens = details.cache_write_tokens556 cache_write_tokens = details.cache_write_tokens

541 ordinary_input_tokens = input_tokens - cached_tokens - cache_write_tokens557 ordinary_input_tokens = input_tokens - cached_tokens - cache_write_tokens

542 558 

543 weighted_input_tokens =559 weighted_input_tokens = ordinary_input_tokens +

544 ordinary_input_tokens +

545 (cached_tokens * cache_input_multiplier) +560 (cached_tokens * cache_input_multiplier) +

546 (cache_write_tokens * cache_write_multiplier)561 (cache_write_tokens * cache_write_multiplier)

547 (weighted_input_tokens * input_price_per_million) / 1_000_000562 (weighted_input_tokens * input_price_per_million) / 1_000_000

Details

312response = client.responses.create(312response = client.responses.create(

313 model: "gpt-6-astra",313 model: "gpt-6-astra",

314 instructions: "Talk like a pirate.",314 instructions: "Talk like a pirate.",

315 reasoning: {effort: :low},315 reasoning: { effort: :low },

316 input: "Are semicolons optional in JavaScript?"316 input: "Are semicolons optional in JavaScript?"

317)317)

318 318 


488client = OpenAI::Client.new488client = OpenAI::Client.new

489response = client.responses.create(489response = client.responses.create(

490 model: "gpt-6-astra",490 model: "gpt-6-astra",

491 reasoning: {effort: :low},491 reasoning: { effort: :low },

492 input: [492 input: [

493 {role: :developer, content: "Talk like a pirate."},493 {

494 {role: :user, content: "Are semicolons optional in JavaScript?"}494 role: :developer,

495 content: "Talk like a pirate."

496 },

497 {

498 role: :user,

499 content: "Are semicolons optional in JavaScript?"

500 }

495 ]501 ]

496)502)

497 503 

Details

286 completion = client.chat.completions.create(286 completion = client.chat.completions.create(

287 model: "gpt-6-astra",287 model: "gpt-6-astra",

288 messages: [288 messages: [

289 {role: :system, content: meta_prompt},289 {

290 role: :system,

291 content: meta_prompt

292 },

290 {293 {

291 role: :user,294 role: :user,

292 content: "Task, Goal, or Current Prompt:\n#{task_or_prompt}"295 content: "Task, Goal, or Current Prompt:\n#{task_or_prompt}"


533 completion = client.chat.completions.create(536 completion = client.chat.completions.create(

534 model: "gpt-6-astra",537 model: "gpt-6-astra",

535 messages: [538 messages: [

536 {role: :system, content: meta_prompt},539 {

540 role: :system,

541 content: meta_prompt

542 },

537 {543 {

538 role: :user,544 role: :user,

539 content: "Task, Goal, or Current Prompt:\n#{task_or_prompt}"545 content: "Task, Goal, or Current Prompt:\n#{task_or_prompt}"


894 completion = client.chat.completions.create(900 completion = client.chat.completions.create(

895 model: "gpt-6-astra",901 model: "gpt-6-astra",

896 messages: [902 messages: [

897 {role: :system, content: meta_prompt},903 {

904 role: :system,

905 content: meta_prompt

906 },

898 {907 {

899 role: :user,908 role: :user,

900 content: "Task, Goal, or Current Prompt:\n#{task_or_prompt}"909 content: "Task, Goal, or Current Prompt:\n#{task_or_prompt}"


1218 completion = client.chat.completions.create(1227 completion = client.chat.completions.create(

1219 model: "gpt-6-astra",1228 model: "gpt-6-astra",

1220 messages: [1229 messages: [

1221 {role: :system, content: meta_prompt},1230 {

1231 role: :system,

1232 content: meta_prompt

1233 },

1222 {1234 {

1223 role: :user,1235 role: :user,

1224 content: "Task, Goal, or Current Prompt:\n#{task_or_prompt}"1236 content: "Task, Goal, or Current Prompt:\n#{task_or_prompt}"


2205 }2217 }

2206 },2218 },

2207 "items" => {2219 "items" => {

2208 "anyOf" => [{2220 "anyOf" => [

2221 {

2209 "$ref" => "#/$defs/schema_definition"2222 "$ref" => "#/$defs/schema_definition"

2210 }, {2223 }, {

2211 "type" => "array",2224 "type" => "array",

2212 "items" => {2225 "items" => {

2213 "$ref" => "#/$defs/schema_definition"2226 "$ref" => "#/$defs/schema_definition"

2214 }2227 }

2215 }]2228 }

2229 ]

2216 },2230 },

2217 "required" => {2231 "required" => {

2218 "type" => "array",2232 "type" => "array",


2251 }2265 }

2252 },2266 },

2253 "items" => {2267 "items" => {

2254 "anyOf" => [{2268 "anyOf" => [

2269 {

2255 "$ref" => "#/$defs/schema_definition"2270 "$ref" => "#/$defs/schema_definition"

2256 }, {2271 }, {

2257 "type" => "array",2272 "type" => "array",

2258 "items" => {2273 "items" => {

2259 "$ref" => "#/$defs/schema_definition"2274 "$ref" => "#/$defs/schema_definition"

2260 }2275 }

2261 }]2276 }

2277 ]

2262 },2278 },

2263 "required" => {2279 "required" => {

2264 "type" => "array",2280 "type" => "array",


2456client = OpenAI::Client.new2472client = OpenAI::Client.new

2457completion = client.chat.completions.create(2473completion = client.chat.completions.create(

2458 model: "gpt-5.6-terra",2474 model: "gpt-5.6-terra",

2459 response_format: {type: :json_schema, json_schema: META_SCHEMA},2475 response_format: {

2476 type: :json_schema,

2477 json_schema: META_SCHEMA

2478 },

2460 messages: [2479 messages: [

2461 {role: :system, content: META_PROMPT},2480 {

2462 {role: :user, content: "Description: Schedule a meeting with a title and start time."}2481 role: :system,

2482 content: META_PROMPT

2483 },

2484 {

2485 role: :user,

2486 content: "Description: Schedule a meeting with a title and start time."

2487 }

2463 ]2488 ]

2464)2489)

2465message = completion.choices.fetch(0).message2490message = completion.choices.fetch(0).message


3166 }3192 }

3167 },3193 },

3168 "items" => {3194 "items" => {

3169 "anyOf" => [{3195 "anyOf" => [

3196 {

3170 "$ref" => "#/$defs/schema_definition"3197 "$ref" => "#/$defs/schema_definition"

3171 }, {3198 }, {

3172 "type" => "array",3199 "type" => "array",

3173 "items" => {3200 "items" => {

3174 "$ref" => "#/$defs/schema_definition"3201 "$ref" => "#/$defs/schema_definition"

3175 }3202 }

3176 }]3203 }

3204 ]

3177 },3205 },

3178 "required" => {3206 "required" => {

3179 "type" => "array",3207 "type" => "array",


3332client = OpenAI::Client.new3360client = OpenAI::Client.new

3333completion = client.chat.completions.create(3361completion = client.chat.completions.create(

3334 model: "gpt-5.6-terra",3362 model: "gpt-5.6-terra",

3335 response_format: {type: :json_schema, json_schema: META_SCHEMA},3363 response_format: {

3364 type: :json_schema,

3365 json_schema: META_SCHEMA

3366 },

3336 messages: [3367 messages: [

3337 {role: :system, content: META_PROMPT},3368 {

3338 {role: :user, content: "Description: Schedule a meeting with a title and start time."}3369 role: :system,

3370 content: META_PROMPT

3371 },

3372 {

3373 role: :user,

3374 content: "Description: Schedule a meeting with a title and start time."

3375 }

3339 ]3376 ]

3340)3377)

3341message = completion.choices.fetch(0).message3378message = completion.choices.fetch(0).message

Details

32```32```

33 33 

34```python34```python

35import os35# Replace the illustrative IDs and URLs below with your own resource values.

36 36 

37from openai import OpenAI37from openai import OpenAI

38 38 

39client = OpenAI()39client = OpenAI()

40prompt_id = os.environ["OPENAI_PROMPT_ID"]40prompt_id = "pmpt_123"

41 41 

42response = client.responses.create(42response = client.responses.create(

43 prompt={43 prompt={

Details

120```120```

121 121 

122```ruby122```ruby

123# Replace the illustrative IDs and URLs below with your own resource values.

123connection.session.update(124connection.session.update(

124 type: :realtime,125 type: :realtime,

125 model: "gpt-realtime-2.1",126 model: "gpt-realtime-2.1",

126 output_modalities: [:audio],127 output_modalities: [:audio],

127 audio: {128 audio: {

128 input: {129 input: {

129 format: {type: :"audio/pcm", rate: 24_000},130 format: {

130 turn_detection: {type: :semantic_vad}131 type: :"audio/pcm",

132 rate: 24_000

133 },

134 turn_detection: { type: :semantic_vad }

131 },135 },

132 output: {136 output: {

133 format: {type: :"audio/pcm", rate: 24_000},137 format: {

138 type: :"audio/pcm",

139 rate: 24_000

140 },

134 voice: :marin141 voice: :marin

135 }142 }

136 },143 },

137 prompt: {144 prompt: {

138 id: ENV.fetch("OPENAI_REALTIME_PROMPT_ID"),145 id: "pmpt_123",

139 version: "89",146 version: "89",

140 variables: {city: "Paris"}147 variables: { city: "Paris" }

141 },148 },

142 instructions: "Speak clearly and briefly. Confirm before taking action."149 instructions: "Speak clearly and briefly. Confirm before taking action."

143)150)


212connection.conversation.items.create(219connection.conversation.items.create(

213 type: :message,220 type: :message,

214 role: :user,221 role: :user,

215 content: [{type: :input_text, text: "What is the weather like today?"}]222 content: [

223 {

224 type: :input_text,

225 text: "What is the weather like today?"

226 }

227 ]

216)228)

217```229```

218 230 


665connection.conversation.items.create(677connection.conversation.items.create(

666 type: :message,678 type: :message,

667 role: :user,679 role: :user,

668 content: [{type: :input_audio, audio: audio}]680 content: [

681 {

682 type: :input_audio,

683 audio: audio

684 }

685 ]

669)686)

670```687```

671 688 


753 type: :message,770 type: :message,

754 role: :user,771 role: :user,

755 content: [772 content: [

756 {type: :input_image, image_url: "data:image/png;base64,#{encoded_image}"},773 {

757 {type: :input_text, text: "Describe this image."}774 type: :input_image,

775 image_url: "data:image/png;base64,#{encoded_image}"

776 },

777 {

778 type: :input_text,

779 text: "Describe this image."

780 }

758 ]781 ]

759)782)

760connection.response.create(output_modalities: [:text])783connection.response.create(output_modalities: [:text])


845```ruby868```ruby

846connection.response.create(869connection.response.create(

847 conversation: :none,870 conversation: :none,

848 metadata: {topic: "classification"},871 metadata: { topic: "classification" },

849 output_modalities: [:text],872 output_modalities: [:text],

850 instructions: "Classify the conversation as support or sales."873 instructions: "Classify the conversation as support or sales."

851)874)


983```ruby1006```ruby

984connection.response.create(1007connection.response.create(

985 conversation: :none,1008 conversation: :none,

986 metadata: {topic: "classification"},1009 metadata: { topic: "classification" },

987 output_modalities: [:text],1010 output_modalities: [:text],

988 input: [1011 input: [

989 {type: :item_reference, id: ENV.fetch("OPENAI_REALTIME_CONTEXT_ITEM_ID")},1012 {

1013 type: :item_reference,

1014 id: existing_item_id

1015 },

990 {1016 {

991 type: :message,1017 type: :message,

992 role: :user,1018 role: :user,

993 content: [{type: :input_text, text: "Classify this issue: my order is late."}]1019 content: [

1020 {

1021 type: :input_text,

1022 text: "Classify this issue: my order is late."

1023 }

1024 ]

994 }1025 }

995 ]1026 ]

996)1027)

Details

89connection.session.update(89connection.session.update(

90 type: :realtime,90 type: :realtime,

91 model: "gpt-realtime-2.1",91 model: "gpt-realtime-2.1",

92 tools: [{92 tools: [

93 {

93 type: :function,94 type: :function,

94 name: "lookup_order",95 name: "lookup_order",

95 description: "Look up an order by its order number.",96 description: "Look up an order by its order number.",


103 },104 },

104 required: ["order_number"]105 required: ["order_number"]

105 }106 }

106 }],107 }

108 ],

107 tool_choice: :auto109 tool_choice: :auto

108)110)

109```111```


228 type: :realtime,230 type: :realtime,

229 model: "gpt-realtime-2.1",231 model: "gpt-realtime-2.1",

230 output_modalities: [:text],232 output_modalities: [:text],

231 tools: [{233 tools: [

234 {

232 type: :mcp,235 type: :mcp,

233 server_label: "openai_docs",236 server_label: "openai_docs",

234 server_url: "https://developers.openai.com/mcp",237 server_url: "https://developers.openai.com/mcp",

235 allowed_tools: ["search_openai_docs", "fetch_openai_doc"],238 allowed_tools: ["search_openai_docs", "fetch_openai_doc"],

236 require_approval: :never239 require_approval: :never

237 }]240 }

241 ]

238)242)

239```243```

240 244 


305 type: :realtime,309 type: :realtime,

306 model: "gpt-realtime-2.1",310 model: "gpt-realtime-2.1",

307 output_modalities: [:text],311 output_modalities: [:text],

308 tools: [{312 tools: [

313 {

309 type: :mcp,314 type: :mcp,

310 server_label: "google_calendar",315 server_label: "google_calendar",

311 connector_id: "connector_googlecalendar",316 connector_id: "connector_googlecalendar",

312 authorization: access_token,317 authorization: access_token,

313 allowed_tools: ["search_events", "read_event"],318 allowed_tools: ["search_events", "read_event"],

314 require_approval: :never319 require_approval: :never

315 }]320 }

321 ]

316)322)

317```323```

318 324 


503 puts("MCP tools ready for item: #{event.item_id}")509 puts("MCP tools ready for item: #{event.item_id}")

504 connection.response.create(510 connection.response.create(

505 output_modalities: [:text],511 output_modalities: [:text],

506 input: [{512 input: [

513 {

507 type: :message,514 type: :message,

508 role: :user,515 role: :user,

509 content: [{516 content: [

517 {

510 type: :input_text,518 type: :input_text,

511 text: "Which Realtime API transport should browser clients use?"519 text: "Which Realtime API transport should browser clients use?"

512 }]520 }

513 }],521 ]

522 }

523 ],

514 tool_choice: :required524 tool_choice: :required

515 )525 )

516 when OpenAI::Realtime::ConversationItemDone526 when OpenAI::Realtime::ConversationItemDone


585```595```

586 596 

587```python597```python

598# Use the ID from the received MCP approval-request item.

588def approve_mcp_request(ws, approval_request_id):599def approve_mcp_request(ws, approval_request_id):

589 event = {600 event = {

590 "type": "conversation.item.create",601 "type": "conversation.item.create",


600```611```

601 612 

602```ruby613```ruby

614# Use the ID from the received MCP approval-request item.

603approval_request_id = item.id615approval_request_id = item.id

604 616 

605connection.conversation.items.create(617connection.conversation.items.create(


686```ruby698```ruby

687connection.response.create(699connection.response.create(

688 output_modalities: [:text],700 output_modalities: [:text],

689 input: [{701 input: [

702 {

690 type: :message,703 type: :message,

691 role: :user,704 role: :user,

692 content: [{705 content: [

706 {

693 type: :input_text,707 type: :input_text,

694 text: "Which Realtime API transport should browser clients use?"708 text: "Which Realtime API transport should browser clients use?"

695 }]709 }

696 }],710 ]

697 tools: [{711 }

712 ],

713 tools: [

714 {

698 type: :mcp,715 type: :mcp,

699 server_label: "openai_docs",716 server_label: "openai_docs",

700 server_url: "https://developers.openai.com/mcp",717 server_url: "https://developers.openai.com/mcp",

701 allowed_tools: ["search_openai_docs", "fetch_openai_doc"],718 allowed_tools: ["search_openai_docs", "fetch_openai_doc"],

702 require_approval: :never719 require_approval: :never

703 }]720 }

721 ]

704)722)

705```723```

706 724 


781```ruby799```ruby

782connection.response.create(800connection.response.create(

783 output_modalities: [:text],801 output_modalities: [:text],

784 input: [{802 input: [

803 {

785 type: :message,804 type: :message,

786 role: :user,805 role: :user,

787 content: [{type: :input_text, text: "Check my schedule this afternoon."}]806 content: [

788 }],807 {

789 tools: [{type: :mcp, server_label: "google_calendar"}]808 type: :input_text,

809 text: "Check my schedule this afternoon."

810 }

811 ]

812 }

813 ],

814 tools: [

815 {

816 type: :mcp,

817 server_label: "google_calendar"

818 }

819 ]

790)820)

791```821```

792 822 

Details

162require "json"162require "json"

163 163 

164endpoint = Async::HTTP::Endpoint.parse("wss://api.openai.com/v1/realtime/translations?model=gpt-realtime-translate", timeout: 10, alpn_protocols: ["http/1.1"])164endpoint = Async::HTTP::Endpoint.parse("wss://api.openai.com/v1/realtime/translations?model=gpt-realtime-translate", timeout: 10, alpn_protocols: ["http/1.1"])

165headers = {"Authorization" => "Bearer #{ENV.fetch("OPENAI_API_KEY")}", "OpenAI-Safety-Identifier" => "hashed-user-id"}165headers = {

166 "Authorization" => "Bearer #{ENV.fetch("OPENAI_API_KEY")}",

167 "OpenAI-Safety-Identifier" => "hashed-user-id"

168}

166Sync do |task|169Sync do |task|

167 task.with_timeout(120) do170 task.with_timeout(120) do

168 Async::WebSocket::Client.connect(endpoint, headers: headers) do |connection|171 Async::WebSocket::Client.connect(endpoint, headers: headers) do |connection|


219```223```

220 224 

221```ruby225```ruby

222connection.write(JSON.generate(type: "session.update", session: {audio: {output: {language: "es"}}}))226connection.write(JSON.generate(type: "session.update", session: { audio: { output: { language: "es" } } }))

223connection.flush227connection.flush

224```228```

225 229 

Details

159 159 

160response = client.responses.create(160response = client.responses.create(

161 model: "gpt-6-astra",161 model: "gpt-6-astra",

162 reasoning: {effort: :low},162 reasoning: { effort: :low },

163 input: prompt163 input: prompt

164)164)

165 165 


486response = client.responses.create(486response = client.responses.create(

487 model: "gpt-6-astra",487 model: "gpt-6-astra",

488 max_output_tokens: 300,488 max_output_tokens: 300,

489 reasoning: {effort: :medium},489 reasoning: { effort: :medium },

490 input: prompt490 input: prompt

491)491)

492 492 


666first = client.responses.create(666first = client.responses.create(

667 model: "gpt-5.6",667 model: "gpt-5.6",

668 input: "Inspect this repository and identify the likely bug.",668 input: "Inspect this repository and identify the likely bug.",

669 reasoning: {context: :current_turn}669 reasoning: { context: :current_turn }

670)670)

671 671 

672second = client.responses.create(672second = client.responses.create(

673 model: "gpt-5.6",673 model: "gpt-5.6",

674 previous_response_id: first.id,674 previous_response_id: first.id,

675 input: "Now patch the bug and explain the change.",675 input: "Now patch the bug and explain the change.",

676 reasoning: {context: :all_turns}676 reasoning: { context: :all_turns }

677)677)

678 678 

679puts(second.output_text)679puts(second.output_text)


710 710 

711```javascript711```javascript

712import OpenAI from "openai";712import OpenAI from "openai";

713import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";

713 714 

714const client = new OpenAI();715const client = new OpenAI();

715 716 


728 reasoning: { context: "current_turn" },729 reasoning: { context: "current_turn" },

729});730});

730 731 

731// Keep every output item, including encrypted reasoning and assistant phase.732// Keep replayable output, including encrypted reasoning and assistant phase.

732history.push(...first.output);733history.push(...toResponseInputItems(first.output));

733history.push({734history.push({

734 role: "user",735 role: "user",

735 content: "Now patch the bug and explain the change.",736 content: "Now patch the bug and explain the change.",


907 908 

908client = OpenAI::Client.new909client = OpenAI::Client.new

909history = [910history = [

910 {role: :user, content: "Inspect this repository and identify the likely bug."}911 {

912 role: :user,

913 content: "Inspect this repository and identify the likely bug."

914 }

911]915]

912 916 

913first = client.responses.create(917first = client.responses.create(

914 model: "gpt-5.6",918 model: "gpt-5.6",

915 store: false,919 store: false,

916 input: history,920 input: history,

917 reasoning: {context: :current_turn}921 reasoning: { context: :current_turn }

918)922)

919history.concat(first.output)923history.concat(first.output)

920history << {role: :user, content: "Now patch the bug and explain the change."}924history << {

925 role: :user,

926 content: "Now patch the bug and explain the change."

927}

921 928 

922second = client.responses.create(929second = client.responses.create(

923 model: "gpt-5.6",930 model: "gpt-5.6",

924 store: false,931 store: false,

925 input: history,932 input: history,

926 reasoning: {context: :all_turns}933 reasoning: { context: :all_turns }

927)934)

928 935 

929puts(second.output_text)936puts(second.output_text)


1292response = client.responses.create(1299response = client.responses.create(

1293 model: "gpt-6-astra",1300 model: "gpt-6-astra",

1294 input: "What is the capital of France?",1301 input: "What is the capital of France?",

1295 reasoning: {effort: :low, summary: :auto}1302 reasoning: {

1303 effort: :low,

1304 summary: :auto

1305 }

1296)1306)

1297 1307 

1298puts(response.output)1308puts(response.output)

Details

1185require "openai"1185require "openai"

1186 1186 

1187client = OpenAI::Client.new1187client = OpenAI::Client.new

1188file = client.vector_stores.files.update("file_123", vector_store_id: "vs_123", attributes: {category: "policy"})1188file = client.vector_stores.files.update("file_123", vector_store_id: "vs_123", attributes: { category: "policy" })

1189puts(file.id)1189puts(file.id)

1190```1190```

1191 1191 


1458batch = client.vector_stores.file_batches.create(1458batch = client.vector_stores.file_batches.create(

1459 "vs_123",1459 "vs_123",

1460 files: [1460 files: [

1461 {file_id: "file_123", attributes: {department: "finance"}},1461 {

1462 file_id: "file_123",

1463 attributes: { department: "finance" }

1464 },

1462 {1465 {

1463 file_id: "file_456",1466 file_id: "file_456",

1464 chunking_strategy: {1467 chunking_strategy: {


1791require "openai"1794require "openai"

1792 1795 

1793client = OpenAI::Client.new1796client = OpenAI::Client.new

1794file = client.vector_stores.files.create("<vector_store_id>", file_id: "file_123", attributes: {category: "policy"})1797file = client.vector_stores.files.create("<vector_store_id>", file_id: "file_123", attributes: { category: "policy" })

1795puts(file.id)1798puts(file.id)

1796```1799```

1797 1800 


1873client = OpenAI::Client.new1876client = OpenAI::Client.new

1874store = client.vector_stores.update(1877store = client.vector_stores.update(

1875 "vs_123",1878 "vs_123",

1876 expires_after: {anchor: :last_active_at, days: 7}1879 expires_after: {

1880 anchor: :last_active_at,

1881 days: 7

1882 }

1877)1883)

1878puts(store.expires_after)1884puts(store.expires_after)

1879```1885```


2041```2047```

2042 2048 

2043```python2049```python

2050# Use results and user_query from the preceding search step.

2044formatted_results = format_results(results.data)2051formatted_results = format_results(results.data)

2045 2052 

2046"\n".join("\n".join(c.text for c in result.content) for result in results.data)2053"\n".join("\n".join(c.text for c in result.content) for result in results.data)


2176 role: :developer,2183 role: :developer,

2177 content: "Answer the query concisely using only the provided sources."2184 content: "Answer the query concisely using only the provided sources."

2178 },2185 },

2179 {role: :user, content: "Sources: <sources>#{sources}</sources>\n\nQuery: #{query}"}2186 {

2187 role: :user,

2188 content: "Sources: <sources>#{sources}</sources>\n\nQuery: #{query}"

2189 }

2180 ]2190 ]

2181)2191)

2182puts(completion.choices.fetch(0).message.content)2192puts(completion.choices.fetch(0).message.content)


2258 {2268 {

2259 file_id: "file-12345",2269 file_id: "file-12345",

2260 filename: "woodchuck_policy.txt",2270 filename: "woodchuck_policy.txt",

2261 content: [{text: "Each passenger may carry up to two woodchucks."}]2271 content: [{ text: "Each passenger may carry up to two woodchucks." }]

2262 }2272 }

2263]2273]

2264 2274 

Details

140client = OpenAI::Client.new140client = OpenAI::Client.new

141completion = client.chat.completions.create(141completion = client.chat.completions.create(

142 model: "gpt-6-astra",142 model: "gpt-6-astra",

143 messages: [{role: :user, content: "Help me plan a study schedule."}],143 messages: [

144 {

145 role: :user,

146 content: "Help me plan a study schedule."

147 }

148 ],

144 safety_identifier: "user_1234"149 safety_identifier: "user_1234"

145)150)

146 151 

Details

205client = OpenAI::Client.new205client = OpenAI::Client.new

206completion = client.chat.completions.create(206completion = client.chat.completions.create(

207 model: "gpt-5.6-terra",207 model: "gpt-5.6-terra",

208 messages: [{role: :user, content: "Help me plan a study schedule."}],208 messages: [

209 {

210 role: :user,

211 content: "Help me plan a study schedule."

212 }

213 ],

209 safety_identifier: "user_1234"214 safety_identifier: "user_1234"

210)215)

211 216 

Details

54}54}

55```55```

56 56 

57After verifying and acknowledging the webhook, retrieve the alert in your background processing. Set `SAFETY_ALERT_ID` to `data.id`, not the event's `id`. Use an API key authorized for the same project with the `api.safety.alerts.read` permission:57After verifying and acknowledging the webhook, retrieve the alert in your background processing. Replace the illustrative `salert_123` value with `data.id` from the webhook. The event's `id` identifies the webhook event rather than the alert. Use an API key authorized for the same project with the `api.safety.alerts.read` permission:

58 58 

59```bash59```bash

60curl "https://api.openai.com/v1/safety/alerts/${SAFETY_ALERT_ID}" \60curl "https://api.openai.com/v1/safety/alerts/salert_123" \

61 -H "Authorization: Bearer ${OPENAI_API_KEY}"61 -H "Authorization: Bearer ${OPENAI_API_KEY}"

62```62```

63 63 

64Retrieve a project safety alert64Retrieve a project safety alert

65 65 

66```javascript66```javascript

67// Replace the illustrative IDs and URLs below with your own resource values.

67import OpenAI from "openai";68import OpenAI from "openai";

68 69 

69const client = new OpenAI();70const client = new OpenAI();

70const alertId = process.env.SAFETY_ALERT_ID;71const alertId = "salert_123";

71if (!alertId) throw new Error("Set SAFETY_ALERT_ID.");

72 72 

73const alert = await client.safety.alerts.retrieve(alertId);73const alert = await client.safety.alerts.retrieve(alertId);

74console.log(alert.error_type, alert.reason, alert.response_id);74console.log(alert.error_type, alert.reason, alert.response_id);

75```75```

76 76 

77```python77```python

78import os78# Replace the illustrative IDs and URLs below with your own resource values.

79 79 

80from openai import OpenAI80from openai import OpenAI

81 81 

82client = OpenAI()82client = OpenAI()

83alert = client.safety.alerts.retrieve(os.environ["SAFETY_ALERT_ID"])83alert = client.safety.alerts.retrieve("salert_123")

84print(alert.error_type, alert.reason)84print(alert.error_type, alert.reason)

85```85```

86 86 

87```go87```go

88// Replace the illustrative IDs and URLs below with your own resource values.

88package main89package main

89 90 

90import (91import (

91 "context"92 "context"

92 "fmt"93 "fmt"

93 "os"

94 94 

95 "github.com/openai/openai-go/v3"95 "github.com/openai/openai-go/v3"

96)96)

97 97 

98func main() {98func main() {

99 client := openai.NewClient()99 client := openai.NewClient()

100 alert, err := client.Safety.Alerts.Get(context.Background(), os.Getenv("SAFETY_ALERT_ID"))100 alert, err := client.Safety.Alerts.Get(context.Background(), "salert_123")

101 if err != nil {101 if err != nil {

102 panic(err)102 panic(err)

103 }103 }


108```108```

109 109 

110```java110```java

111// Replace the illustrative IDs and URLs below with your own resource values.

111import com.openai.models.safety.alerts.SafetyAlert;112import com.openai.models.safety.alerts.SafetyAlert;

112 113 

113SafetyAlert alert = client.safety().alerts().retrieve(System.getenv("SAFETY_ALERT_ID"));114SafetyAlert alert = client.safety().alerts().retrieve("salert_123");

114System.out.println(alert.errorType());115System.out.println(alert.errorType());

115alert.reason().ifPresent(System.out::println);116alert.reason().ifPresent(System.out::println);

116System.out.println(alert.requestPaused());117System.out.println(alert.requestPaused());

117```118```

118 119 

119```ruby120```ruby

121# Replace the illustrative IDs and URLs below with your own resource values.

120require "openai"122require "openai"

121 123 

122client = OpenAI::Client.new124client = OpenAI::Client.new

123alert = client.safety.alerts.retrieve(ENV.fetch("SAFETY_ALERT_ID"))125alert = client.safety.alerts.retrieve("salert_123")

124puts(alert.error_type)126puts(alert.error_type)

125puts(alert.reason)127puts(alert.reason)

126puts(alert.request_paused)128puts(alert.request_paused)

Details

490 known_speaker_names: ["agent"],490 known_speaker_names: ["agent"],

491 known_speaker_references: ["data:audio/wav;base64,#{speaker_reference}"]491 known_speaker_references: ["data:audio/wav;base64,#{speaker_reference}"]

492)492)

493segments = Array(transcript.to_h.fetch(:segments) do493segments = Array(

494 transcript.to_h.fetch(:segments) do

494 raise "The transcription did not include speaker segments"495 raise "The transcription did not include speaker segments"

495end)496 end

497)

496segments.each do |segment|498segments.each do |segment|

497 segment = Hash.try_convert(segment) or raise "Invalid speaker segment"499 segment = Hash.try_convert(segment) or raise "Invalid speaker segment"

498 puts(500 puts(

499 "#{segment.fetch(:speaker)}: #{segment.fetch(:text)} " \501 "#{segment.fetch(:speaker)}: #{segment.fetch(:text)} " \

500 "(#{segment.fetch(:start)}-#{segment.fetch(:end)})"502 "(#{segment.fetch(:start)}-#{segment.fetch(:end_)})"

501 )503 )

502end504end

503```505```

guides/steering.md +12 −116

Details

49 49 

50## Run a complete example50## Run a complete example

51 51 

52The .NET SDK does not provide a Responses WebSocket client, so a C# SDK variant is not available for this example.

53 

52Update a project plan while it runs54Update a project plan while it runs

53 55 

54```javascript56```javascript


188asyncio.run(main())190asyncio.run(main())

189```191```

190 192 

191```csharp

192using System.Net.WebSockets;

193using System.Text.Json;

194 

195// Set OPENAI_API_KEY before running this example.

196// ClientWebSocket is built in; no extra package is required.

197 

198using ClientWebSocket socket = new();

199string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

200socket.Options.SetRequestHeader("Authorization", $"Bearer {key}");

201Uri endpoint = new("wss://api.openai.com/v1/responses");

202 

203using CancellationTokenSource timeout = new(TimeSpan.FromSeconds(120));

204await socket.ConnectAsync(endpoint, timeout.Token);

205string? initialResponseId = null;

206string? successorResponseId = null;

207 

208await SendAsync(new

209{

210 type = "response.create",

211 model = "gpt-6-astra",

212 reasoning = new { effort = "medium" },

213 input = "Draft a project plan for building a task-tracking app.",

214});

215 

216while (true)

217{

218 using JsonDocument message = await ReceiveAsync();

219 JsonElement data = message.RootElement;

220 string? eventType = data.GetProperty("type").GetString();

221 if (eventType == "response.created")

222 {

223 string? responseId = data.GetProperty("response").GetProperty("id").GetString();

224 if (initialResponseId is null)

225 {

226 initialResponseId = responseId;

227 // Simulate a user adding instructions while the response runs.

228 await SendAsync(new

229 {

230 type = "response.steer",

231 previous_response_id = initialResponseId,

232 input = "Keep the scope small enough for one developer to finish in two weeks.",

233 });

234 }

235 else

236 {

237 successorResponseId = responseId;

238 }

239 }

240 else if (eventType is "response.steer.failed" or "response.failed" or "error")

241 {

242 throw new InvalidOperationException(data.GetRawText());

243 }

244 else if (eventType == "response.incomplete")

245 {

246 JsonElement response = data.GetProperty("response");

247 if (response.GetProperty("id").GetString() != initialResponseId

248 || !response.TryGetProperty("incomplete_details", out JsonElement details)

249 || !details.TryGetProperty("reason", out JsonElement reason)

250 || reason.GetString() != "steered")

251 {

252 throw new InvalidOperationException(data.GetRawText());

253 }

254 }

255 else if (eventType == "response.completed"

256 && data.GetProperty("response").GetProperty("id").GetString() == successorResponseId)

257 {

258 foreach (JsonElement item in data.GetProperty("response").GetProperty("output").EnumerateArray())

259 {

260 if (item.GetProperty("type").GetString() != "message") continue;

261 foreach (JsonElement part in item.GetProperty("content").EnumerateArray())

262 {

263 if (part.GetProperty("type").GetString() == "output_text")

264 {

265 Console.Write(part.GetProperty("text").GetString());

266 }

267 }

268 }

269 Console.WriteLine();

270 break;

271 }

272 // Acceptance only queues the input. Keep reading past the first response.

273}

274 

275async Task SendAsync<T>(T data)

276{

277 byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(data);

278 await socket.SendAsync(bytes.AsMemory(), WebSocketMessageType.Text, true, timeout.Token);

279}

280 

281async Task<JsonDocument> ReceiveAsync()

282{

283 using MemoryStream message = new();

284 byte[] buffer = new byte[8192];

285 ValueWebSocketReceiveResult result;

286 do

287 {

288 result = await socket.ReceiveAsync(buffer.AsMemory(), timeout.Token);

289 if (result.MessageType == WebSocketMessageType.Close)

290 {

291 throw new InvalidOperationException(

292 "Connection closed before the steered response finished.");

293 }

294 message.Write(buffer, 0, result.Count);

295 } while (!result.EndOfMessage);

296 message.Position = 0;

297 return await JsonDocument.ParseAsync(message, cancellationToken: timeout.Token);

298}

299```

300 

301```ruby193```ruby

302require "async"194require "async"

303require "async/http/endpoint"195require "async/http/endpoint"


305require "json"197require "json"

306 198 

307endpoint = Async::HTTP::Endpoint.parse("wss://api.openai.com/v1/responses", timeout: 10, alpn_protocols: ["http/1.1"])199endpoint = Async::HTTP::Endpoint.parse("wss://api.openai.com/v1/responses", timeout: 10, alpn_protocols: ["http/1.1"])

308headers = {"Authorization" => "Bearer #{ENV.fetch("OPENAI_API_KEY")}"}200headers = { "Authorization" => "Bearer #{ENV.fetch("OPENAI_API_KEY")}" }

309Sync do |task|201Sync do |task|

310 task.with_timeout(120) do202 task.with_timeout(120) do

311 Async::WebSocket::Client.connect(endpoint, headers: headers) do |connection|203 Async::WebSocket::Client.connect(endpoint, headers: headers) do |connection|

312 connection.write(JSON.generate(204 connection.write(

313 type: "response.create", model: "gpt-6-astra", reasoning: {effort: "medium"},205 JSON.generate(

206 type: "response.create", model: "gpt-6-astra", reasoning: { effort: "medium" },

314 input: "Draft a project plan for building a task-tracking app."207 input: "Draft a project plan for building a task-tracking app."

315 ))208 )

209 )

316 connection.flush210 connection.flush

317 state = {}211 state = {}

318 while (message = connection.read)212 while (message = connection.read)


322 when "response.created"216 when "response.created"

323 if !state[:initial_id]217 if !state[:initial_id]

324 state[:initial_id] = response.fetch("id")218 state[:initial_id] = response.fetch("id")

325 connection.write(JSON.generate(219 connection.write(

220 JSON.generate(

326 type: "response.steer", previous_response_id: state[:initial_id],221 type: "response.steer", previous_response_id: state[:initial_id],

327 input: "Keep the scope small enough for one developer to finish in two weeks."222 input: "Keep the scope small enough for one developer to finish in two weeks."

328 ))223 )

224 )

329 connection.flush225 connection.flush

330 else226 else

331 state[:successor_id] = response.fetch("id")227 state[:successor_id] = response.fetch("id")

Details

242event_schema = {242event_schema = {

243 type: :object,243 type: :object,

244 properties: {244 properties: {

245 name: {type: :string},245 name: { type: :string },

246 date: {type: :string},246 date: { type: :string },

247 participants: {type: :array, items: {type: :string}}247 participants: {

248 type: :array,

249 items: { type: :string }

250 }

248 },251 },

249 required: %w[name date participants],252 required: %w[name date participants],

250 additionalProperties: false253 additionalProperties: false


253response = client.responses.create(256response = client.responses.create(

254 model: "gpt-6-astra",257 model: "gpt-6-astra",

255 input: [258 input: [

256 {role: :system, content: "Extract the event information."},259 {

257 {role: :user, content: "Alice and Bob are going to a science fair on Friday."}260 role: :system,

261 content: "Extract the event information."

262 },

263 {

264 role: :user,

265 content: "Alice and Bob are going to a science fair on Friday."

266 }

258 ],267 ],

259 text: {268 text: {

260 format: {269 format: {


615step_schema = {624step_schema = {

616 type: :object,625 type: :object,

617 properties: {626 properties: {

618 explanation: {type: :string},627 explanation: { type: :string },

619 output: {type: :string}628 output: { type: :string }

620 },629 },

621 required: %w[explanation output],630 required: %w[explanation output],

622 additionalProperties: false631 additionalProperties: false


624math_schema = {633math_schema = {

625 type: :object,634 type: :object,

626 properties: {635 properties: {

627 steps: {type: :array, items: step_schema},636 steps: {

628 final_answer: {type: :string}637 type: :array,

638 items: step_schema

639 },

640 final_answer: { type: :string }

629 },641 },

630 required: %w[steps final_answer],642 required: %w[steps final_answer],

631 additionalProperties: false643 additionalProperties: false


638 role: :system,650 role: :system,

639 content: "You are a helpful math tutor. Guide the user through the solution step by step."651 content: "You are a helpful math tutor. Guide the user through the solution step by step."

640 },652 },

641 {role: :user, content: "How can I solve 8x + 7 = -23?"}653 {

654 role: :user,

655 content: "How can I solve 8x + 7 = -23?"

656 }

642 ],657 ],

643 text: {658 text: {

644 format: {659 format: {


1015paper_schema = {1030paper_schema = {

1016 type: :object,1031 type: :object,

1017 properties: {1032 properties: {

1018 title: {type: :string},1033 title: { type: :string },

1019 authors: {type: :array, items: {type: :string}},1034 authors: {

1020 abstract: {type: :string},1035 type: :array,

1021 keywords: {type: :array, items: {type: :string}}1036 items: { type: :string }

1037 },

1038 abstract: { type: :string },

1039 keywords: {

1040 type: :array,

1041 items: { type: :string }

1042 }

1022 },1043 },

1023 required: %w[title authors abstract keywords],1044 required: %w[title authors abstract keywords],

1024 additionalProperties: false1045 additionalProperties: false


1031 role: :system,1052 role: :system,

1032 content: "Extract structured data from the supplied research paper text."1053 content: "Extract structured data from the supplied research paper text."

1033 },1054 },

1034 {role: :user, content: research_paper}1055 {

1056 role: :user,

1057 content: research_paper

1058 }

1035 ],1059 ],

1036 text: {1060 text: {

1037 format: {1061 format: {


1433 type: :string,1457 type: :string,

1434 enum: %w[div button header section field form]1458 enum: %w[div button header section field form]

1435 },1459 },

1436 label: {type: :string},1460 label: { type: :string },

1437 children: {type: :array, items: {"$ref" => "#"}},1461 children: {

1462 type: :array,

1463 items: { "$ref" => "#" }

1464 },

1438 attributes: {1465 attributes: {

1439 type: :array,1466 type: :array,

1440 items: {1467 items: {

1441 type: :object,1468 type: :object,

1442 properties: {1469 properties: {

1443 name: {type: :string},1470 name: { type: :string },

1444 value: {type: :string}1471 value: { type: :string }

1445 },1472 },

1446 required: %w[name value],1473 required: %w[name value],

1447 additionalProperties: false1474 additionalProperties: false


1455response = client.responses.create(1482response = client.responses.create(

1456 model: "gpt-6-astra",1483 model: "gpt-6-astra",

1457 input: [1484 input: [

1458 {role: :system, content: "Convert the user request into a UI definition."},1485 {

1459 {role: :user, content: "Make a user profile form."}1486 role: :system,

1487 content: "Convert the user request into a UI definition."

1488 },

1489 {

1490 role: :user,

1491 content: "Make a user profile form."

1492 }

1460 ],1493 ],

1461 text: {1494 text: {

1462 format: {1495 format: {


1907 role: :system,1940 role: :system,

1908 content: "Determine whether the user input violates the guidelines and explain any violation."1941 content: "Determine whether the user input violates the guidelines and explain any violation."

1909 },1942 },

1910 {role: :user, content: "How do I prepare for a job interview?"}1943 {

1944 role: :user,

1945 content: "How do I prepare for a job interview?"

1946 }

1911 ],1947 ],

1912 text: {1948 text: {

1913 format: {1949 format: {


2311 items: {2347 items: {

2312 type: :object,2348 type: :object,

2313 properties: {2349 properties: {

2314 explanation: {type: :string},2350 explanation: { type: :string },

2315 output: {type: :string}2351 output: { type: :string }

2316 },2352 },

2317 required: %w[explanation output],2353 required: %w[explanation output],

2318 additionalProperties: false2354 additionalProperties: false

2319 }2355 }

2320 },2356 },

2321 final_answer: {type: :string}2357 final_answer: { type: :string }

2322 },2358 },

2323 required: %w[steps final_answer],2359 required: %w[steps final_answer],

2324 additionalProperties: false2360 additionalProperties: false


2331 role: :system,2367 role: :system,

2332 content: "You are a helpful math tutor. Guide the user through the solution step by step."2368 content: "You are a helpful math tutor. Guide the user through the solution step by step."

2333 },2369 },

2334 {role: :user, content: "How can I solve 8x + 7 = -23?"}2370 {

2371 role: :user,

2372 content: "How can I solve 8x + 7 = -23?"

2373 }

2335 ],2374 ],

2336 text: {2375 text: {

2337 format: {2376 format: {


2802step_schema = {2841step_schema = {

2803 type: :object,2842 type: :object,

2804 properties: {2843 properties: {

2805 explanation: {type: :string},2844 explanation: { type: :string },

2806 output: {type: :string}2845 output: { type: :string }

2807 },2846 },

2808 required: %w[explanation output],2847 required: %w[explanation output],

2809 additionalProperties: false2848 additionalProperties: false


2811math_schema = {2850math_schema = {

2812 type: :object,2851 type: :object,

2813 properties: {2852 properties: {

2814 steps: {type: :array, items: step_schema},2853 steps: {

2815 final_answer: {type: :string}2854 type: :array,

2855 items: step_schema

2856 },

2857 final_answer: { type: :string }

2816 },2858 },

2817 required: %w[steps final_answer],2859 required: %w[steps final_answer],

2818 additionalProperties: false2860 additionalProperties: false


2825 role: :system,2867 role: :system,

2826 content: "You are a helpful math tutor. Guide the user through the solution step by step."2868 content: "You are a helpful math tutor. Guide the user through the solution step by step."

2827 },2869 },

2828 {role: :user, content: "How can I solve 8x + 7 = -23?"}2870 {

2871 role: :user,

2872 content: "How can I solve 8x + 7 = -23?"

2873 }

2829 ],2874 ],

2830 max_output_tokens: 1_024,2875 max_output_tokens: 1_024,

2831 text: {2876 text: {


3172 items: {3217 items: {

3173 type: :object,3218 type: :object,

3174 properties: {3219 properties: {

3175 explanation: {type: :string},3220 explanation: { type: :string },

3176 output: {type: :string}3221 output: { type: :string }

3177 },3222 },

3178 required: %w[explanation output],3223 required: %w[explanation output],

3179 additionalProperties: false3224 additionalProperties: false

3180 }3225 }

3181 },3226 },

3182 final_answer: {type: :string}3227 final_answer: { type: :string }

3183 },3228 },

3184 required: %w[steps final_answer],3229 required: %w[steps final_answer],

3185 additionalProperties: false3230 additionalProperties: false


3192 role: :system,3237 role: :system,

3193 content: "You are a helpful math tutor. Guide the user through the solution step by step."3238 content: "You are a helpful math tutor. Guide the user through the solution step by step."

3194 },3239 },

3195 {role: :user, content: "How can I solve 8x + 7 = -23?"}3240 {

3241 role: :user,

3242 content: "How can I solve 8x + 7 = -23?"

3243 }

3196 ],3244 ],

3197 text: {3245 text: {

3198 format: {3246 format: {


3477entities_schema = {3525entities_schema = {

3478 type: :object,3526 type: :object,

3479 properties: {3527 properties: {

3480 attributes: {type: :array, items: {type: :string}},3528 attributes: {

3481 colors: {type: :array, items: {type: :string}},3529 type: :array,

3482 animals: {type: :array, items: {type: :string}}3530 items: { type: :string }

3531 },

3532 colors: {

3533 type: :array,

3534 items: { type: :string }

3535 },

3536 animals: {

3537 type: :array,

3538 items: { type: :string }

3539 }

3483 },3540 },

3484 required: %w[attributes colors animals],3541 required: %w[attributes colors animals],

3485 additionalProperties: false3542 additionalProperties: false


3488stream = client.responses.stream(3545stream = client.responses.stream(

3489 model: "gpt-6-astra",3546 model: "gpt-6-astra",

3490 input: [3547 input: [

3491 {role: :system, content: "Extract entities from the input text."},3548 {

3549 role: :system,

3550 content: "Extract entities from the input text."

3551 },

3492 {3552 {

3493 role: :user,3553 role: :user,

3494 content: "The quick brown fox jumps over the lazy dog with piercing blue eyes."3554 content: "The quick brown fox jumps over the lazy dog with piercing blue eyes."


4342response = client.responses.create(4402response = client.responses.create(

4343 model: "gpt-6-astra",4403 model: "gpt-6-astra",

4344 input: [4404 input: [

4345 {role: :system, content: "You are a helpful assistant designed to output JSON."},4405 {

4406 role: :system,

4407 content: "You are a helpful assistant designed to output JSON."

4408 },

4346 {4409 {

4347 role: :user,4410 role: :user,

4348 content: "Who won the World Series in 2020? Respond in the format {winner: ...}."4411 content: "Who won the World Series in 2020? Respond in the format {winner: ...}."

4349 }4412 }

4350 ],4413 ],

4351 text: {format: {type: :json_object}}4414 text: { format: { type: :json_object } }

4352)4415)

4353 4416 

4354if response.status == OpenAI::Responses::ResponseStatus::INCOMPLETE4417if response.status == OpenAI::Responses::ResponseStatus::INCOMPLETE

guides/text.md +10 −4

Details

300response = client.responses.create(300response = client.responses.create(

301 model: "gpt-6-astra",301 model: "gpt-6-astra",

302 instructions: "Talk like a pirate.",302 instructions: "Talk like a pirate.",

303 reasoning: {effort: :low},303 reasoning: { effort: :low },

304 input: "Are semicolons optional in JavaScript?"304 input: "Are semicolons optional in JavaScript?"

305)305)

306 306 


476client = OpenAI::Client.new476client = OpenAI::Client.new

477response = client.responses.create(477response = client.responses.create(

478 model: "gpt-6-astra",478 model: "gpt-6-astra",

479 reasoning: {effort: :low},479 reasoning: { effort: :low },

480 input: [480 input: [

481 {role: :developer, content: "Talk like a pirate."},481 {

482 {role: :user, content: "Are semicolons optional in JavaScript?"}482 role: :developer,

483 content: "Talk like a pirate."

484 },

485 {

486 role: :user,

487 content: "Are semicolons optional in JavaScript?"

488 }

483 ]489 ]

484)490)

485 491 

Details

233 233 

234client = OpenAI::Client.new234client = OpenAI::Client.new

235conversation = [235conversation = [

236 {role: :user, content: "What is 2 + 2?"},236 {

237 {role: :assistant, content: "2 + 2 equals 4."},237 role: :user,

238 {role: :user, content: "What about 3 + 3?"}238 content: "What is 2 + 2?"

239 },

240 {

241 role: :assistant,

242 content: "2 + 2 equals 4."

243 },

244 {

245 role: :user,

246 content: "What about 3 + 3?"

247 }

239]248]

240 249 

241count = client.responses.input_tokens.count(250count = client.responses.input_tokens.count(


524 image_url: "https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg",533 image_url: "https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg",

525 detail: :auto534 detail: :auto

526 },535 },

527 {type: :input_text, text: "Summarize this chart."}536 {

537 type: :input_text,

538 text: "Summarize this chart."

539 }

528 ]540 ]

529 }541 }

530 ]542 ]


715 strict: true,727 strict: true,

716 parameters: {728 parameters: {

717 type: "object",729 type: "object",

718 properties: {location: {type: "string"}},730 properties: { location: { type: "string" } },

719 required: ["location"],731 required: ["location"],

720 additionalProperties: false732 additionalProperties: false

721 }733 }

guides/tools.md +15 −4

Details

4 4 

5When generating model responses or building agents, you can extend capabilities using built‑in tools, function calling, Programmatic Tool Calling, tool search, and remote MCP servers. These enable the model to search the web, retrieve from your files, load deferred tool definitions at runtime, call your own functions, compose tool calls in JavaScript, or access third‑party services. Only `gpt-5.4` and later models support `tool_search`.5When generating model responses or building agents, you can extend capabilities using built‑in tools, function calling, Programmatic Tool Calling, tool search, and remote MCP servers. These enable the model to search the web, retrieve from your files, load deferred tool definitions at runtime, call your own functions, compose tool calls in JavaScript, or access third‑party services. Only `gpt-5.4` and later models support `tool_search`.

6 6 

7Choose the integration for your runtime: configure tools on [Responses API requests](#usage-in-the-api), on [Agents API agents](#agents-api), or in [Agents SDK definitions](#usage-in-the-agents-sdk). Tool availability, configuration, and call handling depend on the integration. The examples below use the Responses API.

8 

7 9 

8 10 

9Web search11Web search


109 111 

110response = openai.responses.create(112response = openai.responses.create(

111 model: "gpt-6-astra",113 model: "gpt-6-astra",

112 tools: [{type: "web_search"}],114 tools: [{ type: "web_search" }],

113 input: "What was a positive news story from today?"115 input: "What was a positive news story from today?"

114)116)

115 117 


496client = OpenAI::Client.new498client = OpenAI::Client.new

497parameters = {499parameters = {

498 type: :object,500 type: :object,

499 properties: {customer_id: {type: :string}},501 properties: { customer_id: { type: :string } },

500 required: ["customer_id"],502 required: ["customer_id"],

501 additionalProperties: false503 additionalProperties: false

502}504}


525 }527 }

526 ]528 ]

527 },529 },

528 {type: :tool_search}530 { type: :tool_search }

529 ]531 ]

530)532)

531 533 


781response = openai.responses.create(783response = openai.responses.create(

782 model: "gpt-6-astra",784 model: "gpt-6-astra",

783 input: [785 input: [

784 {role: "user", content: "What is the weather like in Paris today?"}786 {

787 role: "user",

788 content: "What is the weather like in Paris today?"

789 }

785 ],790 ],

786 tools: tools791 tools: tools

787)792)


1074 1079 

1075You can explicitly control or guide this behavior by setting the `tool_choice` parameter [in the API request](https://developers.openai.com/api/reference/resources/responses/methods/create).1080You can explicitly control or guide this behavior by setting the `tool_choice` parameter [in the API request](https://developers.openai.com/api/reference/resources/responses/methods/create).

1076 1081 

1082## Agents API

1083 

1084The [Agents API](https://developers.openai.com/api/docs/guides/agents-api/overview) runs the agent loop for you. Configure tools in `agent.tools`, handle function calls in your application, and connect a sandbox when the tools need an execution environment.

1085 

1086See [Functions](https://developers.openai.com/api/docs/guides/agents-api/tools/functions) to call application code, [MCP connections](https://developers.openai.com/api/docs/guides/agents-api/tools/mcp) to connect tool servers, and [sandbox configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration#environment-settings) for tools that need an execution environment. [Programmatic Tool Calling](https://developers.openai.com/api/docs/guides/tools-programmatic-tool-calling#agents-api) is enabled by default. [Skills](https://developers.openai.com/api/docs/guides/tools-skills#agents-api) are discovered through the sandbox's capability directories.

1087 

1077## Usage in the Agents SDK1088## Usage in the Agents SDK

1078 1089 

1079In the Agents SDK, the tool semantics stay the same, but the wiring moves into the agent definition and workflow design rather than a single Responses API request.1090In the Agents SDK, the tool semantics stay the same, but the wiring moves into the agent definition and workflow design rather than a single Responses API request.

Details

144response = client.responses.create(144response = client.responses.create(

145 model: "gpt-6-astra",145 model: "gpt-6-astra",

146 input: "Rename fib() to fibonacci() in lib/fib.py and update run.py to use the new name.",146 input: "Rename fib() to fibonacci() in lib/fib.py and update run.py to use the new name.",

147 tools: [{type: :apply_patch}]147 tools: [{ type: :apply_patch }]

148)148)

149 149 

150patch_calls = response.output.select { |item| item.type == :apply_patch_call }150patch_calls = response.output.select { |item| item.type == :apply_patch_call }


292response = client.responses.create(292response = client.responses.create(

293 model: "gpt-6-astra",293 model: "gpt-6-astra",

294 previous_response_id: response_id,294 previous_response_id: response_id,

295 input: [{295 input: [

296 {

296 type: :apply_patch_call_output,297 type: :apply_patch_call_output,

297 call_id: patch_call_id,298 call_id: patch_call_id,

298 status: :completed,299 status: :completed,

299 output: "Patch applied successfully."300 output: "Patch applied successfully."

300 }],301 }

301 tools: [{type: :apply_patch}]302 ],

303 tools: [{ type: :apply_patch }]

302)304)

303 305 

304puts(response.output_text)306puts(response.output_text)

Details

138 tools: [138 tools: [

139 {139 {

140 type: :code_interpreter,140 type: :code_interpreter,

141 container: {type: :auto, memory_limit: "4g"}141 container: {

142 type: :auto,

143 memory_limit: "4g"

144 }

142 }145 }

143 ]146 ]

144)147)


309container = client.containers.create(name: "analysis", memory_limit: "4g")312container = client.containers.create(name: "analysis", memory_limit: "4g")

310response = client.responses.create(313response = client.responses.create(

311 model: "gpt-6-astra",314 model: "gpt-6-astra",

312 tools: [{type: :code_interpreter, container: container.id}],315 tools: [

316 {

317 type: :code_interpreter,

318 container: container.id

319 }

320 ],

313 tool_choice: :required,321 tool_choice: :required,

314 input: "Calculate 4 * 3.82, then take the square root twice."322 input: "Calculate 4 * 3.82, then take the square root twice."

315)323)

Details

231def run_computer_use(endpoint, prompt)231def run_computer_use(endpoint, prompt)

232 client = OpenAI::Client.new232 client = OpenAI::Client.new

233 session_id = SecureRandom.uuid233 session_id = SecureRandom.uuid

234 tools = [{234 tools = [

235 type: :function, name: "exec_py",235 {

236 type: :function,

237 name: "exec_py",

236 description: "Run Python in a persistent desktop. Variables persist across calls. PyAutoGUI operations are synchronous. Available: pyautogui, time, log(value), and display(PIL_image). Inspect the screen with display(pyautogui.screenshot()) before acting. Use screenshot coordinates and check the screen after a short group of actions. Keep screenshots in memory and PyAutoGUI's fail-safe enabled.",238 description: "Run Python in a persistent desktop. Variables persist across calls. PyAutoGUI operations are synchronous. Available: pyautogui, time, log(value), and display(PIL_image). Inspect the screen with display(pyautogui.screenshot()) before acting. Use screenshot coordinates and check the screen after a short group of actions. Keep screenshots in memory and PyAutoGUI's fail-safe enabled.",

237 parameters: {type: :object, properties: {code: {type: :string}}, required: ["code"], additionalProperties: false},239 parameters: {

240 type: :object,

241 properties: { code: { type: :string } },

242 required: ["code"],

243 additionalProperties: false

244 },

238 strict: true245 strict: true

239 }]246 }

247 ]

240 next_input = []248 next_input = []

241 next_input << {role: :user, content: prompt}249 next_input << {

250 role: :user,

251 content: prompt

252 }

242 history = {}253 history = {}

243 20.times do |turn|254 20.times do |turn|

244 response = client.responses.create(255 response = client.responses.create(


254 next_input.clear267 next_input.clear

255 calls.each do |call|268 calls.each do |call|

256 raise "Unexpected tool: #{call.name}" unless call.name == "exec_py"269 raise "Unexpected tool: #{call.name}" unless call.name == "exec_py"

270 

257 code = JSON.parse(call.arguments).fetch("code")271 code = JSON.parse(call.arguments).fetch("code")

258 raise "Expected Python source text" unless code.is_a?(String)272 raise "Expected Python source text" unless code.is_a?(String)

273 

259 output = execute_in_sandbox(code, session_id, endpoint)274 output = execute_in_sandbox(code, session_id, endpoint)

260 next_input << {type: :function_call_output, call_id: call.call_id, output: output}275 next_input << {

276 type: :function_call_output,

277 call_id: call.call_id,

278 output: output

279 }

261 end280 end

262 history[:id] = response.id281 history[:id] = response.id

263 end282 end


385response = client.responses.create(404response = client.responses.create(

386 model: "gpt-5.6-sol",405 model: "gpt-5.6-sol",

387 input: "Open the Filters panel if needed, then search for penguin. Use the computer tool for UI interaction.",406 input: "Open the Filters panel if needed, then search for penguin. Use the computer tool for UI interaction.",

388 tools: [{type: :computer}]407 tools: [{ type: :computer }]

389)408)

390 409 

391puts(response.output)410puts(response.output)


573response = client.responses.create(592response = client.responses.create(

574 model: "gpt-5.6-sol",593 model: "gpt-5.6-sol",

575 previous_response_id: "resp_abc123",594 previous_response_id: "resp_abc123",

576 input: [{595 input: [

596 {

577 type: :computer_call_output,597 type: :computer_call_output,

578 call_id: "call_abc123",598 call_id: "call_abc123",

579 output: {599 output: {


581 image_url: "data:image/png;base64,<base64 bytes here>",601 image_url: "data:image/png;base64,<base64 bytes here>",

582 detail: :original602 detail: :original

583 }603 }

584 }],604 }

585 tools: [{type: :computer}]605 ],

606 tools: [{ type: :computer }]

586)607)

587 608 

588puts(response.output)609puts(response.output)

Details

2017 puts(code)2017 puts(code)

2018 print("Run this code in the isolated runtime? Type yes: ")2018 print("Run this code in the isolated runtime? Type yes: ")

2019 unless $stdin.gets&.strip == "yes"2019 unless $stdin.gets&.strip == "yes"

2020 return [{type: "input_text", text: "The user declined this execution."}]2020 return [

2021 {

2022 type: "input_text",

2023 text: "The user declined this execution."

2024 }

2025 ]

2021 end2026 end

2027 

2022 uri = URI(endpoint)2028 uri = URI(endpoint)

2023 request = Net::HTTP::Post.new(uri)2029 request = Net::HTTP::Post.new(uri)

2024 request["Content-Type"] = "application/json"2030 request["Content-Type"] = "application/json"


2032 payload = JSON.parse(response.body)2038 payload = JSON.parse(response.body)

2033 output = payload.is_a?(Hash) && payload["output"]2039 output = payload.is_a?(Hash) && payload["output"]

2034 raise "The execution service returned no observations" unless output.is_a?(Array) && !output.empty?2040 raise "The execution service returned no observations" unless output.is_a?(Array) && !output.empty?

2041 

2035 output.map do |item|2042 output.map do |item|

2036 raise "Invalid execution-service output item" unless item.is_a?(Hash)2043 raise "Invalid execution-service output item" unless item.is_a?(Hash)

2044 

2037 if item["type"] == "input_text" && item["text"].is_a?(String)2045 if item["type"] == "input_text" && item["text"].is_a?(String)

2038 {type: "input_text", text: item["text"]}2046 {

2047 type: "input_text",

2048 text: item["text"]

2049 }

2039 elsif item["type"] == "input_image" && item["image_url"].is_a?(String) && item["detail"] == "original"2050 elsif item["type"] == "input_image" && item["image_url"].is_a?(String) && item["detail"] == "original"

2040 {type: "input_image", image_url: item["image_url"], detail: "original"}2051 {

2052 type: "input_image",

2053 image_url: item["image_url"],

2054 detail: "original"

2055 }

2041 else2056 else

2042 raise "Expected input_text or input_image with original detail"2057 raise "Expected input_text or input_image with original detail"

2043 end2058 end


2307 model: "computer-use-preview",2322 model: "computer-use-preview",

2308 input: "Check whether the Filters panel is open.",2323 input: "Check whether the Filters panel is open.",

2309 truncation: :auto,2324 truncation: :auto,

2310 tools: [{2325 tools: [

2326 {

2311 type: :computer_use_preview,2327 type: :computer_use_preview,

2312 display_width: 1024,2328 display_width: 1024,

2313 display_height: 768,2329 display_height: 768,

2314 environment: :browser2330 environment: :browser

2315 }]2331 }

2332 ]

2316)2333)

2317 2334 

2318puts(response.output)2335puts(response.output)

Details

7- **Connectors** are OpenAI-maintained MCP wrappers for popular services like Google Workspace or Dropbox, like the connectors available in [ChatGPT](https://chatgpt.com).7- **Connectors** are OpenAI-maintained MCP wrappers for popular services like Google Workspace or Dropbox, like the connectors available in [ChatGPT](https://chatgpt.com).

8- **Remote MCP servers** can be any server on the public Internet that implements a remote [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) server.8- **Remote MCP servers** can be any server on the public Internet that implements a remote [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) server.

9 9 

10This guide will show how to use both remote MCP servers and connectors to give the model access to new capabilities.10This guide will show how to use both remote MCP servers and connectors with the Responses API. For Agents API sessions, see [MCP connections](https://developers.openai.com/api/docs/guides/agents-api/tools/mcp), which covers connections from the managed service or from your sandbox.

11 11 

12## Secure MCP Tunnel12## Secure MCP Tunnel

13 13 


374response = client.responses.create(374response = client.responses.create(

375 model: "gpt-6-astra",375 model: "gpt-6-astra",

376 input: "Summarize the Q2 earnings report.",376 input: "Summarize the Q2 earnings report.",

377 tools: [{377 tools: [

378 {

378 type: :mcp,379 type: :mcp,

379 server_label: "Dropbox",380 server_label: "Dropbox",

380 connector_id: "connector_dropbox",381 connector_id: "connector_dropbox",

381 authorization: "<oauth access token>",382 authorization: "<oauth access token>",

382 require_approval: :never383 require_approval: :never

383 }]384 }

385 ]

384)386)

385 387 

386puts(response.output_text)388puts(response.output_text)


388 390 

389 391 

390 392 

391The API will return new items in the `output` array of the model response. If the model decides to use a Connector or MCP server, it will first make a request to list available tools from the server, which will create a `mcp_list_tools` output item. From the simple remote MCP server example above, it contains only one tool definition:393The API will return new items in the `output` array of the model response. If the model decides to use a Connector or MCP server, it will first make a request to list available tools from the server, which will create a `mcp_list_tools` output item. From the remote MCP server example above, it contains only one tool definition:

392 394 

393```json395```json

394{396{


435 437 

436## How it works438## How it works

437 439 

438The MCP tool (for both remote MCP servers and connectors) is available in the [Responses API](https://developers.openai.com/api/reference/resources/responses/methods/create) in most recent models. Check MCP tool compatibility for your model [here](https://developers.openai.com/api/docs/models). When you're using the MCP tool, you only pay for [tokens](https://developers.openai.com/api/docs/pricing) used when importing tool definitions or making tool calls. There are no additional fees involved per tool call.440The MCP tool (for both remote MCP servers and connectors) is available in the [Responses API](https://developers.openai.com/api/reference/resources/responses/methods/create) in most recent models. Check MCP tool compatibility for your model [here](https://developers.openai.com/api/docs/models). When you're using the MCP tool, you only pay for [tokens](https://developers.openai.com/api/docs/pricing) used when importing tool definitions or making tool calls. No additional fees apply per tool call.

439 441 

440Below, we'll step through the process the API takes when calling an MCP tool.442Below, we'll step through the process the API takes when calling an MCP tool.

441 443 


892response = client.responses.create(894response = client.responses.create(

893 model: "gpt-6-astra",895 model: "gpt-6-astra",

894 previous_response_id: "resp_682d498bdefc81918b4a6aa477bfafd904ad1e533afccbfa",896 previous_response_id: "resp_682d498bdefc81918b4a6aa477bfafd904ad1e533afccbfa",

895 input: [{897 input: [

898 {

896 type: :mcp_approval_response,899 type: :mcp_approval_response,

897 approval_request_id: "mcpr_682d498e3bd4819196a0ce1664f8e77b04ad1e533afccbfa",900 approval_request_id: "mcpr_682d498e3bd4819196a0ce1664f8e77b04ad1e533afccbfa",

898 approve: true901 approve: true

899 }],902 }

900 tools: [{903 ],

904 tools: [

905 {

901 type: :mcp,906 type: :mcp,

902 server_label: "dmcp",907 server_label: "dmcp",

903 server_url: "https://dmcp-server.deno.dev/mcp",908 server_url: "https://dmcp-server.deno.dev/mcp",

904 server_description: "A Dungeons and Dragons MCP server.",909 server_description: "A Dungeons and Dragons MCP server.",

905 require_approval: :always910 require_approval: :always

906 }]911 }

912 ]

907)913)

908 914 

909puts(response.output_text)915puts(response.output_text)

910```916```

911 917 

912 918 

913Here we're using the `previous_response_id` parameter to chain this new Response, with the previous Response that generated the approval request. But you can also pass back the [outputs from one response, as inputs into another](https://developers.openai.com/api/docs/guides/conversation-state#manually-manage-conversation-state) for maximum control over what enter's the model's context.919Here we're using the `previous_response_id` parameter to chain this new Response, with the previous Response that generated the approval request. But you can also pass back the [outputs from one response, as inputs into another](https://developers.openai.com/api/docs/guides/conversation-state#manually-manage-conversation-state) for maximum control over what enters the model's context.

914 920 

915If and when you feel comfortable trusting a remote MCP server, you can choose to skip the approvals for reduced latency. To do this, you can set the `require_approval` parameter of the MCP tool to an object listing just the tools you'd like to skip approvals for like shown below, or set it to the value `'never'` to skip approvals for all tools in that remote MCP server.921If and when you feel comfortable trusting a remote MCP server, you can choose to skip the approvals for reduced latency. To do this, you can set the `require_approval` parameter of the MCP tool to an object listing just the tools you'd like to skip approvals for like shown below, or set it to the value `'never'` to skip approvals for all tools in that remote MCP server.

916 922 


1099 server_label: "deepwiki",1105 server_label: "deepwiki",

1100 server_url: "https://mcp.deepwiki.com/mcp",1106 server_url: "https://mcp.deepwiki.com/mcp",

1101 require_approval: {1107 require_approval: {

1102 never: {tool_names: ["ask_question", "read_wiki_structure"]}1108 never: { tool_names: ["ask_question", "read_wiki_structure"] }

1103 }1109 }

1104 }1110 }

1105 ]1111 ]


1270response = client.responses.create(1276response = client.responses.create(

1271 model: "gpt-6-astra",1277 model: "gpt-6-astra",

1272 input: "Create a payment link for $20.",1278 input: "Create a payment link for $20.",

1273 tools: [{1279 tools: [

1280 {

1274 type: :mcp,1281 type: :mcp,

1275 server_label: "stripe",1282 server_label: "stripe",

1276 server_url: "https://mcp.stripe.com",1283 server_url: "https://mcp.stripe.com",

1277 authorization: ENV.fetch("STRIPE_OAUTH_ACCESS_TOKEN")1284 authorization: ENV.fetch("STRIPE_OAUTH_ACCESS_TOKEN")

1278 }]1285 }

1286 ]

1279)1287)

1280 1288 

1281puts(response.output_text)1289puts(response.output_text)


1317 1325 

1318This authorization scope will enable the API to read Google Calendar events. In the UI under "Step 1: Select and authorize APIs".1326This authorization scope will enable the API to read Google Calendar events. In the UI under "Step 1: Select and authorize APIs".

1319 1327 

1320After authorizing the application with your Google account, you will come to "Step 2: Exchange authorization code for tokens". This will generate an access token you can use in an API request using the Google Calendar connector:1328After authorizing the application with your Google account, you will come to **Step 2: Exchange authorization code for tokens**. This will generate an access token you can use in an API request using the Google Calendar connector:

1321 1329 

1322Use the Google Calendar connector1330Use the Google Calendar connector

1323 1331 


1477response = client.responses.create(1485response = client.responses.create(

1478 model: "gpt-6-astra",1486 model: "gpt-6-astra",

1479 input: "What's on my Google Calendar for today?",1487 input: "What's on my Google Calendar for today?",

1480 tools: [{1488 tools: [

1489 {

1481 type: :mcp,1490 type: :mcp,

1482 server_label: "google_calendar",1491 server_label: "google_calendar",

1483 connector_id: "connector_googlecalendar",1492 connector_id: "connector_googlecalendar",

1484 authorization: "<oauth access token>",1493 authorization: "<oauth access token>",

1485 require_approval: :never1494 require_approval: :never

1486 }]1495 }

1496 ]

1487)1497)

1488 1498 

1489puts(response.output_text)1499puts(response.output_text)


1881 1891 

1882#### Prompt injection1892#### Prompt injection

1883 1893 

1884[Prompt injection](https://chatgpt.com/?prompt=what%20is%20prompt%20injection?) is an important security consideration in any LLM application, and is especially true when you give the model access to MCP servers and connectors which can access sensitive data or take action. Use these tools with appropriate caution and mitigations if the prompt for the model contains user-provided content.1894[Prompt injection](https://chatgpt.com/?prompt=what%20is%20prompt%20injection?) is an important security consideration in any LLM application, and is especially true when you give the model access to MCP servers and connectors which can access sensitive data or take action. Use these tools with appropriate caution and protective measures if the prompt for the model contains user-provided content.

1885 1895 

1886#### Always require approval for sensitive actions1896#### Always require approval for sensitive actions

1887 1897 


1893 1903 

1894#### Connecting to trusted servers1904#### Connecting to trusted servers

1895 1905 

1896Pick official servers hosted by the service providers themselves (e.g. we recommend connecting to the Stripe server hosted by Stripe themselves on mcp.stripe.com, instead of a Stripe MCP server hosted by a third party). Because there aren't too many official remote MCP servers today, you may be tempted to use a MCP server hosted by an organization that doesn't operate that server and simply proxies request to that service via your API. If you must do this, be extra careful in doing your due diligence on these "aggregators", and carefully review how they use your data.1906Pick official servers hosted by the service providers themselves (for example, we recommend connecting to the Stripe server hosted by Stripe at `mcp.stripe.com`, instead of a Stripe MCP server hosted by a third party). Because there aren't too many official remote MCP servers today, you may be tempted to use an MCP server hosted by an organization that doesn't operate that server and proxies requests to that service via your API. If you must do this, be extra careful in doing your due diligence on these "aggregators," and carefully review how they use your data.

1897 1907 

1898#### Log and review data being shared with third party MCP servers.1908#### Log and review data being shared with third party MCP servers.

1899 1909 

1900Because MCP servers define their own tool definitions, they may request for data that you may not always be comfortable sharing with the host of that MCP server. Because of this, the MCP tool in the Responses API defaults to requiring approvals of each MCP tool call being made. When developing your application, review the type of data being shared with these MCP servers carefully and robustly. Once you gain confidence in your trust of this MCP server, you can skip these approvals for more performant execution.1910Because MCP servers define their own tool definitions, they may request for data that you may not always be comfortable sharing with the host of that MCP server. Because of this, the MCP tool in the Responses API defaults to requiring approvals of each MCP tool call being made. When developing your application, review the type of data being shared with these MCP servers carefully and robustly. Once you gain confidence in your trust of this MCP server, you can skip these approvals to reduce execution latency.

1901 1911 

1902We also recommend logging any data sent to MCP servers. If you're using the Responses API with `store=true`, these data are already logged via the API for 30 days unless Zero Data Retention is enabled for your organization. You may also want to log these data in your own systems and perform periodic reviews on this to ensure data is being shared per your expectations.1912We also recommend logging any data sent to MCP servers. If you're using the Responses API with `store=true`, these data are already logged via the API for 30 days unless Zero Data Retention is enabled for your organization. You may also want to log these data in your own systems and perform periodic reviews on this to ensure data is being shared per your expectations.

1903 1913 

Details

167response = client.responses.create(167response = client.responses.create(

168 model: "gpt-6-astra",168 model: "gpt-6-astra",

169 input: "Generate an image of a gray tabby cat hugging an otter with an orange scarf.",169 input: "Generate an image of a gray tabby cat hugging an otter with an orange scarf.",

170 tools: [{type: :image_generation, model: "gpt-image-2.5-sunburst"}]170 tools: [

171 {

172 type: :image_generation,

173 model: "gpt-image-2.5-sunburst"

174 }

175 ]

171)176)

172 177 

173image_call = response.output.find do |item|178image_call = response.output.find do |item|


491first = client.responses.create(496first = client.responses.create(

492 model: "gpt-6-astra",497 model: "gpt-6-astra",

493 input: "Generate an image of a gray tabby cat hugging an otter with an orange scarf.",498 input: "Generate an image of a gray tabby cat hugging an otter with an orange scarf.",

494 tools: [{type: :image_generation, model: "gpt-image-2.5-sunburst"}]499 tools: [

500 {

501 type: :image_generation,

502 model: "gpt-image-2.5-sunburst"

503 }

504 ]

495)505)

496 506 

497first_image = first.output.find do |item|507first_image = first.output.find do |item|


508 model: "gpt-6-astra",518 model: "gpt-6-astra",

509 input: "Now make it look realistic.",519 input: "Now make it look realistic.",

510 previous_response_id: first.id,520 previous_response_id: first.id,

511 tools: [{type: :image_generation, model: "gpt-image-2.5-sunburst"}]521 tools: [

522 {

523 type: :image_generation,

524 model: "gpt-image-2.5-sunburst"

525 }

526 ]

512)527)

513 528 

514follow_up_image = follow_up.output.find do |item|529follow_up_image = follow_up.output.find do |item|


826first = client.responses.create(841first = client.responses.create(

827 model: "gpt-6-astra",842 model: "gpt-6-astra",

828 input: "Generate an image of a gray tabby cat hugging an otter with an orange scarf.",843 input: "Generate an image of a gray tabby cat hugging an otter with an orange scarf.",

829 tools: [{type: :image_generation, model: "gpt-image-2.5-sunburst"}]844 tools: [

845 {

846 type: :image_generation,

847 model: "gpt-image-2.5-sunburst"

848 }

849 ]

830)850)

831 851 

832first_image = first.output.find do |item|852first_image = first.output.find do |item|


844 input: [864 input: [

845 {865 {

846 role: :user,866 role: :user,

847 content: [{type: :input_text, text: "Now make it look realistic."}]867 content: [

868 {

869 type: :input_text,

870 text: "Now make it look realistic."

871 }

872 ]

848 },873 },

849 {type: :image_generation_call, id: first_image.id}874 {

875 type: :image_generation_call,

876 id: first_image.id

877 }

850 ],878 ],

851 tools: [{type: :image_generation, model: "gpt-image-2.5-sunburst"}]879 tools: [

880 {

881 type: :image_generation,

882 model: "gpt-image-2.5-sunburst"

883 }

884 ]

852)885)

853 886 

854follow_up_image = follow_up.output.find do |item|887follow_up_image = follow_up.output.find do |item|


1053stream = client.responses.stream(1086stream = client.responses.stream(

1054 model: "gpt-6-astra",1087 model: "gpt-6-astra",

1055 input: "Generate an image of a river made of white owl feathers.",1088 input: "Generate an image of a river made of white owl feathers.",

1056 tools: [{type: :image_generation, model: "gpt-image-2.5-sunburst", partial_images: 2}]1089 tools: [

1090 {

1091 type: :image_generation,

1092 model: "gpt-image-2.5-sunburst",

1093 partial_images: 2

1094 }

1095 ]

1057)1096)

1058 1097 

1059stream.each do |event|1098stream.each do |event|

Details

728MAX_TIMEOUT_MS = 10_000728MAX_TIMEOUT_MS = 10_000

729response = client.responses.create(729response = client.responses.create(

730 model: "codex-mini-latest",730 model: "codex-mini-latest",

731 tools: [{type: :local_shell}],731 tools: [{ type: :local_shell }],

732 parallel_tool_calls: false,732 parallel_tool_calls: false,

733 input: "List files in the current directory."733 input: "List files in the current directory."

734)734)


751 else751 else

752 begin752 begin

753 executable = action.command.fetch(0)753 executable = action.command.fetch(0)

754 environment = {"PATH" => ENV.fetch("PATH", "")}.merge(action.env.transform_keys(&:to_s))754 environment = { "PATH" => ENV.fetch("PATH", "") }.merge(action.env.transform_keys(&:to_s))

755 status, timed_out = Open3.popen3(755 status, timed_out = Open3.popen3(

756 environment,756 environment,

757 [executable, executable],757 [executable, executable],


839 839 

840 response = client.responses.create(840 response = client.responses.create(

841 model: "codex-mini-latest",841 model: "codex-mini-latest",

842 tools: [{type: :local_shell}],842 tools: [{ type: :local_shell }],

843 parallel_tool_calls: false,843 parallel_tool_calls: false,

844 previous_response_id: response.id,844 previous_response_id: response.id,

845 input: [{845 input: [

846 {

846 type: :local_shell_call_output,847 type: :local_shell_call_output,

847 id: shell_call.call_id,848 id: shell_call.call_id,

848 output: (stdout + stderr).encode("UTF-8", invalid: :replace, undef: :replace)849 output: (stdout + stderr).encode("UTF-8", invalid: :replace, undef: :replace)

849 }]850 }

851 ]

850 )852 )

851end853end

852 854 

Details

2 2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 4 

5Programmatic Tool Calling lets a model write and run JavaScript that coordinates the tools in a Responses API request. A program can call tools in parallel, use loops and conditions, and keep intermediate results in the hosted runtime. This is useful when a task needs a sequence of related tool calls or needs to process large tool outputs before returning a result.5Programmatic Tool Calling lets a model write and run JavaScript that coordinates its tools. A program can call tools in parallel, use loops and conditions, and keep intermediate results in the hosted runtime. This is useful when a task needs a sequence of related tool calls or needs to process large tool outputs before returning a result.

6 6 

7Your application decides whether Programmatic Tool Calling is available and which eligible tools the model can call directly, from a program, or either way. It continues to run any client-owned tool calls.7In the Responses API, your application decides whether Programmatic Tool Calling is available and which eligible tools the model can call directly, from a program, or either way. It continues to run any client-owned tool calls. The [Agents API](#agents-api) enables Programmatic Tool Calling by default and manages the agent loop for you.

8 8 

9Check the [model page](https://developers.openai.com/api/docs/models) before enabling Programmatic Tool Calling.9Check the [model page](https://developers.openai.com/api/docs/models) before enabling Programmatic Tool Calling.

10 10 


12 12 

13OpenAI runs each generated program in a fresh, isolated V8 runtime. The runtime supports JavaScript with top-level `await`, but it does not provide Node.js, package installation, direct network access, a general-purpose filesystem, subprocess execution, a console, or persistent JavaScript state between program executions. Programs can interact with external systems only through tools enabled in the request and can emit output with `text(...)` or `image(...)`.13OpenAI runs each generated program in a fresh, isolated V8 runtime. The runtime supports JavaScript with top-level `await`, but it does not provide Node.js, package installation, direct network access, a general-purpose filesystem, subprocess execution, a console, or persistent JavaScript state between program executions. Programs can interact with external systems only through tools enabled in the request and can emit output with `text(...)` or `image(...)`.

14 14 

15Programmatic Tool Calling supports Zero Data Retention (ZDR) workflows without requiring a persistent code-execution container. ZDR must be enabled for the organization or project; setting `store: false` enables stateless continuation but does not enable ZDR by itself. Eligibility and retention depend on the complete request, including its model, tools, and third-party services; see [data controls](https://developers.openai.com/api/docs/guides/your-data).15For Responses API requests, Programmatic Tool Calling supports Zero Data Retention (ZDR) workflows without requiring a persistent code-execution container. ZDR must be enabled for the organization or project; setting `store: false` enables stateless continuation but does not enable ZDR by itself. Eligibility and retention depend on the complete request, including its model, tools, and third-party services; see [data controls](https://developers.openai.com/api/docs/guides/your-data).

16 16 

17## Choose when to use Programmatic Tool Calling17## Choose when to use Programmatic Tool Calling

18 18 


29 29 

30## Configure Programmatic Tool Calling30## Configure Programmatic Tool Calling

31 31 

32Add the `programmatic_tool_calling` hosted tool to the request. Then set `allowed_callers` on each eligible tool that the program can invoke.32For the Responses API, add the `programmatic_tool_calling` hosted tool to the request. Then set `allowed_callers` on each eligible tool that the program can invoke.

33 33 

34Enable Programmatic Tool Calling34Enable Programmatic Tool Calling

35 35 


225 225 

226```javascript226```javascript

227import OpenAI from "openai";227import OpenAI from "openai";

228import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";

228 229 

229const client = new OpenAI();230const client = new OpenAI();

230 231 


304 throw new Error(`Response ended with status ${response.status}`);305 throw new Error(`Response ended with status ${response.status}`);

305 }306 }

306 307 

307 // Preserve every output item, including program and reasoning items.308 // Preserve replayable output, including program and reasoning items.

308 input.push(...response.output);309 input.push(...toResponseInputItems(response.output));

309 310 

310 const calls = response.output.filter((item) => item.type === "function_call");311 const calls = response.output.filter((item) => item.type === "function_call");

311 312 


648client = OpenAI::Client.new649client = OpenAI::Client.new

649 650 

650def get_inventory(sku:)651def get_inventory(sku:)

651 {sku: sku, available_units: 42}652 {

653 sku: sku,

654 available_units: 42

655 }

652end656end

653 657 

654def get_demand(sku:)658def get_demand(sku:)

655 {sku: sku, requested_units: 31}659 {

660 sku: sku,

661 requested_units: 31

662 }

656end663end

657 664 

658implementations = {665implementations = {


666 description: "Return an object with sku (string) and available_units (number).",673 description: "Return an object with sku (string) and available_units (number).",

667 parameters: {674 parameters: {

668 type: :object,675 type: :object,

669 properties: {sku: {type: :string}},676 properties: { sku: { type: :string } },

670 required: ["sku"],677 required: ["sku"],

671 additionalProperties: false678 additionalProperties: false

672 },679 },

673 output_schema: {680 output_schema: {

674 type: :object,681 type: :object,

675 properties: {682 properties: {

676 sku: {type: :string},683 sku: { type: :string },

677 available_units: {type: :number}684 available_units: { type: :number }

678 },685 },

679 required: %w[sku available_units],686 required: %w[sku available_units],

680 additionalProperties: false687 additionalProperties: false


688 description: "Return an object with sku (string) and requested_units (number).",695 description: "Return an object with sku (string) and requested_units (number).",

689 parameters: {696 parameters: {

690 type: :object,697 type: :object,

691 properties: {sku: {type: :string}},698 properties: { sku: { type: :string } },

692 required: ["sku"],699 required: ["sku"],

693 additionalProperties: false700 additionalProperties: false

694 },701 },

695 output_schema: {702 output_schema: {

696 type: :object,703 type: :object,

697 properties: {704 properties: {

698 sku: {type: :string},705 sku: { type: :string },

699 requested_units: {type: :number}706 requested_units: { type: :number }

700 },707 },

701 required: %w[sku requested_units],708 required: %w[sku requested_units],

702 additionalProperties: false709 additionalProperties: false


704 allowed_callers: [:programmatic],711 allowed_callers: [:programmatic],

705 strict: true712 strict: true

706 },713 },

707 {type: :programmatic_tool_calling}714 { type: :programmatic_tool_calling }

715]

716input = [

717 {

718 role: :user,

719 content: "Compare inventory with demand for sku_123."

720 }

708]721]

709input = [{role: :user, content: "Compare inventory with demand for sku_123."}]

710 722 

711loop do723loop do

712 response = client.responses.create(724 response = client.responses.create(


791- Safety outcomes, especially for side effects and approval requirements.803- Safety outcomes, especially for side effects and approval requirements.

792- Whether the route that ran matched the intended workflow stage.804- Whether the route that ran matched the intended workflow stage.

793 805 

806## Agents API

807 

808In the [Agents API](https://developers.openai.com/api/docs/guides/agents-api/overview), Programmatic Tool Calling runs in the OpenAI-managed agent harness and is enabled by default. The harness gives the agent an `exec` tool and makes its existing tools available inside generated JavaScript. You don't need to wrap those tools as command-line programs or install them in the sandbox.

809 

810To disable Programmatic Tool Calling, include this entry in `agent.tools`:

811 

812```json

813{

814 "type": "programmatic_tool_calling",

815 "enabled": false

816}

817```

818 

819Omitting the entry or its `enabled` field leaves Programmatic Tool Calling enabled. A type-only entry, `{ "type": "programmatic_tool_calling" }`, also keeps it enabled. The `allowed_callers` configuration and Responses continuation loop above describe the Responses API integration.

820 

821Programmatic Tool Calling also works in conversation-only sessions with `environment.type` set to `none`. Bash, executor MCPs, and other tools that run in a sandbox still require an [execution environment](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted).

822 

823Orchestrating a tool in JavaScript doesn't change where the tool runs. A shell call runs commands in the sandbox; the JavaScript runtime doesn't start system processes itself. Executor MCPs still use the sandbox, and function tools still call your application server. The agent processes their results before deciding what enters model context.

824 

825Use the routing guidance above to define which workflow stages should use code. Follow [Functions](https://developers.openai.com/api/docs/guides/agents-api/tools/functions) and [MCP connections](https://developers.openai.com/api/docs/guides/agents-api/tools/mcp) for Agents API configuration and call handling.

826 

794## Related guides827## Related guides

795 828 

796- Use [function calling](https://developers.openai.com/api/docs/guides/function-calling) to define client-owned functions.829- Use [function calling](https://developers.openai.com/api/docs/guides/function-calling) to define client-owned functions.

Details

154response = client.responses.create(154response = client.responses.create(

155 model: "gpt-6-astra",155 model: "gpt-6-astra",

156 input: "Run ls -lah /mnt/data, then show the Python and Node.js versions.",156 input: "Run ls -lah /mnt/data, then show the Python and Node.js versions.",

157 tools: [{type: :shell, environment: {type: :container_auto}}]157 tools: [

158 {

159 type: :shell,

160 environment: { type: :container_auto }

161 }

162 ]

158)163)

159 164 

160puts(response.output_text)165puts(response.output_text)


278require "openai"283require "openai"

279 284 

280client = OpenAI::Client.new285client = OpenAI::Client.new

281container = client.containers.create(name: "analysis", expires_after: {anchor: :last_active_at, minutes: 20})286container = client.containers.create(

287 name: "analysis", expires_after: {

288 anchor: :last_active_at,

289 minutes: 20

290 }

291)

282puts(container.id)292puts(container.id)

283```293```

284 294 


403response = client.responses.create(413response = client.responses.create(

404 model: "gpt-6-astra",414 model: "gpt-6-astra",

405 input: "List files in the container and show disk usage.",415 input: "List files in the container and show disk usage.",

406 tools: [{416 tools: [

417 {

407 type: :shell,418 type: :shell,

408 environment: {type: :container_reference, container_id: "cntr_08f3d96c87a585390069118b594f7481a088b16cda7d9415fe"}419 environment: {

409 }]420 type: :container_reference,

421 container_id: "cntr_08f3d96c87a585390069118b594f7481a088b16cda7d9415fe"

422 }

423 }

424 ]

410)425)

411 426 

412puts(response.output_text)427puts(response.output_text)


458```473```

459 474 

460```python475```python

461import os476# Replace the illustrative IDs and URLs below with your own resource values.

462from openai import OpenAI477from openai import OpenAI

463 478 

464client = OpenAI()479client = OpenAI()

465skill_id = os.environ["OPENAI_SKILL_ID"]480skill_id = "skill_123"

466 481 

467container = client.containers.create(482container = client.containers.create(

468 name="skill-container",483 name="skill-container",


541container = client.containers.create(556container = client.containers.create(

542 name: "skill-container",557 name: "skill-container",

543 skills: [558 skills: [

544 {type: :skill_reference, skill_id: "skill_4db6f1a2c9e73508b41f9da06e2c7b5f"},559 {

560 type: :skill_reference,

561 skill_id: "skill_4db6f1a2c9e73508b41f9da06e2c7b5f"

562 },

545 {563 {

546 type: :skill_reference,564 type: :skill_reference,

547 skill_id: "openai-spreadsheets",565 skill_id: "openai-spreadsheets",


736 model: "gpt-6-astra",754 model: "gpt-6-astra",

737 input: "Fetch release pages and write /mnt/data/release_digest.md.",755 input: "Fetch release pages and write /mnt/data/release_digest.md.",

738 tool_choice: :required,756 tool_choice: :required,

739 tools: [{757 tools: [

758 {

740 type: :shell,759 type: :shell,

741 environment: {760 environment: {

742 type: :container_auto,761 type: :container_auto,


745 allowed_domains: ["pypi.org", "files.pythonhosted.org", "github.com"]764 allowed_domains: ["pypi.org", "files.pythonhosted.org", "github.com"]

746 }765 }

747 }766 }

748 }]767 }

768 ]

749)769)

750 770 

751puts(response.output_text)771puts(response.output_text)


967base64_string = Base64.strict_encode64(File.binread("report.csv"))987base64_string = Base64.strict_encode64(File.binread("report.csv"))

968container = client.containers.create(988container = client.containers.create(

969 name: "inline-skill-container",989 name: "inline-skill-container",

970 skills: [{990 skills: [

991 {

971 type: :inline,992 type: :inline,

972 name: "csv-insights",993 name: "csv-insights",

973 description: "Summarize CSV files and produce a markdown report.",994 description: "Summarize CSV files and produce a markdown report.",

974 source: {type: :base64, media_type: "application/zip", data: inline_zip}995 source: {

975 }]996 type: :base64,

997 media_type: "application/zip",

998 data: inline_zip

999 }

1000 }

1001 ]

976)1002)

977response = client.responses.create(1003response = client.responses.create(

978 model: "gpt-6-astra",1004 model: "gpt-6-astra",

979 tools: [{type: :shell, environment: {type: :container_reference, container_id: container.id}}],1005 tools: [

980 input: [{role: :user, content: [1006 {

981 {type: :input_file, filename: "report.csv", file_data: "data:text/csv;base64,#{base64_string}"},1007 type: :shell,

982 {type: :input_text, text: "Use the csv-insights skill to summarize report.csv."}1008 environment: {

983 ]}]1009 type: :container_reference,

1010 container_id: container.id

1011 }

1012 }

1013 ],

1014 input: [

1015 {

1016 role: :user,

1017 content: [

1018 {

1019 type: :input_file,

1020 filename: "report.csv",

1021 file_data: "data:text/csv;base64,#{base64_string}"

1022 },

1023 {

1024 type: :input_text,

1025 text: "Use the csv-insights skill to summarize report.csv."

1026 }

1027 ]

1028 }

1029 ]

984)1030)

985puts(response.output_text)1031puts(response.output_text)

986```1032```


1010```1056```

1011 1057 

1012```python1058```python

1013import os1059# Replace the illustrative IDs and URLs below with your own resource values.

1014from openai import OpenAI1060from openai import OpenAI

1015 1061 

1016client = OpenAI()1062client = OpenAI()

1017container_id = os.environ["OPENAI_CONTAINER_ID"]1063container_id = "cntr_123"

1018 1064 

1019deleted = client.containers.delete(container_id)1065deleted = client.containers.delete(container_id)

1020 1066 


1282 input: "Use curl to call https://httpbin.org/headers with an " \1328 input: "Use curl to call https://httpbin.org/headers with an " \

1283 '"Authorization: Bearer $API_KEY" header.',1329 '"Authorization: Bearer $API_KEY" header.',

1284 tool_choice: :required,1330 tool_choice: :required,

1285 tools: [{1331 tools: [

1332 {

1286 type: :shell,1333 type: :shell,

1287 environment: {1334 environment: {

1288 type: :container_auto,1335 type: :container_auto,

1289 network_policy: {1336 network_policy: {

1290 type: :allowlist,1337 type: :allowlist,

1291 allowed_domains: ["httpbin.org"],1338 allowed_domains: ["httpbin.org"],

1292 domain_secrets: [{1339 domain_secrets: [

1340 {

1293 domain: "httpbin.org",1341 domain: "httpbin.org",

1294 name: "API_KEY",1342 name: "API_KEY",

1295 value: "debug-secret-123"1343 value: "debug-secret-123"

1296 }]1344 }

1345 ]

1346 }

1297 }1347 }

1298 }1348 }

1299 }]1349 ]

1300)1350)

1301 1351 

1302puts(response.output_text)1352puts(response.output_text)


1438 model: "gpt-6-astra",1488 model: "gpt-6-astra",

1439 input: "Read /mnt/data/top5.csv and report the top candidate.",1489 input: "Read /mnt/data/top5.csv and report the top candidate.",

1440 previous_response_id: "resp_2a8e5c9174d63b0f18a4c572de9f64a1b3c76d508e12f9ab47",1490 previous_response_id: "resp_2a8e5c9174d63b0f18a4c572de9f64a1b3c76d508e12f9ab47",

1441 tools: [{1491 tools: [

1492 {

1442 type: :shell,1493 type: :shell,

1443 environment: {type: :container_reference, container_id: "cntr_f19c2b51e4a06793d82d54a7be0fc9154d3361ab28ce7f6041"}1494 environment: {

1444 }]1495 type: :container_reference,

1496 container_id: "cntr_f19c2b51e4a06793d82d54a7be0fc9154d3361ab28ce7f6041"

1497 }

1498 }

1499 ]

1445)1500)

1446 1501 

1447puts(response.output_text)1502puts(response.output_text)


1583 model: "gpt-6-astra",1638 model: "gpt-6-astra",

1584 instructions: "The local shell environment is macOS.",1639 instructions: "The local shell environment is macOS.",

1585 input: "Find the largest PDF in ~/Documents.",1640 input: "Find the largest PDF in ~/Documents.",

1586 tools: [{type: :shell, environment: {type: :local}}]1641 tools: [

1642 {

1643 type: :shell,

1644 environment: { type: :local }

1645 }

1646 ]

1587)1647)

1588 1648 

1589puts(response.output)1649puts(response.output)

Details

2 2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 4 

5Agent Skills let you upload and reuse versioned bundles of files in hosted and local shell environments.5Agent Skills give an agent reusable instructions and supporting files for a task. Use them with Responses API shell tools or make them available in an [Agents API sandbox](#agents-api).

6 6 

7We support Skills in two form factors: local execution and hosted,7The upload, attachment, and versioning instructions below describe Responses API shell tools. Agents API sessions discover skills from directories in their sandbox.

8 container-based execution. To run code on your own machine, use the local8 

9 execution mode of the [shell tool](https://developers.openai.com/api/docs/guides/tools-shell).9The Responses API supports Skills in two form factors: local execution and

10 hosted, container-based execution. To run code on your own machine, use the

11 local execution mode of the [shell tool](https://developers.openai.com/api/docs/guides/tools-shell).

10 12 

11## What's a skill13## What's a skill

12 14 

13A skill is a versioned bundle of files plus a `SKILL.md` manifest (front matter + instructions). Skills are modular instructions you can use to codify processes and conventions, from company style guides to multi-step workflows.15A skill is a directory of files with a `SKILL.md` manifest (front matter + instructions). Skills are modular instructions you can use to codify processes and conventions, from company style guides to multi-step workflows. Uploaded skills use versioned bundles.

14 16 

15Skills are compatible with the open [Agent Skills standard](https://agentskills.io/home).17Skills are compatible with the open [Agent Skills standard](https://agentskills.io/home).

16 18 


26```28```

27 29 

28 30 

31During skill discovery, the model sees the skill's name and description. Write a description that explains both what the skill does and when to use it. For example, "Review and redline vendor agreements using the fallback clauses" gives the model more useful context than "Helps with legal work."

32 

33Keep the main instructions in `SKILL.md` and link to supporting files as needed:

34 

35```text

36review-pr/

37├── SKILL.md

38├── references/

39│ └── review-guidelines.md

40├── scripts/

41│ └── check-changes.sh

42└── assets/

43 └── review-template.md

44```

45 

46Use `references/` for background material, `scripts/` for repeatable actions, and `assets/` for reusable templates.

47 

29## Create a skill48## Create a skill

30 49 

31You can upload a directory as multipart form data or upload a `.zip` that contains a single top-level folder.50You can upload a directory as multipart form data or upload a `.zip` that contains a single top-level folder.


218response = client.responses.create(237response = client.responses.create(

219 model: "gpt-6-astra",238 model: "gpt-6-astra",

220 input: "Use the skills to add 144 and 377, then compute a triangle area with base 9 and height 13.",239 input: "Use the skills to add 144 and 377, then compute a triangle area with base 9 and height 13.",

221 tools: [{240 tools: [

241 {

222 type: :shell,242 type: :shell,

223 environment: {243 environment: {

224 type: :container_auto,244 type: :container_auto,

225 skills: [245 skills: [

226 {type: :skill_reference, skill_id: "<skill_id>"},246 {

227 {type: :skill_reference, skill_id: "<skill_id>", version: "2"}247 type: :skill_reference,

248 skill_id: "<skill_id>"

249 },

250 {

251 type: :skill_reference,

252 skill_id: "<skill_id>",

253 version: "2"

254 }

228 ]255 ]

229 }256 }

230 }]257 }

258 ]

231)259)

232 260 

233puts(response.output_text)261puts(response.output_text)


409response = client.responses.create(437response = client.responses.create(

410 model: "gpt-6-astra",438 model: "gpt-6-astra",

411 input: "Use the csv-insights skill to summarize today's CSV reports.",439 input: "Use the csv-insights skill to summarize today's CSV reports.",

412 tools: [{440 tools: [

441 {

413 type: :shell,442 type: :shell,

414 environment: {443 environment: {

415 type: :local,444 type: :local,

416 skills: [{445 skills: [

446 {

417 name: "csv-insights",447 name: "csv-insights",

418 description: "Summarize CSV files and produce a Markdown report.",448 description: "Summarize CSV files and produce a Markdown report.",

419 path: "<path-to-skill-folder>"449 path: "<path-to-skill-folder>"

420 }]

421 }450 }

422 }]451 ]

452 }

453 }

454 ]

423)455)

424 456 

425puts(response.output_text)457puts(response.output_text)

426```458```

427 459 

428 460 

461## Agents API

462 

463To use skills in the [Agents API](https://developers.openai.com/api/docs/guides/agents-api/overview), put the skill directories in the sandbox and register their parent directories in `environment.capability_directories` when creating the session. These are called **capability directories**. The harness uses them to discover skills; this setup doesn't use the hosted shell's `skill_reference` attachment format.

464 

465For example, place a contract-review skill and a pull-request-review skill in the sandbox:

466 

467```text

468/workspace/capabilities/

469├── legal/

470│ └── contract-redline/

471│ ├── SKILL.md

472│ └── references/

473│ └── fallback-clauses.md

474└── engineering/

475 └── review-pr/

476 ├── SKILL.md

477 └── references/

478 └── review-guidelines.md

479```

480 

481Use this environment configuration in the session-creation request:

482 

483```json

484{

485 "environment": {

486 "type": "self_hosted",

487 "workspace_directory": "/workspace",

488 "capability_directories": [

489 "/workspace/capabilities/legal",

490 "/workspace/capabilities/engineering"

491 ]

492 }

493}

494```

495 

496Capability directories have these requirements:

497 

498- Paths must point to directories inside the sandbox.

499- Paths must be absolute and unique, and cannot contain `.` or `..` path segments.

500- A session can register up to 32 capability directories.

501- Directories must already exist in the environment.

502 

503Once the sandbox becomes available, the harness searches these directories for `SKILL.md` files and adds each discovered skill's name and description to context. The model can select relevant skills and read their full instructions and supporting files.

504 

505See [Agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration) for session setup and [Connect a sandbox](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted) for the execution environment. Review the skills and their supporting files before making them available to the agent, and follow the [sandbox security guidance](https://developers.openai.com/api/docs/guides/agents-api/environments/security).

506 

429## Skills in the user prompt507## Skills in the user prompt

430 508 

431When skills are available to the tool, the platform adds each skill's `name`, `description`, and `path` to user prompt context so the model knows the skill exists.509For Responses API shell tools, the platform adds each available skill's `name`, `description`, and `path` to user prompt context so the model knows the skill exists.

432 510 

433The model decides whether to invoke a skill based on this metadata. If the model invokes a skill, it uses the `path` to read the full Markdown instructions from `SKILL.md`.511The model decides whether to invoke a skill based on this metadata. If the model invokes a skill, it uses the `path` to read the full Markdown instructions from `SKILL.md`.

434 512 


559 637 

560#### Validate data residency and retention requirements638#### Validate data residency and retention requirements

561 639 

562We support Skills in two form factors: local execution and hosted container-based execution. Hosted skills follow the same container lifecycle as hosted shell: mounted skills and container files remain available while the container is active and are discarded when the container expires or is deleted. If you want execution to stay entirely on infrastructure you manage, use local shell mode. Read more about our [data controls](https://developers.openai.com/api/docs/guides/your-data).640The Responses API supports Skills in two form factors: local execution and hosted container-based execution. Hosted skills follow the same container lifecycle as hosted shell: mounted skills and container files remain available while the container is active and are discarded when the container expires or is deleted. If you want execution to stay entirely on infrastructure you manage, use local shell mode. For Agents API sandboxes, see [Sandbox lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle). Read more about our [data controls](https://developers.openai.com/api/docs/guides/your-data).

Details

36 36 

37### Create the application server37### Create the application server

38 38 

39Save the server example in a new directory and set `OPENAI_API_KEY` in its environment. For Node.js, use `server.mjs` and install `openai` and `express` with `npm install openai express`. Install the corresponding OpenAI SDK for the other language variants; the Ruby example also uses `webrick`. This example binds to `127.0.0.1`, accepts session requests from `http://localhost:3000`, and serves `index.html` from the directory where you run it.39Save the server example in a new directory and set `OPENAI_API_KEY` in its environment. For Node.js, use `server.mjs` and install `openai` and `express` with `npm install openai express`. For Python, install `openai`. This example binds to `127.0.0.1`, accepts session requests from `http://localhost:3000`, and serves `index.html` from the directory where you run it.

40 40 

41Choose a server language below; each variant serves `index.html` and the same `/api/session` endpoint on port 3000. Use an SDK version with Live support. Run only one variant at a time.41Choose a server language below; each variant serves `index.html` and the same `/api/session` endpoint on port 3000. Use an SDK version with Live support. Run only one variant at a time.

42 42 


192 ThreadingHTTPServer(("127.0.0.1", 3000), SessionHandler).serve_forever()192 ThreadingHTTPServer(("127.0.0.1", 3000), SessionHandler).serve_forever()

193```193```

194 194 

195```go

196package main

197 

198import (

199 "encoding/json"

200 "log"

201 "net/http"

202 "os"

203 "strings"

204 

205 "github.com/openai/openai-go/v3"

206 "github.com/openai/openai-go/v3/live"

207 "github.com/openai/openai-go/v3/option"

208)

209 

210func main() {

211 client := openai.NewClient(option.WithMaxRetries(0))

212 const origin = "http://localhost:3000"

213 mux := http.NewServeMux()

214 mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) {

215 page, err := os.ReadFile("index.html")

216 if err != nil {

217 http.Error(w, "index.html unavailable", 500)

218 return

219 }

220 w.Header().Set("Content-Type", "text/html")

221 w.Write(page)

222 })

223 mux.HandleFunc("POST /api/session", func(w http.ResponseWriter, r *http.Request) {

224 // Local-only demo: add application authentication before exposing it.

225 if r.Header.Get("Origin") != origin {

226 http.Error(w, "Unexpected request origin", 403)

227 return

228 }

229 var offer struct {

230 SDP string `json:"sdp"`

231 }

232 r.Body = http.MaxBytesReader(w, r.Body, 65536)

233 if err := json.NewDecoder(r.Body).Decode(&offer); err != nil || strings.TrimSpace(offer.SDP) == "" {

234 http.Error(w, "An SDP offer is required", 400)

235 return

236 }

237 result, err := client.Live.New(r.Context(), live.LiveNewParams{

238 Session: live.MediaSessionConfigParam{

239 Model: "gpt-live-1",

240 Instructions: openai.String("Be concise. Delegate requests needing current information to the backend, which can search the web."),

241 Delegation: live.MediaSessionConfigDelegationUnionParam{

242 OfResponses: &live.MediaSessionConfigDelegationResponsesParam{

243 Responses: live.ResponsesDelegationConfigParam{

244 Model: "gpt-5.6-terra",

245 Instructions: openai.String("Use web search when current facts are needed. Return concise, grounded results for a spoken conversation."),

246 Tools: []live.ResponsesDelegationConfigToolUnionParam{{

247 OfWebSearch: &live.ResponsesDelegationConfigToolWebSearchParam{},

248 }},

249 ToolChoice: live.ResponsesDelegationConfigToolChoiceUnionParam{OfLiveToolChoiceEnum: openai.String("auto")},

250 },

251 },

252 },

253 },

254 Transport: live.LiveNewParamsTransport{Sdp: offer.SDP},

255 })

256 if err != nil {

257 log.Print(err)

258 http.Error(w, "Live session creation failed", 502)

259 return

260 }

261 // Return the SDK's typed session ID and SDP answer unchanged.

262 w.Header().Set("Content-Type", "application/json")

263 w.WriteHeader(http.StatusCreated)

264 json.NewEncoder(w).Encode(result)

265 })

266 log.Printf("Open %s", origin)

267 log.Fatal(http.ListenAndServe("127.0.0.1:3000", mux))

268}

269```

270 

271```java

272import com.fasterxml.jackson.databind.json.JsonMapper;

273import com.openai.client.OpenAIClient;

274import com.openai.client.okhttp.OpenAIOkHttpClient;

275import com.openai.core.ObjectMappers;

276import com.openai.models.live.LiveCreateParams;

277import com.openai.models.live.LiveCreateResponse;

278import com.openai.models.live.MediaSessionConfig;

279import com.openai.models.live.ResponsesDelegationConfig;

280import com.sun.net.httpserver.HttpExchange;

281import com.sun.net.httpserver.HttpServer;

282import java.io.IOException;

283import java.net.InetSocketAddress;

284import java.nio.charset.StandardCharsets;

285import java.nio.file.Files;

286import java.nio.file.Path;

287 

288public class LiveConnectionWebrtcExample {

289 record SDPOffer(String sdp) {}

290 

291 static void reply(HttpExchange exchange, int status, byte[] body, String contentType)

292 throws IOException {

293 exchange.getResponseHeaders().set("Content-Type", contentType);

294 exchange.sendResponseHeaders(status, body.length);

295 try (var output = exchange.getResponseBody()) {

296 output.write(body);

297 }

298 }

299 

300 public static void main(String[] args) throws IOException {

301 OpenAIClient client = OpenAIOkHttpClient.builder().fromEnv().maxRetries(0).build();

302 JsonMapper json = ObjectMappers.jsonMapper();

303 String origin = "http://localhost:3000";

304 HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 3000), 0);

305 server.createContext(

306 "/",

307 exchange -> {

308 String path = exchange.getRequestURI().getPath();

309 if (path.equals("/") && exchange.getRequestMethod().equals("GET")) {

310 reply(exchange, 200, Files.readAllBytes(Path.of("index.html")), "text/html");

311 return;

312 }

313 if (!path.equals("/api/session") || !exchange.getRequestMethod().equals("POST")) {

314 reply(exchange, 404, new byte[0], "text/plain");

315 return;

316 }

317 // Local-only demo: add application authentication before exposing it.

318 if (!origin.equals(exchange.getRequestHeaders().getFirst("Origin"))) {

319 reply(exchange, 403, new byte[0], "text/plain");

320 return;

321 }

322 SDPOffer offer;

323 try {

324 byte[] body = exchange.getRequestBody().readNBytes(65537);

325 if (body.length > 65536) throw new IOException("SDP offer too large");

326 offer = json.readValue(body, SDPOffer.class);

327 if (offer.sdp() == null || offer.sdp().isBlank())

328 throw new IOException("Missing SDP offer");

329 } catch (IOException error) {

330 reply(

331 exchange,

332 400,

333 "An SDP offer is required".getBytes(StandardCharsets.UTF_8),

334 "text/plain");

335 return;

336 }

337 try {

338 LiveCreateResponse result =

339 client

340 .live()

341 .create(

342 LiveCreateParams.builder()

343 .session(

344 MediaSessionConfig.builder()

345 .model("gpt-live-1")

346 .instructions(

347 "Be concise. Delegate requests needing current information to the backend, which can search the web.")

348 .responsesDelegation(

349 ResponsesDelegationConfig.builder()

350 .model("gpt-5.6-terra")

351 .instructions(

352 "Use web search when current facts are needed. Return concise, grounded results for a spoken conversation.")

353 .addToolWebSearch()

354 .toolChoice(

355 ResponsesDelegationConfig.ToolChoice

356 .LiveToolChoiceEnum.AUTO)

357 .build())

358 .build())

359 .transport(

360 LiveCreateParams.Transport.builder().sdp(offer.sdp()).build())

361 .build());

362 // Return the SDK's typed session ID and SDP answer unchanged.

363 reply(exchange, 201, json.writeValueAsBytes(result), "application/json");

364 } catch (com.openai.errors.OpenAIException error) {

365 System.err.println(error.getMessage());

366 reply(

367 exchange,

368 502,

369 "Live session creation failed".getBytes(StandardCharsets.UTF_8),

370 "text/plain");

371 }

372 });

373 System.out.println("Open " + origin);

374 server.start();

375 }

376}

377```

378 

379```ruby

380require "json"

381require "openai"

382require "webrick"

383 

384client = OpenAI::Client.new(max_retries: 0)

385origin = "http://localhost:3000"

386server = WEBrick::HTTPServer.new(Port: 3000, BindAddress: "127.0.0.1")

387server.mount_proc("/") do |request, response|

388 if request.path == "/" && request.request_method == "GET"

389 response["Content-Type"] = "text/html"

390 response.body = File.read("index.html")

391 next

392 end

393 unless request.path == "/api/session" && request.request_method == "POST"

394 response.status = 404

395 next

396 end

397 # Local-only demo: add application authentication before exposing it.

398 unless request["Origin"] == origin

399 response.status = 403

400 next

401 end

402 begin

403 raise ArgumentError if request.body.to_s.bytesize > 65_536

404 

405 offer = JSON.parse(request.body.to_s)

406 sdp = offer.fetch("sdp")

407 raise ArgumentError unless sdp.is_a?(String) && !sdp.strip.empty?

408 rescue JSON::ParserError, KeyError, ArgumentError

409 response.status = 400

410 response.body = "An SDP offer is required"

411 next

412 end

413 session = OpenAI::Models::Live::MediaSessionConfig.new(

414 model: "gpt-live-1",

415 instructions: "Be concise. Delegate requests needing current information to the backend, which can search the web.",

416 delegation: OpenAI::Models::Live::MediaSessionConfig::Delegation::Responses.new(

417 responses: OpenAI::Models::Live::ResponsesDelegationConfig.new(

418 model: "gpt-5.6-terra",

419 instructions: "Use web search when current facts are needed. Return concise, grounded results for a spoken conversation.",

420 tools: [OpenAI::Models::Live::ResponsesDelegationConfig::Tool::WebSearch.new],

421 tool_choice: :auto

422 )

423 )

424 )

425 begin

426 result = client.live.create(

427 session: session,

428 transport: OpenAI::Models::Live::LiveCreateParams::Transport.new(sdp: sdp)

429 )

430 # Return the SDK's typed session ID and SDP answer unchanged.

431 response.status = 201

432 response["Content-Type"] = "application/json"

433 response.body = result.to_json

434 rescue OpenAI::Errors::APIError => error

435 warn(error.message)

436 response.status = 502

437 response.body = "Live session creation failed"

438 end

439end

440trap("INT") { server.shutdown }

441puts "Open #{origin}"

442server.start

443```

444 

445 195 

446Before making the server accessible to other users, protect `/api/session` with your application's authentication, authorization, request limits, and HTTPS. The origin check in this local example does not authenticate users.196Before making the server accessible to other users, protect `/api/session` with your application's authentication, authorization, request limits, and HTTPS. The origin check in this local example does not authenticate users.

447 197 


604```354```

605 355 

606 356 

607Run your chosen server (`node server.mjs`, `python server.py`, `go run main.go`, `ruby server.rb`, or the Java `LiveConnectionWebrtcExample` class), open `http://localhost:3000`, and select **Start conversation**. After the status changes to **Connected**, ask a question that needs current information to exercise hosted search. Use the audio controls if your browser blocks autoplay.357Run your chosen server (`node server.mjs` or `python server.py`), open `http://localhost:3000`, and select **Start conversation**. After the status changes to **Connected**, ask a question that needs current information to exercise hosted search. Use the audio controls if your browser blocks autoplay.

608 358 

609### Read the session response359### Read the session response

610 360 

Details

397require "openai"397require "openai"

398 398 

399client = OpenAI::Client.new(399client = OpenAI::Client.new(

400 default_headers: {"OpenAI-Safety-Identifier" => "hashed-user-id"}400 default_headers: { "OpenAI-Safety-Identifier" => "hashed-user-id" }

401)401)

402 402 

403client.realtime.connect(model: "gpt-realtime-2.1") do |connection|403client.realtime.connect(model: "gpt-realtime-2.1") do |connection|

Details

6 6 

7To receive misalignment monitoring notifications for an API project, see [Receive project safety alerts](https://developers.openai.com/api/docs/guides/safety-checks/misalignment-monitoring#receive-project-safety-alerts).7To receive misalignment monitoring notifications for an API project, see [Receive project safety alerts](https://developers.openai.com/api/docs/guides/safety-checks/misalignment-monitoring#receive-project-safety-alerts).

8 8 

9For Agents API sessions, see [Session webhooks](https://developers.openai.com/api/docs/guides/agents-api/sessions/webhooks) for session events and recovery patterns. Use the endpoint setup, signature verification, and delivery guidance on this page for the webhook receiver.

10 

9[API reference for webhook events11[API reference for webhook events

10 12 

11 13 

12 14 

13 View the full list of webhook events.](https://developers.openai.com/api/reference/resources/webhooks)15 View the full list of webhook events.](https://developers.openai.com/api/reference/resources/webhooks)

14 16 

15Below are examples of simple servers capable of ingesting webhooks from OpenAI, specifically for the [`response.completed`](https://developers.openai.com/api/reference/resources/webhooks) event.17Below are examples of servers capable of ingesting webhooks from OpenAI, specifically for the [`response.completed`](https://developers.openai.com/api/reference/resources/webhooks) event.

16 18 

17For the Ruby examples, install the required dependencies with19For the Ruby examples, install the required dependencies with

18`gem install openai webrick`, then set `OPENAI_API_KEY` and20`gem install openai webrick`, then set `OPENAI_API_KEY` and

Details

117end118end

118 119 

119endpoint = Async::HTTP::Endpoint.parse("wss://api.openai.com/v1/responses", timeout: 10, alpn_protocols: ["http/1.1"])120endpoint = Async::HTTP::Endpoint.parse("wss://api.openai.com/v1/responses", timeout: 10, alpn_protocols: ["http/1.1"])

120headers = {"Authorization" => "Bearer #{ENV.fetch("OPENAI_API_KEY")}"}121headers = { "Authorization" => "Bearer #{ENV.fetch("OPENAI_API_KEY")}" }

121Sync do |task|122Sync do |task|

122 task.with_timeout(120) do123 task.with_timeout(120) do

123 Async::WebSocket::Client.connect(endpoint, headers: headers) do |connection|124 Async::WebSocket::Client.connect(endpoint, headers: headers) do |connection|

124 connection.write(JSON.generate(125 connection.write(

126 JSON.generate(

125 type: "response.create", stream_id: "main", model: "gpt-6-astra", store: false,127 type: "response.create", stream_id: "main", model: "gpt-6-astra", store: false,

126 input: [{role: "user", content: "Find fizz_buzz()"}], tools: []128 input: [

127 ))129 {

130 role: "user",

131 content: "Find fizz_buzz()"

132 }

133 ], tools: []

134 )

135 )

128 connection.flush136 connection.flush

129 print_response(wait_for_response(connection))137 print_response(wait_for_response(connection))

130 end138 end


329 end338 end

330end339end

331 340 

332tools = [{341tools = [

333 type: "function", name: "get_test_results", description: "Read the demo test results.",342 {

334 parameters: {type: "object", properties: {}, required: [], additionalProperties: false}, strict: true343 type: "function",

335}]344 name: "get_test_results",

345 description: "Read the demo test results.",

346 parameters: {

347 type: "object",

348 properties: {},

349 required: [],

350 additionalProperties: false

351 },

352 strict: true

353 }

354]

336 355 

337endpoint = Async::HTTP::Endpoint.parse("wss://api.openai.com/v1/responses", timeout: 10, alpn_protocols: ["http/1.1"])356endpoint = Async::HTTP::Endpoint.parse("wss://api.openai.com/v1/responses", timeout: 10, alpn_protocols: ["http/1.1"])

338headers = {"Authorization" => "Bearer #{ENV.fetch("OPENAI_API_KEY")}"}357headers = { "Authorization" => "Bearer #{ENV.fetch("OPENAI_API_KEY")}" }

339Sync do |task|358Sync do |task|

340 task.with_timeout(120) do359 task.with_timeout(120) do

341 Async::WebSocket::Client.connect(endpoint, headers: headers) do |connection|360 Async::WebSocket::Client.connect(endpoint, headers: headers) do |connection|

342 connection.write(JSON.generate(361 connection.write(

362 JSON.generate(

343 type: "response.create", stream_id: "main", model: "gpt-6-astra", store: false,363 type: "response.create", stream_id: "main", model: "gpt-6-astra", store: false,

344 input: "Find the failing test and suggest a fix.", tools: tools,364 input: "Find the failing test and suggest a fix.", tools: tools,

345 tool_choice: {type: "function", name: "get_test_results"}, parallel_tool_calls: false365 tool_choice: {

346 ))366 type: "function",

367 name: "get_test_results"

368 }, parallel_tool_calls: false

369 )

370 )

347 connection.flush371 connection.flush

348 response = wait_for_response(connection)372 response = wait_for_response(connection)

349 call = response.fetch("output").find { |item| item["type"] == "function_call" }373 call = response.fetch("output").find { |item| item["type"] == "function_call" }

350 unless call && call["name"] == "get_test_results" && JSON.parse(call.fetch("arguments")) == {}374 unless call && call["name"] == "get_test_results" && JSON.parse(call.fetch("arguments")) == {}

351 raise "Expected a get_test_results call with no arguments"375 raise "Expected a get_test_results call with no arguments"

352 end376 end

377 

353 # Demo data. Replace this with your test runner.378 # Demo data. Replace this with your test runner.

354 result = {test: "test_fizz_buzz", failure: 'Expected "FizzBuzz" for 15, got "Fizz".'}379 result = {

355 connection.write(JSON.generate(380 test: "test_fizz_buzz",

381 failure: 'Expected "FizzBuzz" for 15, got "Fizz".'

382 }

383 connection.write(

384 JSON.generate(

356 type: "response.create", stream_id: "main", model: "gpt-6-astra", store: false,385 type: "response.create", stream_id: "main", model: "gpt-6-astra", store: false,

357 previous_response_id: response.fetch("id"),386 previous_response_id: response.fetch("id"),

358 input: [387 input: [

359 {type: "function_call_output", call_id: call.fetch("call_id"), output: JSON.generate(result)},388 {

360 {role: "user", content: "Now optimize it."}389 type: "function_call_output",

390 call_id: call.fetch("call_id"),

391 output: JSON.generate(result)

392 },

393 {

394 role: "user",

395 content: "Now optimize it."

396 }

361 ],397 ],

362 tools: tools, tool_choice: "none"398 tools: tools, tool_choice: "none"

363 ))399 )

400 )

364 connection.flush401 connection.flush

365 print_response(wait_for_response(connection))402 print_response(wait_for_response(connection))

366 end403 end


516client = OpenAI::Client.new554client = OpenAI::Client.new

517compacted = client.responses.compact(555compacted = client.responses.compact(

518 model: "gpt-6-astra",556 model: "gpt-6-astra",

519 input: [{role: :user, content: "Find the failing test."}]557 input: [

558 {

559 role: :user,

560 content: "Find the failing test."

561 }

562 ]

520)563)

521next_input = compacted.output.map(&:to_h)564next_input = compacted.output.map(&:to_h)

522next_input << {role: :user, content: "Continue from here."}565next_input << {

566 role: :user,

567 content: "Continue from here."

568}

523 569 

524endpoint = Async::HTTP::Endpoint.parse("wss://api.openai.com/v1/responses", timeout: 10, alpn_protocols: ["http/1.1"])570endpoint = Async::HTTP::Endpoint.parse("wss://api.openai.com/v1/responses", timeout: 10, alpn_protocols: ["http/1.1"])

525headers = {"Authorization" => "Bearer #{ENV.fetch("OPENAI_API_KEY")}"}571headers = { "Authorization" => "Bearer #{ENV.fetch("OPENAI_API_KEY")}" }

526Sync do |task|572Sync do |task|

527 task.with_timeout(120) do573 task.with_timeout(120) do

528 Async::WebSocket::Client.connect(endpoint, headers: headers) do |connection|574 Async::WebSocket::Client.connect(endpoint, headers: headers) do |connection|

529 connection.write(JSON.generate(575 connection.write(

576 JSON.generate(

530 type: "response.create", stream_id: "main", model: "gpt-6-astra", store: false,577 type: "response.create", stream_id: "main", model: "gpt-6-astra", store: false,

531 input: next_input, tools: []578 input: next_input, tools: []

532 ))579 )

580 )

533 connection.flush581 connection.flush

534 print_response(wait_for_response(connection))582 print_response(wait_for_response(connection))

535 end583 end


831 879 

832def send_create(connection, stream_id, text, previous_response_id = nil)880def send_create(connection, stream_id, text, previous_response_id = nil)

833 payload = {881 payload = {

834 type: "response.create", stream_id: stream_id, model: "gpt-6-astra", store: false,882 type: "response.create",

835 input: [{role: "user", content: text}]883 stream_id: stream_id,

884 model: "gpt-6-astra",

885 store: false,

886 input: [

887 {

888 role: "user",

889 content: text

890 }

891 ]

836 }892 }

837 payload[:previous_response_id] = previous_response_id if previous_response_id893 payload[:previous_response_id] = previous_response_id if previous_response_id

838 connection.write(JSON.generate(payload))894 connection.write(JSON.generate(payload))


860end918end

861 919 

862endpoint = Async::HTTP::Endpoint.parse("wss://api.openai.com/v1/responses", timeout: 10, alpn_protocols: ["http/1.1"])920endpoint = Async::HTTP::Endpoint.parse("wss://api.openai.com/v1/responses", timeout: 10, alpn_protocols: ["http/1.1"])

863headers = {"Authorization" => "Bearer #{ENV.fetch("OPENAI_API_KEY")}"}921headers = { "Authorization" => "Bearer #{ENV.fetch("OPENAI_API_KEY")}" }

864Sync do |task|922Sync do |task|

865 task.with_timeout(120) do923 task.with_timeout(120) do

866 Async::WebSocket::Client.connect(endpoint, headers: headers) do |connection|924 Async::WebSocket::Client.connect(endpoint, headers: headers) do |connection|

Details

331authorization behavior, and current limitations, see the331authorization behavior, and current limitations, see the

332[workload identity token exchange reference](https://developers.openai.com/api/reference/workload-identity-federation).332[workload identity token exchange reference](https://developers.openai.com/api/reference/workload-identity-federation).

333 333 

334#### Renew the access token

335 

336If you manage token exchange directly, keep `access_token` and `expires_at`

337together when passing the credential from a token service to an application.

338The `expires_at` field is an absolute UTC expiration expressed as a Unix

339timestamp in seconds. Schedule renewal before that time, allowing for clock

340differences and request latency.

341 

342The `expires_in` field is the token's lifetime in seconds from issuance. For

343example, a token issued at 12:00 UTC with `expires_in: 3600` expires at 13:00

344UTC, even if another service receives it at 12:05 UTC. Transport and processing

345time don't extend the token's lifetime. See the [response

346fields](https://developers.openai.com/api/reference/workload-identity-federation#response) for details.

347 

348Token exchange doesn't return a refresh token. To renew, repeat the exchange

349with a valid external identity token or client certificate.

350 

334## Use workload identity with Codex351## Use workload identity with Codex

335 352 

336Use this path for trusted Codex automation in a managed ChatGPT workspace.353Use this path for trusted Codex automation in a managed ChatGPT workspace.

Details

390 "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",390 "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",

391 "token_type": "Bearer",391 "token_type": "Bearer",

392 "expires_in": 3600,392 "expires_in": 3600,

393 "expires_at": 1789045200,

393 "scope": "api.model.read api.model.request"394 "scope": "api.model.read api.model.request"

394}395}

395```396```

396 397 

397The `scope` property is returned only when the matching service account mapping has permissions.398The `scope` property is returned only when the matching service account mapping has permissions.

398 399 

399The `expires_in` value of `3600` is illustrative. The returned lifetime can be shorter when the verified client certificate expires sooner.400The expiration values are illustrative. The returned lifetime can be shorter when the verified client certificate expires sooner. See the [token exchange response fields](https://developers.openai.com/api/reference/workload-identity-federation#response) for the units and meaning of `expires_in` and `expires_at`.

400 401 

401Read the `access_token` value from the successful response into your application's credential store or an environment variable such as `OPENAI_WIF_ACCESS_TOKEN`. Treat it as a secret and don't print, log, or commit it.402Read the `access_token` value from the successful response into your application's credential store or an environment variable such as `OPENAI_WIF_ACCESS_TOKEN`. Treat it as a secret and don't print, log, or commit it.

402 403 


422 423 

423An X.509 workload identity token expires after at most one hour and never outlives the verified client certificate. The exchange doesn't return a refresh token. Repeat the certificate exchange to obtain another access token.424An X.509 workload identity token expires after at most one hour and never outlives the verified client certificate. The exchange doesn't return a refresh token. Repeat the certificate exchange to obtain another access token.

424 425 

426For manual exchanges, keep `expires_at` with the access token and schedule another exchange before that timestamp. Allow for clock differences and request latency. See [token renewal guidance](https://developers.openai.com/api/docs/guides/workload-identity-federation#renew-the-access-token) for an example.

427 

425Rotating an intermediate certificate doesn't require changing the configured root. Present the new complete chain on subsequent exchanges and API requests.428Rotating an intermediate certificate doesn't require changing the configured root. Present the new complete chain on subsequent exchanges and API requests.

426 429 

427## Troubleshoot token exchange430## Troubleshoot token exchange

Details

61| `/v1/conversations` | No | Until deleted | Until deleted | No | No |61| `/v1/conversations` | No | Until deleted | Until deleted | No | No |

62| `/v1/conversations/items` | No | Until deleted | Until deleted | No | No |62| `/v1/conversations/items` | No | Until deleted | Until deleted | No | No |

63| `/v1/chatkit/threads` | No | Until deleted | Until deleted | No | No |63| `/v1/chatkit/threads` | No | Until deleted | Until deleted | No | No |

64| `/v1/agents` | No | 30 days | Until deleted | No | No |

64| `/v1/assistants` | No | 30 days | Until deleted | No | No |65| `/v1/assistants` | No | 30 days | Until deleted | No | No |

65| `/v1/threads` | No | 30 days | Until deleted | No | No |66| `/v1/threads` | No | 30 days | Until deleted | No | No |

66| `/v1/threads/messages` | No | 30 days | Until deleted | No | No |67| `/v1/threads/messages` | No | 30 days | Until deleted | No | No |

libraries.md +1 −1

Details

173<dependency>173<dependency>

174 <groupId>com.openai</groupId>174 <groupId>com.openai</groupId>

175 <artifactId>openai-java</artifactId>175 <artifactId>openai-java</artifactId>

176 <version>4.62.0</version>176 <version>4.63.1</version>

177</dependency>177</dependency>

178```178```

179 179 

mcp.md +3 −5

Details

172 172 

173 173 

174```python174```python

175# Replace the illustrative IDs and URLs below with your own resource values.

175"""176"""

176Sample MCP Server for ChatGPT Integration177Sample MCP Server for ChatGPT Integration

177 178 


212 213 

213# OpenAI configuration214# OpenAI configuration

214OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]215OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]

215VECTOR_STORE_ID = os.environ["VECTOR_STORE_ID"]216VECTOR_STORE_ID = "vs_123"

216 217 

217# Initialize OpenAI client218# Initialize OpenAI client

218openai_client = OpenAI(api_key=OPENAI_API_KEY)219openai_client = OpenAI(api_key=OPENAI_API_KEY)


395 396 

396 397 

397 398 

398On Replit, you will need to configure two environment variables in the "Secrets" UI:399On Replit, configure `OPENAI_API_KEY` with your OpenAI API key in the "Secrets" UI. In the sample, replace `vs_123` with the ID of the vector store you created earlier for search.

399 

400- `OPENAI_API_KEY` - Your standard OpenAI API key

401- `VECTOR_STORE_ID` - The unique identifier of a vector store that can be used for search - the one you created earlier.

402 400 

403On free Replit accounts, server URLs are active for as long as the editor is active, so while you are testing, you'll need to keep the browser tab open. You can get a URL for your MCP server by clicking on the chainlink icon:401On free Replit accounts, server URLs are active for as long as the editor is active, so while you are testing, you'll need to keep the browser tab open. You can get a URL for your MCP server by clicking on the chainlink icon:

404 402 

models.md +1 −0

Details

121- [TTS-1](/api/docs/models/tts-1.md): Text-to-speech model optimized for speed121- [TTS-1](/api/docs/models/tts-1.md): Text-to-speech model optimized for speed

122- [TTS-1 HD](/api/docs/models/tts-1-hd.md): Text-to-speech model optimized for quality122- [TTS-1 HD](/api/docs/models/tts-1-hd.md): Text-to-speech model optimized for quality

123- [Whisper](/api/docs/models/whisper-1.md): General-purpose speech recognition model123- [Whisper](/api/docs/models/whisper-1.md): General-purpose speech recognition model

124- [GPT-Rosalind](/api/docs/pricing#specialized-models): Life sciences reasoning for approved organizations. Model ID: `gpt-rosalind-research`.

models/all.md +1 −0

Details

121- [TTS-1](/api/docs/models/tts-1.md): Text-to-speech model optimized for speed121- [TTS-1](/api/docs/models/tts-1.md): Text-to-speech model optimized for speed

122- [TTS-1 HD](/api/docs/models/tts-1-hd.md): Text-to-speech model optimized for quality122- [TTS-1 HD](/api/docs/models/tts-1-hd.md): Text-to-speech model optimized for quality

123- [Whisper](/api/docs/models/whisper-1.md): General-purpose speech recognition model123- [Whisper](/api/docs/models/whisper-1.md): General-purpose speech recognition model

124- [GPT-Rosalind](/api/docs/pricing#specialized-models): Life sciences reasoning for approved organizations. Model ID: `gpt-rosalind-research`.

quickstart.md +15 −6

Details

190<dependency>190<dependency>

191 <groupId>com.openai</groupId>191 <groupId>com.openai</groupId>

192 <artifactId>openai-java</artifactId>192 <artifactId>openai-java</artifactId>

193 <version>4.62.0</version>193 <version>4.63.1</version>

194</dependency>194</dependency>

195```195```

196 196 


1048 {1048 {

1049 role: "user",1049 role: "user",

1050 content: [1050 content: [

1051 {type: "input_file", file_id: file.id},1051 {

1052 {type: "input_text", text: "What is the first dragon in the book?"}1052 type: "input_file",

1053 file_id: file.id

1054 },

1055 {

1056 type: "input_text",

1057 text: "What is the first dragon in the book?"

1058 }

1053 ]1059 ]

1054 }1060 }

1055 ]1061 ]


1210 1216 

1211response = openai.responses.create(1217response = openai.responses.create(

1212 model: "gpt-6-astra",1218 model: "gpt-6-astra",

1213 tools: [{type: "web_search"}],1219 tools: [{ type: "web_search" }],

1214 input: "What was a positive news story from today?"1220 input: "What was a positive news story from today?"

1215)1221)

1216 1222 


1497 tools: [1503 tools: [

1498 {1504 {

1499 type: "code_interpreter",1505 type: "code_interpreter",

1500 container: {type: "auto"}1506 container: { type: "auto" }

1501 }1507 }

1502 ],1508 ],

1503 input: "I need to solve the equation 3x + 11 = 14. Can you help me?"1509 input: "I need to solve the equation 3x + 11 = 14. Can you help me?"


1772response = openai.responses.create(1778response = openai.responses.create(

1773 model: "gpt-6-astra",1779 model: "gpt-6-astra",

1774 input: [1780 input: [

1775 {role: "user", content: "What is the weather like in Paris today?"}1781 {

1782 role: "user",

1783 content: "What is the weather like in Paris today?"

1784 }

1776 ],1785 ],

1777 tools: tools1786 tools: tools

1778)1787)

Details

260 response = client.chat.completions.create(260 response = client.chat.completions.create(

261 model: "gpt-5.5",261 model: "gpt-5.5",

262 messages: [262 messages: [

263 {role: :system, content: instructions},263 {

264 {role: :user, content: transcription}264 role: :system,

265 content: instructions

266 },

267 {

268 role: :user,

269 content: transcription

270 }

265 ]271 ]

266 )272 )

267 response.choices.first.message.content || ""273 response.choices.first.message.content || ""