Agent Sandbox Registry

python-sdk

Agents API reference

docs/en/reference/agents-api.md

Agents API reference

Every HTTP route the sandbox server serves once the agents capability group is on. The group is off by default. All 70 routes below are implemented and each one has a test. Two runtimes have a local backend: Codex (binary codex) and Qwen Code (binary qwen). claude-code, cursor and command are declared adapters with no local backend: GET /v1/agents/runtimes reports implemented: false for them, and a request that selects one gets 404 agents.not_implemented_locally whose body points at GET /v1/agents/runtimes.

This is not sb.agent.*. Usage and the worked case are in Agents API. Field-level refusals are in Hosted agents. This page lists parameters and responses per route.

Endpoints are declared in server-sdk/src/agent_sandbox/server/endpoints.py; handlers are mounted in routes_agents.py.

Common conventions

Turn the group on:

export ABX_SERVER_ENABLED_GROUPS=agents      # or server.groups: [agents] in template.yaml
python -m agent_sandbox.server serve --port 9000
Item Value
Base URL http://<host>:9000. An OpenAI-style client uses base_url=http://<host>:9000/v1
Request body Content-Type: application/json, an object. An empty body is {}
Token With ABX_SERVER_TOKEN set, every route accepts X-Access-Token or Authorization: Bearer
Local marker Every response, errors included, has the header X-Agent-Sandbox-Local: 1. Success objects carry x_agent_sandbox. compute is local_process, local_state or none, never openai_hosted
Owner The caller's identity id (anonymous when no token is set). Another owner's id and a missing id get the same answer

Error envelope:

{
  "error": {
    "message": "explanation",
    "type": "invalid_request_error",
    "param": "field name or null",
    "code": "agents.not_implemented_locally"
  }
}

type is not_found_error for 404. A variant the server cannot honor is 400 with x-should-retry: false. A route with no local capability, and a request that selects a runtime that is not implemented, is 404 with code agents.not_implemented_locally, x-should-retry: false, and x_agent_sandbox.runtimes pointing at /v1/agents/runtimes. A disabled endpoint is 404 endpoint_disabled with a rule that names the layer that turned it off.

List envelope (every route the tables below call a list):

Query parameter Meaning
limit Integer 1-100, default 20. Outside that is 400 invalid_request, param=limit
order asc or desc, default desc. Any other value is 400
after The id of the last item of the previous page. Not in the current sequence is 404 agents.cursor_not_found
{
  "object": "list",
  "data": [],
  "first_id": null,
  "last_id": null,
  "has_more": false
}

Main fields of the session object agent.session: id, object, agent (id, name, model, instructions, tools, multi_agent.enabled, reasoning, service_tier, text), created_at, environment (type=self_hosted, id, workspace_directory), status, metadata, usage, vault_ids, error, required_actions, last_active_at, x_agent_sandbox.

Adapter selection order: the header X-ABX-Adapter, or the body adapter / runtime, or agent.model / model being exactly codex, openai-codex, qwen-code, qwen, qwen_code or qwencode. A model id such as gpt-5 is passed to the selected CLI and does not select an adapter. With none of them the first in product order is used: codex. default / agents.default in ABX_AGENTS_DEFAULT or ABX_AGENTS_CONFIG changes that default. GET /v1/agents/runtimes?order=asc lists codex, qwen-code, then the declared-only adapters. Lists default to order=desc. default: true marks the current default regardless of array index. A declared adapter that is not implemented (claude-code, cursor, command) is 404 agents.not_implemented_locally; so is an unknown name. Each route can be enabled on its own; the endpoint ids have the default scope agents:use, see Fine-grained capabilities.

The CLIs are not in the base image. Install one when you need it:

npm install -g @openai/codex
npm install -g @qwen-code/qwen-code

Sessions, events, items, turns

POST /v1/agents/sessions

Creates a session. 200, the body is an agent.session.

Field Where Notes
agent body Object. model is an adapter name or a model id to pass to the CLI. instructions is optional
agent_id body Id of a saved definition. When given, the adapter is not chosen from model
adapter / runtime body or inside agent Explicit adapter name
input body Optional. A string, or a message with input_text. Non-empty content queues a turn at once
metadata body Object of string scalars. Nested objects, arrays and null are 400
environment body Optional { "type": "self_hosted", "id": "env_..." }
vault_ids body Optional array of strings
X-ABX-Adapter header Optional

environment.type is always self_hosted. workspace_directory defaults to /workspace.

GET /v1/agents/sessions

List. Besides paging, the query accepts agent_id (filter by definition id). 200, a list of agent.session.

GET /v1/agents/sessions/{id}

Path parameter id. 200, one agent.session. Missing is 404 resource_not_found.

POST /v1/agents/sessions/{id}

Updates metadata only. agent, vault_ids, environment or any other key in the body is 400. 200, the updated agent.session.

DELETE /v1/agents/sessions/{id}

Deletes the session and clears the artifact snapshots. 200:

{ "id": "agsess_...", "object": "agent.session.deleted", "deleted": true, "x_agent_sandbox": {} }

POST /v1/agents/sessions/{id}/events

Queues a turn. 202, empty body.

Field Notes
events Non-empty array of objects
events[].type agent.session.input.message or input.message takes input / content / text. agent.session.input.cancel or input.cancel cancels. tool_result and computer_use_approval_request_result are 400 agents.tool_result_unsupported. Any other type is 400
input Without events, one input may be given directly
Idempotency-Key Optional header. The same key with the same body replays this 202. The same key with a different body is 409 idempotency.mismatch

input_image and input_file are 400. Input sent during a turn queues a new turn (queued_not_steered); it is never written into the running process.

GET /v1/agents/sessions/{id}/events

SSE, Accept: text/event-stream. Query after: without it the stream follows from now until that turn ends; after=0 or after=evt_<seq> replays and closes. The frame id is the runtime sequence number.

GET /v1/agents/sessions/{id}/items

GET /v1/agents/sessions/{id}/turns

GET /v1/agents/sessions/{id}/turns/{turn_id}

GET /v1/agents/sessions/{id}/turns/{turn_id}/items

All are lists. For an item with type=message, content[].type is output_text and content[].text is the CLI text. In the local case Codex's text is hello-from-fake-codex and Qwen Code's is hello-from-fake-qwen.

GET /v1/agents/runtimes

200, a list with every declared runtime. Implemented ones come first (codex, qwen-code), then the declared-only adapters. Each entry:

Field Notes
id / name codex, qwen-code, claude-code, cursor or command
implemented Whether the server has a local backend. Only codex and qwen-code are true. A runtime with false is refused whatever installed says
installed Whether the binary is on the current PATH
default Whether a request that names no adapter uses this one. Product order is codex then qwen-code; order=asc sorts that way. The default is order=desc
object agent.runtime
aliases Codex: openai-codex. Qwen: qwen, qwen_code, qwencode
binary The runtime's binary name
path Where it was found
install_steps [["npm","install","-g","@openai/codex"]], or the Qwen package @qwen-code/qwen-code
auth_env_present Whether the process already has the CLI's key variable
x_agent_sandbox compute: none, plus installed and implemented

Artifacts, traces, subagents

Method Path Success Response
GET /v1/agents/sessions/{id}/artifacts 200 list Snapshots of files the CLI reported. The workspace is not scanned
GET /v1/agents/sessions/{id}/artifacts/{artifact_id} 200 One artifact. path looks like /workspace/<relative path>
DELETE same 200 Deletes the snapshot only. deleted_scope is snapshot; the original file is untouched
GET .../artifacts/{artifact_id}/content 200 Snapshot bytes, X-Content-Type-Options: nosniff
GET /v1/agents/sessions/{id}/traces 200 list Empty by default. With ABX_AGENTS_TRACES=1, one trace per finished turn with timing and item counts, no content
GET .../subagents 200 list Only ids the CLI actually emitted
GET .../subagents/{subagent_id} 200 One subagent. Any other id is 404 resource_not_found, same as a missing one
GET .../subagents/{subagent_id}/items 200 list
GET .../subagents/{subagent_id}/turns 200 list
GET .../turns/{turn_id} 200
GET .../turns/{turn_id}/items 200 list

multi_agent.enabled=true is still 400.

Environments, files, templates

POST /v1/agents/environments

Body: type must resolve to self_hosted. A bare { "type": "openai_hosted" } is converted to self_hosted and noted in x_agent_sandbox.differences. openai_hosted with configuration is 400.

Field Notes
workspace_directory Absolute path, no .., not /, /etc, /proc and the like
env Applied at creation, never returned. PATH, HOME, secrets, proxies, CODEX_*, QWEN_*, GIT_*, ABX_* are refused
files Inline files, written under the workspace at creation

400: network, packages, setup_commands, plugins, skills, desktop, container_size, capability_directories. 200, an environment object. No container is provisioned.

GET /v1/agents/environments

List. 200.

GET /v1/agents/environments/{environment_id}

200, one environment, or 404.

POST /v1/agents/environments/{environment_id}/files

Body: path and the content. The path is relative to the workspace. A symlink is 400. An existing file with different content is 400 agents.file_exists. Writing .git/, .codex/, .claude/, .cursor/, .qwen/, .qoder/, .ssh/, or .mcp.json, AGENTS.md, QWEN.md, a shell rc file, is 400 agents.path_refused. Size limit ABX_AGENTS_MAX_FILE_BYTES, default 10 MiB. 200.

GET /v1/agents/environments/{environment_id}/files

200 list. Regular files only. Query page is an opaque paging token; the next page is in next.

Environment templates

Method Path Body Success
POST /v1/agents/environments/templates {name, env, files}. Fields the environment refuses are 400 here too 200
GET /v1/agents/environments/templates none 200 list. env values and file contents are not returned, only size_bytes
GET .../templates/{environment_template_id} none 200
POST same update {name, env, files} 200
DELETE same none 200

A template is applied to an environment through environment_template_id on a session or environment.

Executor routes that are refusals

Method Path Response
POST /v1/agents/environments/{environment_id}/register 404 agents.not_implemented_locally
GET /v1/agents/environments/{environment_id}/connect same

Agent definitions

POST /v1/agents

Field Notes
model Required, unless the string is itself an adapter name. Otherwise passed to the CLI as -m
name Optional
instructions Optional
metadata Object of string scalars
tools Only type=mcp tools with http transport. stdio, allowed_tools and other tool types are 400
reasoning.effort Codex only. 400 on Qwen Code
reasoning.summary 400
multi_agent.enabled 400
service_tier Only auto or default
text.verbosity Only medium
text.format Only text

200. id looks like agent_.... MCP header values are "***" in the response.

GET /v1/agents

200 list.

GET /v1/agents/{agent_id}

200, or 404 resource_not_found with param=agent_id.

POST /v1/agents/{agent_id}

Updates in place. Omitted fields are unchanged. adapter or runtime in the body is 400: a definition cannot change runtime. 200. A running session keeps the copy it started with.

DELETE /v1/agents/{agent_id}

200: { "id", "object": "agent.deleted", "deleted": true }. Existing sessions are not stopped.

Vaults and credentials

Vaults live in memory. Each owner may hold at most ABX_AGENTS_MAX_VAULTS (100 by default).

Method Path Body Success
POST /v1/vaults Only name and metadata. Other keys are 400 200. The object has only id, name, metadata, created_at
GET /v1/vaults paging 200 list
GET /v1/vaults/{vault_id} none 200, or 404
POST /v1/vaults/{vault_id} only name, metadata may change 200
DELETE /v1/vaults/{vault_id} none 200

Credentials:

Method Path Body Success
POST .../credentials name required, metadata optional, auth required 200. The response carries no secret value, prefix, length or hash
GET .../credentials paging 200 list
GET .../credentials/{credential_id} none 200
POST same update auth and other allowed fields 200. Takes effect from the next turn
DELETE same none 200

auth allows static_bearer, or mcp_oauth with an access token. 400: environment_variable, mcp_oauth.refresh, networking.unrestricted, limited hosts other than the credential's host, an mcp_server_url that is not https or points at a private network, a token containing a newline.

Once attached through a session's vault_ids, a credential is injected only into an MCP tool whose scheme, host and port all match, as an Authorization header in the child process environment, never in argv. When two credentials match, credential_id is required. A credential that is deleted or past expires_at makes the next event 400 agents.credential_unavailable or agents.credential_expired.

Responses

POST /v1/responses treats one CLI turn as one response. The object of a success is response.

Field Notes
model The id of a saved definition, or a model passed through to the CLI (for example gpt-5-codex). A runtime name such as qwen means the CLI's default model, and the response has model_resolved: false
input A string, or user / system / developer messages with input_text content
instructions Optional string
metadata String scalars
store Omitted or true stores it. false together with background is 400. A store: false response disappears when the turn ends
background Boolean. When true you need not wait for the turn
stream When true, the finished turn is replayed as SSE (response.created, response.output_text.delta, response.completed)
previous_response_id Continues a previous one. Only the latest response of that conversation can be continued, otherwise 400 agents.previous_response_not_latest
tools MCP only: server_label, server_url, write-only headers and authorization
reasoning.effort Codex only

400: a non-MCP entry in tools, conversation, prompt, a text.format other than text, any include, service_tier other than auto|default, tool_choice other than auto, input_image, input_file, assistant history, unknown parameters. temperature, top_p and max_output_tokens are ignored and listed in the response header X-Agent-Sandbox-Ignored-Params.

Output items are only what the CLI actually produced: message, reasoning, command_execution, mcp_call, and tool_call (with tool_name) for other tools. web_search_call, function_call, code_interpreter_call and local_shell_call never appear. When the CLI reports no usage, usage is null.

Method Path Success
POST /v1/responses 200, or SSE with stream: true
GET /v1/responses 400 agents.websocket_upgrade_required without a WebSocket upgrade
POST /v1/responses/input_tokens 404 agents.not_implemented_locally. There is no tokenizer that matches the CLI's model
POST /v1/responses/compact The body needs previous_response_id. Qwen runs a local /compress. Codex is 400 agents.compact_unsupported
GET /v1/responses/{response_id} 200. ?stream=true&starting_after=N replays again
DELETE /v1/responses/{response_id} 200
POST .../cancel 200 with the terminal object. An already finished one is 400 agents.not_cancellable, not 409
GET .../input_items 200 list. The input accepted at creation

Idempotency-Key replays the first response. A different body is 409.

Models, files, webhooks

Method Path Parameters Success
GET /v1/models paging 200 list. The caller's definitions and the two implemented runtimes. owned_by is agent-sandbox-local
GET /v1/models/{model} path model 200, or 404 model_not_found. No side effect
DELETE /v1/models/{model} 404 agents.not_implemented_locally. Delete a definition with DELETE /v1/agents/{agent_id}
POST /v1/files JSON: purpose must be user_data, filename, data as base64. Multipart is not parsed 200. The file_id lives in memory. Using an id that was never issued is 400

Webhooks. The signing secret whsec_... is returned once, on creation and on rotation.

Method Path Body Success
POST /v1/webhook_endpoints Only url, event_types, name. url must be https, with no userinfo, no query string, not a private or loopback address. For local development set ABX_AGENTS_WEBHOOK_ALLOW_HTTP=1 and ABX_AGENTS_ALLOW_PRIVATE_URLS=1 200, with a one-time secret
GET /v1/webhook_endpoints paging 200 list. No secret
GET .../{webhook_endpoint_id} none 200. No secret
POST same url, event_types, name 200
DELETE same none 200
POST .../rotate_secret keep_old_secret_active_for_24_hours is 400 200, the new secret appears only this once
POST .../test event_type 200. The server signs and delivers per Standard Webhooks
GET /v1/webhook_event_types none 200. Accepts agent.session.created, in_progress, idle, failed. agent.session.action_required is not accepted

Route index

All 70 routes. Endpoint ids are the ones GET /capabilities and the policy use. The four marked refusal exist to explain with a typed 404 that the server has no such capability (agents.not_implemented_locally); they are declared, counted and tested like the rest.

Endpoint id Method Path Status
agents_sessions_create POST /v1/agents/sessions implemented
agents_sessions_list GET /v1/agents/sessions implemented
agents_sessions_get GET /v1/agents/sessions/{id} implemented
agents_sessions_update POST /v1/agents/sessions/{id} implemented
agents_sessions_delete DELETE /v1/agents/sessions/{id} implemented
agents_sessions_events_create POST /v1/agents/sessions/{id}/events implemented
agents_sessions_events_stream GET /v1/agents/sessions/{id}/events implemented
agents_sessions_items_list GET /v1/agents/sessions/{id}/items implemented
agents_sessions_turns_list GET /v1/agents/sessions/{id}/turns implemented
agents_sessions_turns_get GET /v1/agents/sessions/{id}/turns/{turn_id} implemented
agents_sessions_turns_items_list GET /v1/agents/sessions/{id}/turns/{turn_id}/items implemented
agents_artifacts_list GET /v1/agents/sessions/{id}/artifacts implemented
agents_artifacts_get GET /v1/agents/sessions/{id}/artifacts/{artifact_id} implemented
agents_artifacts_delete DELETE /v1/agents/sessions/{id}/artifacts/{artifact_id} implemented
agents_artifacts_content GET /v1/agents/sessions/{id}/artifacts/{artifact_id}/content implemented
agents_traces_list GET /v1/agents/sessions/{id}/traces implemented
agents_subagents_list GET /v1/agents/sessions/{id}/subagents implemented
agents_subagents_get GET /v1/agents/sessions/{id}/subagents/{subagent_id} implemented
agents_subagents_items_list GET /v1/agents/sessions/{id}/subagents/{subagent_id}/items implemented
agents_subagents_turns_list GET /v1/agents/sessions/{id}/subagents/{subagent_id}/turns implemented
agents_subagents_turns_get GET /v1/agents/sessions/{id}/subagents/{subagent_id}/turns/{turn_id} implemented
agents_subagents_turns_items_list GET /v1/agents/sessions/{id}/subagents/{subagent_id}/turns/{turn_id}/items implemented
agents_runtimes_list GET /v1/agents/runtimes implemented
agents_environments_create POST /v1/agents/environments implemented
agents_environments_list GET /v1/agents/environments implemented
agents_environment_templates_create POST /v1/agents/environments/templates implemented
agents_environment_templates_list GET /v1/agents/environments/templates implemented
agents_environment_templates_get GET /v1/agents/environments/templates/{environment_template_id} implemented
agents_environment_templates_update POST /v1/agents/environments/templates/{environment_template_id} implemented
agents_environment_templates_delete DELETE /v1/agents/environments/templates/{environment_template_id} implemented
agents_environments_get GET /v1/agents/environments/{environment_id} implemented
agents_environment_files_create POST /v1/agents/environments/{environment_id}/files implemented
agents_environment_files_list GET /v1/agents/environments/{environment_id}/files implemented
agents_cannot_executor_registration POST /v1/agents/environments/{environment_id}/register refusal
agents_cannot_executor_channel GET /v1/agents/environments/{environment_id}/connect refusal
agents_definitions_create POST /v1/agents implemented
agents_definitions_list GET /v1/agents implemented
agents_definitions_get GET /v1/agents/{agent_id} implemented
agents_definitions_update POST /v1/agents/{agent_id} implemented
agents_definitions_delete DELETE /v1/agents/{agent_id} implemented
agents_vaults_create POST /v1/vaults implemented
agents_vaults_list GET /v1/vaults implemented
agents_vaults_get GET /v1/vaults/{vault_id} implemented
agents_vaults_update POST /v1/vaults/{vault_id} implemented
agents_vaults_delete DELETE /v1/vaults/{vault_id} implemented
agents_vault_credentials_create POST /v1/vaults/{vault_id}/credentials implemented
agents_vault_credentials_list GET /v1/vaults/{vault_id}/credentials implemented
agents_vault_credentials_get GET /v1/vaults/{vault_id}/credentials/{credential_id} implemented
agents_vault_credentials_update POST /v1/vaults/{vault_id}/credentials/{credential_id} implemented
agents_vault_credentials_delete DELETE /v1/vaults/{vault_id}/credentials/{credential_id} implemented
agents_responses_create POST /v1/responses implemented
agents_responses_connect GET /v1/responses implemented
agents_cannot_responses_input_tokens POST /v1/responses/input_tokens refusal
agents_responses_compact POST /v1/responses/compact implemented
agents_responses_get GET /v1/responses/{response_id} implemented
agents_responses_delete DELETE /v1/responses/{response_id} implemented
agents_responses_cancel POST /v1/responses/{response_id}/cancel implemented
agents_responses_input_items GET /v1/responses/{response_id}/input_items implemented
agents_models_list GET /v1/models implemented
agents_models_get GET /v1/models/{model} implemented
agents_cannot_models_delete DELETE /v1/models/{model} refusal
agents_files_create POST /v1/files implemented
agents_webhooks_create POST /v1/webhook_endpoints implemented
agents_webhooks_list GET /v1/webhook_endpoints implemented
agents_webhooks_get GET /v1/webhook_endpoints/{webhook_endpoint_id} implemented
agents_webhooks_update POST /v1/webhook_endpoints/{webhook_endpoint_id} implemented
agents_webhooks_delete DELETE /v1/webhook_endpoints/{webhook_endpoint_id} implemented
agents_webhooks_rotate_secret POST /v1/webhook_endpoints/{webhook_endpoint_id}/rotate_secret implemented
agents_webhooks_test POST /v1/webhook_endpoints/{webhook_endpoint_id}/test implemented
agents_webhook_event_types GET /v1/webhook_event_types implemented

End-to-end coverage

Case Interfaces covered Notes
examples/agents/complete_case.py POST /v1/agents/sessions, POST .../events, GET .../items, GET /v1/agents/runtimes. With no adapter named, agent.id is codex. claude-code is 404 The events response is a 202 with an empty body. The assistant text in the example comes from a local program on PATH; no model key is read
examples/agents/hosted_codex.py, hosted_qwen.py The same session interfaces, against an already started server The same 200 / 202 / item text as above
examples/scenarios/agent_loop_local.py Not the Agents API. Sandbox commands: health, shell, read a file, SIGTERM stdout '4\n', read back answer=4, returns -15

Parameters and responses of definitions, environments, vaults, responses and webhooks are as in the tables above.