SPB Git forge

spb/doc-api

Public
2commits 1branches 0releases
15.7 MBsize
maindefault branch
13 days agolast push
Python 88.3% TypeScript 7.6% Shell 4.1%
13.1 KB

# Agents API — environments, sandboxes, files, artifacts, vaults and credentials

Status: DOCUMENTED · BETA (OpenAI-Beta: agents=v1) · LIVE_VERIFIED: environment-template create/retrieve/update/delete (201/200/200/200, examples/openai/agents/environment_template_create.*), GET /v1/agents/environments/templates, GET /v1/vaults, GET /v1/agents/sessions/{id}/artifacts (200, empty); LIVE_DISCOVERED: GET /v1/agents/environments/{bogus}, GET /v1/vaults/{bogus} → 404 not_found_error (routes confirmed). Vault/credential creation, hosted-sandbox sessions and live file operations not exercised (no credentials are ever created by this atlas; no container cost incurred). Sources: OpenAI-hosted sandboxes · Self-hosted sandboxes · Sandbox lifecycle · Files and artifacts · Sandbox security · Plugins · MCP · Vaults · provider guides (environments/providers/{modal,cloudflare,vercel,daytona,blaxel,e2b,runloop,digitalocean,oci}) · OpenAPI openapi-master.yaml. Last verified: 2026-09-18.

# 1. Environment types (session.environment)

Type Request (EnvironmentParam) Response (EnvironmentResource) Notes
none {type:"none"} {type:"none"} No filesystem/shell; initial input required; remote MCP (service) + function tools only
openai_hosted `type, packages{python[],system[],npm[]}, setup_commands[{command,cwd}] (≤16, confidential), network{access: enabled·disabled·restricted, allowed_domains[] (1–100 exact hosts)}, env{} (PATH/CODEX_*/OPENAI_API_KEY rejected), capability_directories[], skills[] (≤200: skill_reference{skill_id,version} inline{name,description,source{type:base64,media_type:application/zip,data}}), plugins[] (≤32 inline zips), files[] (≤50: file_id{file_id,path} inline{data,path}), environment_template_id`
self_hosted {type, workspace_directory (required, absolute), capability_directories[]} {type, id, remote_url, workspace_directory, capability_directories} You run codex exec-server; remote_url must be passed unchanged

Environment status (GET /v1/agents/environments/{environment_id} → agent.environment): pending → connected | disconnected | expired | failed; session-event state (SessionEnvironmentStateResource.status): pending · ready · connected · disconnected · failed (+ error{type,code,message}). Wait for connected before file operations.

# 2. OpenAI-hosted sandbox lifecycle

  1. POST /v1/agents/sessions with environment.type: openai_hosted → response means setup started; stream shows agent.session.environment.pending → ready → connected (or failed, read environment.error). agent.session.environment.reset (environment_id, reset_count) signals the sandbox was replaced — conversation survives, files/processes do not.
  2. Files persist across turns while the sandbox exists; /workspace/outputs/** is published as immutable artifacts at turn completion.
  3. Keep-alives run between turns; deletable after ~1 h of inactivity (not configurable). Sandbox compute billed at container rates.
  4. DELETE /v1/agents/sessions/{id} requests cleanup; 409 while busy → retry with a cap. Closing an event stream does not cancel work.

# Live files API (connected hosted environments)

  • GET /v1/agents/environments/{environment_id}/files?path=&limit=&order=&page= → {object:"page", data:[{object:"agent.environment.file", environment_id, path, size_bytes}], next, has_more} (token pagination, not cursor).
  • POST /v1/agents/environments/{environment_id}/files body HostedEnvironmentFileParam (file_id{file_id,path} or inline{data,path}) → 201 agent.environment.file.

# Artifacts (agent.session.artifact)

  • GET /v1/agents/sessions/{id}/artifacts?environment_id=&limit=&order=&after= → list; fields id, session_id, environment_id, turn_id, path, size_bytes, created_at.
  • GET …/artifacts/{artifact_id} metadata · GET …/artifacts/{artifact_id}/content → application/octet-stream · DELETE …/artifacts/{artifact_id} → agent.session.artifact.deleted (environment file untouched).
  • Versioning = (turn_id, path). Not uploadable/editable through the API. Self-hosted files are never published as artifacts.
  • Limits: 50 files/create request; inline 5 MiB/file, 10 MiB total; Files-API copy 50 MiB; artifact 200 MiB; outputs per turn 500 MiB. Spec maxLength of inline data is 6 990 508 chars (≈5 MiB base64).

# 3. Environment templates (agent.environment.template, hosted only)

POST/GET/POST/DELETE /v1/agents/environments/templates[/{environment_template_id}] — same fields as openai_hosted minus environment_template_id, plus name (1–256). Returned resource exposes safe metadata only: setup_commands and env are never returned; inline skills/plugins/files appear without contents (HostedTemplate*Resource). Sessions reference a template with environment.environment_template_id; omitted inline fields inherit, supplied arrays replace (e.g., plugins). Templates save configuration, not a running workspace. Update: omit to keep, null to clear (network: null resets to the default policy). Delete → {object:"agent.environment.template.deleted"}.

# 4. Self-hosted environments and the executor

  • Prepare: mkdir -p /workspace && npm install -g @openai/codex@alpha; allow egress to https://api.openai.com (registration) and wss://codex-cloud-environments.chatgpt.com (commands/results). All connections are outbound; executor reconnects automatically.
  • Keys: application key (api.agents.*, api.responses.write, optionally api.vaults.*) stays outside the sandbox; an environment key (dashboard Agents → Environments → Keys, same org/project/owner, all other permissions None) goes in as CODEX_API_KEY. Agent code can read it, but it only authorizes environment connections.
  • Start: codex exec-server --remote "<session.environment.remote_url>" --environment-id "<session.environment.id>". Webhook agent.session.created carries data.environment_id, data.environment_type, data.connect.remote_url (docs example https://api.openai.com/v1/agents/api).
  • Flow: create session (no input needed) → open event stream → start executor → send agent.session.input.message. Connection states: environment.pending → connected / failed. When input arrives with a disconnected executor the API adds required_actions[{type:"environment_connection", environment_id}], emits webhook agent.session.action_required and stream agent.session.requires_action before waiting up to 5 minutes; if it expires the submission fails (initial input can leave the session failed).
  • Lifecycle rules: one component per session's environment (no duplicates), keep compute between turns or apply a grace period, idle event is not a safe shutdown signal, reusing the environment ID does not restore files, deleting a session neither stops compute nor emits a webhook. Provider recipes: Modal, Cloudflare, Vercel, Daytona, Blaxel, E2B, Runloop, DigitalOcean, OCI (cookbook examples/agents_api/sandboxes/{application_managed,webhook_managed}).

# 5. Tools that depend on the environment

Tool none openai_hosted self_hosted
Bash / apply_patch (built-in) no yes yes
mcp http connection_origin: service (default) yes yes yes
mcp http connection_origin: environment no yes yes
mcp stdio (command, absolute cwd, args, env_vars; hosted needs network enabled) no yes yes
Skills / plugins (capability_directories, skills[], plugins[]) no yes (upload zips / skill_reference to /v1/skills) yes (directories)
web_search, function, tool_search, programmatic_tool_calling yes yes yes

Plugin package: .codex-plugin/plugin.json (name, version, description, skills: "./skills/", mcpServers: "./.mcp.json"), .mcp.json (mcpServers.<label>{type:http,url} / stdio with env_vars, bearer_token_env_var, literal http_headers; env_http_headers unsupported), skills/<name>/SKILL.md (frontmatter name, description). Hosted upload: one ZIP per plugin with name/description matching the manifest. Existing sessions do not reload changed plugins.

# 6. Vaults and credentials

Scopes: api.vaults.read (list/retrieve), api.vaults.write (create/rotate/delete). Header OpenAI-Beta: agents=v1 (live). SDK: client.beta.agents.vaults.*, client.beta.agents.vaults.credentials.*.

Endpoint Body / params Returns
POST /v1/vaults {name (1–256 bytes after trim), metadata{}} 201 vault {id, object:"vault", name, metadata, created_at}
GET /v1/vaults order, limit (1–100, default 20), status (active·archived, or status[]=), after list
GET /v1/vaults/{vault_id} · DELETE vault · vault.deleted (deletes all credentials; does not revoke provider tokens or stop running sessions)
POST /v1/vaults/{vault_id}/credentials {name, auth} where auth = static_bearer{mcp_server_url, token} · `mcp_oauth{mcp_server_url, access_token, expires_at (RFC 3339), refresh{token_endpoint, client_id, refresh_token, resource?, scope?, token_endpoint_auth{type: none client_secret_basic{client_secret}
GET …/credentials (status, cursor) · GET …/credentials/{credential_id} secrets never returned
POST …/credentials/{credential_id} (rotate) `{auth: static_bearer{token} mcp_oauth{access_token?, expires_at? (null clears), refresh{refresh_token?, scope?, token_endpoint_auth{client_secret?}}}
DELETE …/credentials/{credential_id} vault.credential.deleted

Usage: attach vault_ids: ["vault_…"] on session creation; MCP tool transport.server_url must match the credential's mcp_server_url; if several match, set the tool's credential_id. Vaults apply only to connection_origin: service; environment-origin HTTP needs inline transport.authorization/headers (encrypted, omitted from responses) or a trusted proxy; stdio servers read env vars listed in transport.env_vars. environment_variable credentials (hosted sandboxes only): the sandbox gets a placeholder in $SECRET_NAME; the egress proxy substitutes the real value only for allowed HTTPS destinations (networking.limited.allowed_hosts or unrestricted — the latter requires environment.network.access: restricted with explicit allowed_domains). The environment network policy must also permit those hosts.

Security guidance (docs): isolate workloads (VMs, one project per app), restrict egress to the executor hosts + needed MCP servers, keep app keys out of images/source/logs, broker third-party credentials via a proxy, rotate/revoke on suspicion.

# 7. Live verification (2026-09-18)

Call Result
GET /v1/agents/environments/templates?limit=5 200 {object:list, data:[]}
GET /v1/agents/environments/env_doesnotexist 404 not_found_error "No managed agent resource found: env_doesnotexist"
GET /v1/vaults?limit=5 200 empty list
GET /v1/vaults/vault_doesnotexist 404 not_found_error "No vault resource found: vault_doesnotexist"
GET /v1/agents/sessions/{id}/artifacts (env none) 200 empty list
POST /v1/agents/environments/templates (packages.python, setup_commands, network restricted + allowed_domains, env, inline file) 201 agent.environment.template (envtmpl_…); response omits setup_commands/env; inline file echoed as {type, path, size_bytes: 8}
POST …/templates/{id} (name only) · GET …/templates/{id} · DELETE …/templates/{id} 200 (network preserved) · 200 · 200 agent.environment.template.deleted
Vault create, credential create, hosted sandbox session, live files not run (policy: no credential creation; no container spend)