How to Run an OpenAI Agents API Sessionpublic

Last verified 21 Sep 2026

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 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.

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 for the executor.
  • For CLI provisioning, a doctl beta release that includes harness-runtime.
  • For application-managed or webhook-managed provisioning, a DigitalOcean API token 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.

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 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:

doctl auth init

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

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

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:

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:

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. OpenAI session deletion does not emit a webhook that removes the DigitalOcean sandbox.

Remove the DigitalOcean sandbox by name or DigitalOcean session ID:

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:

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:

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:

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

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

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 streams a task, downloads a generated file, and applies bounded timeouts.

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

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:

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 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:

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

We can't find any results for your search.

Try using different keywords or simplifying your search terms.