---
title: How to Run an OpenAI Agents API Session (public)
description: Connect an OpenAI Agents API session to a DigitalOcean Harness Runtime sandbox using the CLI, PyDo, or an OpenAI webhook controller.
product: Managed Agents
url: https://docs.digitalocean.com/products/managed-agents/agent-harness-runtime/how-to/run-openai-agents-api-session/
last_updated: "2026-10-21"
---

> **For AI agents:** The documentation index is at [https://docs.digitalocean.com/llms.txt](https://docs.digitalocean.com/llms.txt). Markdown versions of pages use the same URL with `index.html.md` in place of the HTML page (for example, append `index.html.md` to the directory path instead of opening the HTML document).

# How to Run an OpenAI Agents API Session (public)

DigitalOcean Harness Runtime provides managed, hardware-isolated microVM sandboxes with built-in tools such as Chromium to run harnesses and execute arbitrary code. Rich lifecycle APIs preserve conversational history and working state across sessions, letting you pause, resume, and fork work to control costs and adapt to the nonlinear nature of agentic workflows. Scale complete agents such as Claude Code or use sandboxes independently for code execution, all through the same service. See [What You Can Build](https://docs.digitalocean.com/products/managed-agents/agent-harness-runtime/details/what-you-can-build/index.html.md) for example use cases.

Use the `codex-agentapi` adapter to connect an OpenAI Agents API session to a DigitalOcean Harness Runtime sandbox. OpenAI owns the agent loop and session state. DigitalOcean runs the sandbox and the executor.

Choose one provisioning model per OpenAI session: CLI for interactive work, application-managed with PyDo when your application owns both resources, or webhook-managed when an HTTPS controller provisions sandboxes from OpenAI lifecycle events. Do not mix models for the same session. For ownership, identifier mapping, and recovery behavior, see [Agent Adapters](https://docs.digitalocean.com/products/managed-agents/agent-harness-runtime/concepts/agent-adapters/index.html.md#openai-agents-api-ownership).

## Prerequisites

- A sandbox-enabled DigitalOcean account with access to `codex-agentapi`.
- An OpenAI project with Agents API access.
- An OpenAI API key for application, CLI, or controller requests.
- A separate OpenAI [environment key](https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted#authentication) for the executor.
- For CLI provisioning, a [`doctl` beta release](https://github.com/digitalocean/doctl/releases) that includes `harness-runtime`.
- For application-managed or webhook-managed provisioning, a [DigitalOcean API token](https://docs.digitalocean.com/reference/api/create-personal-access-token/index.html.md) with write access.
- For webhook-managed provisioning, a public HTTPS service that can verify OpenAI webhook signatures.

Both OpenAI keys must have the same owner, organization, and project. Pass only the executor environment key into the sandbox as `CODEX_API_KEY`. Grant the request key the `api.agents.read`, `api.agents.write`, and `api.responses.write` permissions, and set every permission on the executor environment key to `None`, because its environment connection permission is assigned automatically. See [Secrets and Configuration](https://docs.digitalocean.com/products/managed-agents/agent-harness-runtime/concepts/secrets/index.html.md#scoping-credentials).

## Network and Permission Requirements

These apply to every provisioning model below.

### Egress

The adapter requires outbound access to two hosts, and the platform includes them for `codex-agentapi` sessions:

| Host | Purpose |
|---|---|
| `api.openai.com` | Connects the executor to the OpenAI session. |
| `codex-cloud-environments.chatgpt.com` | Carries commands and results between OpenAI and the sandbox. |

You do not need to list either host yourself. The spec examples below include them for clarity. If you set an egress allowlist, add the destinations the agent’s tools need beyond these two.

The executor connection is outbound, so it needs no public inbound address. If a firewall or proxy sits in the path, allow WebSocket upgrades and long-lived connections.

### Permissions

Harness Runtime [permission policies](https://docs.digitalocean.com/products/managed-agents/agent-harness-runtime/concepts/permissions/index.html.md) and approvals do not apply to `codex-agentapi`, because OpenAI owns the agent loop. Do not rely on a `permissions` block in the DigitalOcean spec to constrain this adapter’s tools.

## Run a Session With the CLI

Authenticate `doctl` with your DigitalOcean API token:

```shell
doctl auth init
```

Export the OpenAI API key used by the CLI and the environment key used by the executor:

```shell
export OPENAI_API_KEY=<your-openai-api-key>
export OPENAI_EXECUTOR_API_KEY=<your-openai-environment-key>
```

### Create the Environment Spec

Create a file named `agents.yaml` with the OpenAI session configuration and sandbox environment:

File: `agents.yaml`

```yaml
name: openai-codex-session
agent: codex-agentapi
config:
  agent:
    model: gpt-5.6-sol
    instructions: Work from the files in /workspace.
  environment:
    type: self_hosted
    workspace_directory: /workspace
egress:
  - api.openai.com
  - codex-cloud-environments.chatgpt.com
env:
  CODEX_ENVIRONMENT_ID: ${ENV_ID}
secrets:
  CODEX_API_KEY: ${OPENAI_EXECUTOR_API_KEY}
```

The `config` block is the OpenAI create-session request. `doctl` authenticates that request with `OPENAI_API_KEY`, fills `${ENV_ID}` from the response, and passes `OPENAI_EXECUTOR_API_KEY` into the sandbox as `CODEX_API_KEY`. Spec examples often list the OpenAI executor hosts under `egress` for clarity. The platform also includes those hosts for this adapter. Add any destinations used by the agent’s tools. Keep resolved manifests out of logs and source control.

### Create the Session and Sandbox

Create both resources from the environment spec:

```shell
doctl harness-runtime create --spec agents.yaml
```

The command waits up to 300 seconds for readiness by default. Save the OpenAI session ID and DigitalOcean session ID from the session details. You need both identifiers for management and cleanup.

### Attach to the Session

Attach your terminal to the session:

```shell
doctl harness-runtime launch openai-codex-session
```

Ask the agent to write `hello` to `/workspace/hello.txt` and read it back. Press `Ctrl+D` to detach without deleting either resource. Run the same `launch` command to reattach.

### Clean Up the CLI Session

Save any workspace files you need, then [delete the OpenAI session](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#delete-a-session). OpenAI session deletion does not emit a webhook that removes the DigitalOcean sandbox.

Remove the DigitalOcean sandbox by name or DigitalOcean session ID:

```shell
doctl harness-runtime remove openai-codex-session
```

Confirm both operations succeeded. A failure in either system leaves a resource that requires separate cleanup.

## Provision a Session With PyDo

Use the OpenAI SDK and PyDo when your application owns session creation, sandbox provisioning, input, and cleanup.

Install OpenAI Python SDK 3.13.0 or later and PyDo 0.40.0 beta 8, which includes asynchronous Agents support:

```shell
pip install "openai>=3.13.0" \
  "pydo[aio] @ https://github.com/digitalocean/pydo/releases/download/v0.40.0-beta.8/pydo-0.40.0b8-py3-none-any.whl"
```

Export the credentials:

```shell
export OPENAI_API_KEY=<your-openai-api-key>
export OPENAI_EXECUTOR_API_KEY=<your-openai-environment-key>
export DIGITALOCEAN_TOKEN=<your-digitalocean-api-token>
```

### Create the OpenAI Session

Create a self-hosted session with the OpenAI SDK:

```python
from openai import AsyncOpenAI

client = AsyncOpenAI()

session = await client.beta.agents.sessions.create(
    agent={
        "model": "gpt-5.6-sol",
        "instructions": "Work from the files in /workspace.",
    },
    environment={
        "type": "self_hosted",
        "workspace_directory": "/workspace",
    },
)
```

Save `session.id` and the environment ID returned with the session. The DigitalOcean create request needs both values.

### Create the Sandbox Spec

Create `sandbox.yaml` with only the DigitalOcean sandbox configuration. The application has already sent the model and instructions to OpenAI:

File: `sandbox.yaml`

```yaml
agent: codex-agentapi
egress:
  - api.openai.com
  - codex-cloud-environments.chatgpt.com
env:
  CODEX_ENVIRONMENT_ID: ${ENV_ID}
secrets:
  CODEX_API_KEY: ${OPENAI_EXECUTOR_API_KEY}
```

Add any destinations used by the agent’s tools to `egress`. Keep the unresolved file in source control, but do not log or save the resolved manifest because it contains the executor key.

### Provision the DigitalOcean Sandbox

Create helpers that create and destroy the sandbox:

File: `do_setup.py`

```python
from pathlib import Path

from pydo.aio import Client

async def start_sandbox(
    do_token,
    executor_key,
    openai_session_id,
    environment_id,
):
    async with Client(token=do_token) as client:
        response = await client.agents.create_session(
            params={"openai_session_id": openai_session_id},
            body={
                "manifest": Path("sandbox.yaml").read_text(),
                "variables": {
                    "ENV_ID": environment_id,
                    "OPENAI_EXECUTOR_API_KEY": executor_key,
                },
            },
        )
        return response["session"]

async def stop_sandbox(do_token, session_id):
    async with Client(token=do_token) as client:
        await client.agents.destroy_session(session_id=session_id)
```

Call `start_sandbox` with the OpenAI session ID, its environment ID, and `OPENAI_EXECUTOR_API_KEY`. Save the returned DigitalOcean `session_id`.

Use bounded setup and execution timeouts. If sandbox creation times out before returning an ID, reconcile DigitalOcean sessions before retrying to avoid creating a duplicate sandbox.

### Run the Session and Download Files

Open the OpenAI event stream and send input after starting the sandbox. Input waits for the executor to connect.

Treat connection failures, `agent.session.failed`, and turn failures as errors. Keep the OpenAI session and DigitalOcean sandbox for follow-up turns.

The complete [application-managed DigitalOcean example](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/digitalocean/application_managed) streams a task, downloads a generated file, and applies bounded timeouts.

Retrieve a generated file with `workspace_download`, using a path relative to `/workspace`:

```python
download = await digitalocean.agents.sessions.workspace_download(
    sandbox_id,
    path="plan.md",
    timeout=60,
)
contents = (await download.read()).decode("utf-8")
```

Save any required files before destroying the sandbox.

### Clean Up the Application-Managed Session

Destroy the DigitalOcean sandbox and delete the OpenAI session in separate cleanup operations:

```python
await digitalocean.agents.destroy_session(session_id=sandbox_id)
await client.beta.agents.sessions.delete(session.id)
```

Attempt both operations even if the first one fails, and report cleanup failures. OpenAI session deletion does not emit a webhook that removes the DigitalOcean sandbox.

Do not attach a provisioning webhook controller to an application-managed session.

## Manage Sessions With OpenAI Webhooks

Use an HTTPS webhook controller to provision DigitalOcean sandboxes when OpenAI sessions request an environment connection. The controller can resume a paused sandbox or create one when no active sandbox exists.

The [OpenAI webhook-managed DigitalOcean example](https://github.com/openai/openai-cookbook/tree/main/examples/agents_api/sandboxes/digitalocean/webhook_managed) provides an App Platform deployment specification and a Python controller.

### Configure the Controller

Set these environment variables on the controller:

- `OPENAI_API_KEY`: Authenticates session retrieval.
- `OPENAI_AGENT_ID`: Identifies the stored agent whose sessions this controller manages.
- `OPENAI_EXECUTOR_API_KEY`: Contains the environment key passed into sandboxes as `CODEX_API_KEY`.
- `OPENAI_WEBHOOK_SECRET`: Verifies OpenAI webhook signatures.
- `DIGITALOCEAN_TOKEN`: Authenticates Harness Runtime operations.

Store credentials as encrypted runtime variables. Do not add them to the application image, repository, or build context.

Run one controller worker and one replica unless the controller uses distributed coordination. A process-local lock protects only one process.

### Register the OpenAI Webhook

Deploy the controller, then register its `/webhook` endpoint with the OpenAI project. Subscribe to these events:

- `agent.session.action_required`
- `agent.session.failed`

Save the signing secret OpenAI returns as `OPENAI_WEBHOOK_SECRET`, then redeploy the controller.

### Reconcile Environment Connection Requests

For an `agent.session.action_required` event whose required action is `environment_connection`, perform these operations while holding a per-session lock:

1. Verify the webhook signature before parsing or acting on the payload.
2. Retrieve the current OpenAI session instead of relying only on the event payload.
3. Confirm that the session uses a self-hosted environment and belongs to `OPENAI_AGENT_ID`.
4. Confirm that the current required actions still include `environment_connection`.
5. Look up every DigitalOcean session named `mars-<openai-session-id>`, following pagination.
6. Select the newest non-terminal matching sandbox.
7. Resume it if it is paused, or create a sandbox if no active match exists.

If a matching sandbox is starting or already active, do not create another one. Duplicate and concurrent webhook deliveries are normal, so the handler must be idempotent.

An ambiguous create timeout requires reconciliation before retrying. Creating another sandbox without checking can leave more than one DigitalOcean resource associated with the same OpenAI session.

### Handle Failed Sessions

When the controller receives `agent.session.failed`, retrieve the OpenAI session again. Destroy the matching DigitalOcean sandbox only if the current OpenAI session status remains `failed`.

A delayed event may arrive after state has changed. Rechecking before deletion prevents the controller from destroying a sandbox based on stale information.

### Configure the Webhook Sandbox Spec

Use a flat environment spec for the DigitalOcean create request:

```yaml
name: mars-${OPENAI_SESSION_ID}
agent: codex-agentapi
config:
  agent:
    model: gpt-5.6-sol
  environment:
    type: self_hosted
    workspace_directory: /workspace
secrets:
  CODEX_API_KEY: ${OPENAI_EXECUTOR_API_KEY}
env:
  CODEX_ENVIRONMENT_ID: ${ENV_ID}
egress:
  - api.openai.com
  - codex-cloud-environments.chatgpt.com
```

The controller supplies the session-specific name, environment ID, and executor key when it calls PyDo. Credentials belong under `secrets`, not `env`.

### Clean Up the Webhook Controller

Deleting an OpenAI session does not emit a cleanup webhook. Delete both the OpenAI session and its DigitalOcean sandbox when the application finishes with them.

Remove the OpenAI webhook registration before deleting the controller. This prevents OpenAI from continuing to deliver events to an endpoint that no longer manages sandboxes.

## Next Steps

- [Agent Adapters](https://docs.digitalocean.com/products/managed-agents/agent-harness-runtime/concepts/agent-adapters/index.html.md#openai-agents-api-ownership) explains OpenAI ownership, provisioning models, and identifiers.
- [Secrets and Configuration](https://docs.digitalocean.com/products/managed-agents/agent-harness-runtime/concepts/secrets/index.html.md) explains credential scoping and what the secret store protects.
- [Secure Sessions](https://docs.digitalocean.com/products/managed-agents/agent-harness-runtime/how-to/secure-sessions/index.html.md) is the general hardening checklist for any adapter.
- [Environment Spec Reference](https://docs.digitalocean.com/products/managed-agents/agent-harness-runtime/reference/environment-spec/index.html.md#config-for-codex-agentapi) documents `codex-agentapi` fields and spec variants.
- [Connect a DigitalOcean sandbox to an Agents API session](https://developers.openai.com/api/docs/guides/agents-api/environments/providers/digitalocean) is OpenAI’s provider guide.