/health
Liveness check. The only route that doesn't live under /v1.
Response 200
{ status: "ok", version: "0.1.0" }
$ cat docs/api.md
Complete reference for the HTTP API exposed by AgentCore: the adapter's own endpoints (configuration, sessions, events) and the exclusive controls of each already implemented runtime (Claude Code, Codex and OpenCode).
# local base URL, defined in src/server.ts
$ curl http://127.0.0.1:3000/health
# all routes below, except /health, live under the /v1 prefix
$ curl http://127.0.0.1:3000/v1/sessions
There's no per-provider URL namespace (no /providers/claude or
/:provider/...). Every session, regardless of runtime, is accessed through the
same /v1/sessions/:sessionId/... endpoints. The distinction happens inside
the handler, by looking at the session's runtime field. Routes that don't make sense
for a given runtime respond with 400 and a message explaining the restriction.
Errors always follow the same format, { "error": "message" }, with the
HTTP status communicating the category:
| Status | Meaning |
|---|---|
400 | Invalid body/params, or the operation doesn't apply to the session's runtime |
404 | Session, permission or history not found |
409 | State conflict (session busy, no conversation to fork/rewind yet, etc.) |
502 | Failed to talk to the provider (Claude SDK / Codex SDK / OpenCode server) |
CORS is only enabled outside production (NODE_ENV !== "production").
In production the API is meant to be consumed locally, with no browser crossing origins.
{
id: string,
runtime: "claude" | "codex" | "opencode",
projectPath: string,
providerSessionId?: string,
status: "ready" | "running" | "waiting_permission"
| "completed" | "cancelled" | "error",
createdAt: Date,
title?: string,
forkedFrom?: string,
forkedFromMessageId?: string,
tag?: string,
permissionMode?: "default" | "acceptEdits" | "bypassPermissions"
| "plan" | "dontAsk" | "auto",
model?: string,
// Claude-only
claudeDeniedTools?: string[],
claudeEffortLevel?: "low" | "medium" | "high" | "xhigh",
usage?: SessionUsage,
// Codex-only
codexSandboxMode?: "read-only" | "workspace-write" | "danger-full-access",
codexReasoningEffort?: "minimal" | "low" | "medium" | "high" | "xhigh",
codexWebSearchMode?: "disabled" | "cached" | "live",
codexWebSearchEnabled?: boolean,
codexAdditionalDirectories?: string[],
codexUsage?: CodexSessionUsage,
}
{ type: "agent.started", sessionId }
{ type: "user.message", sessionId, text, messageId?, attachments? }
{ type: "assistant.delta", sessionId, text }
{ type: "assistant.message", sessionId, text, messageId? }
{ type: "tool.started", sessionId, tool, input }
{ type: "tool.completed", sessionId, tool, output? }
{ type: "permission.requested", sessionId, permissionId, tool, description }
{ type: "agent.completed", sessionId }
{ type: "agent.cancelled", sessionId }
{ type: "agent.error", sessionId, message }
{ type: "agent.todo_list", sessionId, items: [{ text, status }] }
// Claude (session.usage)
{ totalCostUsd, inputTokens, outputTokens,
cacheReadInputTokens, cacheCreationInputTokens,
modelUsage: { [model]: { inputTokens, outputTokens,
cacheReadInputTokens, cacheCreationInputTokens, costUsd } } }
// Codex (session.codexUsage)
{ inputTokens, cachedInputTokens, cacheWriteInputTokens,
outputTokens, reasoningOutputTokens }
// OpenCode (queried live from the OpenCode server)
{ costUsd, inputTokens, outputTokens, reasoningTokens,
cacheReadTokens, cacheWriteTokens }
/health
Liveness check. The only route that doesn't live under /v1.
Response 200
{ status: "ok", version: "0.1.0" }
/v1/config
Returns the adapter's current configuration: which runtimes are enabled.
Response 200
{
claude: { enabled: boolean },
codex: { enabled: boolean },
opencode: { enabled: boolean },
}
/v1/config
Enables/disables one or more runtimes at once. Persisted in data/config.json.
Body (at least one key is required)
| Field | Type |
|---|---|
| claude | { enabled?: boolean } |
| codex | { enabled?: boolean } |
| opencode | { enabled?: boolean } |
Response 200
The full, already-updated configuration, same format as GET /v1/config.
/v1/agents
Lists the enabled runtimes and the models each one reports as available
(queried live via each runtime's listModels()).
Response 200
{
agents: [
{ runtime: "claude" | "codex" | "opencode",
models: [{ id: string, displayName: string, description?: string }] }
]
}
A runtime whose model query fails is simply omitted from the list (the error is just logged).
Valid for all three runtimes, unless otherwise noted on each route.
/v1/sessions
Creates a session record. Doesn't start the agent — that only happens when the first message is sent.
Body
| Field | Type | Required |
|---|---|---|
| runtime | "claude" | "codex" | "opencode" | yes |
| projectPath | string | yes |
Response 201
AgentSession object.
/v1/sessions
Lists known sessions, with status filtering and pagination.
Query params
| Field | Type | Default |
|---|---|---|
| status | ready | running | waiting_permission | completed | cancelled | error | (no filter) |
| limit | positive integer | 20 |
| offset | integer >= 0 | 0 |
Response 200
{ sessions: AgentSession[], total: number, limit: number, offset: number }
/v1/sessions/:sessionId
Retrieves the current state of a session (status, providerSessionId, settings, usage, etc).
Response 200
AgentSession object. 404 if it doesn't exist.
/v1/sessions/:sessionId
Renames the session, changing only the displayed title.
Body
| Field | Type | Required |
|---|---|---|
| title | string | yes |
Response 200
Updated AgentSession object.
/v1/sessions/:sessionId
Removes the local session record and, when the runtime supports it, also the conversation on the provider's side (Claude and OpenCode; Codex has no equivalent in its SDK).
Response 200
{ deleted: true }
409 if the session is running or waiting_permission.
/v1/sessions/:sessionId/history
Message history of the conversation, normalized to the AgentEvent format.
The source varies by runtime: Codex's local log, OpenCode's native API, or
getSessionMessages from the Claude Agent SDK.
Query params
| Field | Type |
|---|---|
| limit | positive integer |
| offset | integer >= 0 |
Response 200
{ events: AgentEvent[] }
404 if the session never exchanged messages with the agent.
/v1/sessions/:sessionId/usage
Accumulated token/cost usage for the session. Format depends on the runtime, see Data models.
Response 200
session.usage (Claude), session.codexUsage (Codex), or a
live query to the OpenCode server.
/v1/sessions/:sessionId/fork
Forks the conversation into a new, independent session, without affecting the original.
Body
| Field | Type | Required |
|---|---|---|
| upToMessageId | string | no (cuts the fork at this message) |
Response 201
New AgentSession, with forkedFrom pointing to the original session.
claude / opencode only
Codex has no fork operation in its SDK. 409 if the session doesn't have a conversation yet (no providerSessionId).
/v1/sessions/:sessionId/tag
Sets or clears a free-form tag on the session.
Body
| Field | Type | Required |
|---|---|---|
| tag | string | null | yes (null clears the tag) |
Response 200
Updated AgentSession object.
For Claude sessions that already have a conversation, the tag is also mirrored on the provider's side.
/v1/sessions/:sessionId/messages
asynchronous
Sends a message to the agent. Execution runs in the background; the HTTP response returns immediately and progress is tracked via SSE. Body limit of 32 MB (because of base64 attachments).
Body
| Field | Type | Required |
|---|---|---|
| content | string | yes |
| attachments | { kind: "image"|"document", mediaType, data (base64), filename? }[] | no |
Response 202
{ accepted: true }
Accepted media types: image jpeg, png, gif, webp; document pdf, text/plain.
Codex only accepts image attachments; OpenCode doesn't support attachments yet. 409 if the session is
already running or waiting_permission.
/v1/sessions/:sessionId/events
SSE
Server-Sent Events connection with the live events from the session's execution. Each
event arrives as event: <type> followed by data: <AgentEvent as JSON>.
The connection stays open until the client disconnects.
Possible events
See the AgentEvent union in Data models.
/v1/sessions/:sessionId/cancel
Cancels the ongoing execution. Only signals the cancellation; the agent.cancelled event confirms when it actually stops.
Response 200
{ cancelled: true }
409 if the session isn't running.
Same endpoint for all three runtimes, each with its own applicability rules.
/v1/sessions/:sessionId/permission-mode
Sets the session's permission mode, used as the default for the next executions.
Body
| Field | Type |
|---|---|
| mode | default | acceptEdits | bypassPermissions | plan | dontAsk | auto |
Response 200
{ mode, applied: "live" | "pending" }
applied: "live" when the session is already running and Claude can apply it right away.
For OpenCode only default (agent "build") and plan (agent "plan") are accepted.
not applicable to Codex
/v1/sessions/:sessionId/model
Sets the session's model. null resets to the CLI's default.
Body
| Field | Type |
|---|---|
| model | string | null |
Response 200
{ model, applied: "live" | "pending" }
Live application ("live") currently only exists for running Claude sessions.
/v1/sessions/:sessionId/rewind
Reverts file edits made starting from a specific user message.
Body
| Field | Type | Required |
|---|---|---|
| userMessageId | string | yes |
| dryRun | boolean | no, only supported on Claude |
Response 200
{ canRewind: boolean, filesChanged?: number,
insertions?: number, deletions?: number, error?: string }
claude / opencode only
Codex has no rewind system. 409 if the session doesn't have a conversation to revert yet.
/v1/sessions/:sessionId/permissions/:permissionId/approve
Approves a pending tool permission request (permission.requested event).
Response 200
{ approved: true }
not applicable to Codex
404 if the request has already been answered or never existed.
/v1/sessions/:sessionId/permissions/:permissionId/reject
Rejects a pending permission request.
Body
| Field | Type | Required |
|---|---|---|
| reason | string | no (default: "Rejected by user") |
Response 200
{ rejected: true }
not applicable to Codex
OpenCode doesn't accept a free-text reason; reason is accepted in the body but
isn't forwarded to the OpenCode server.
Routes exclusive to sessions with runtime: "claude". They respond with 400 for any other runtime.
/v1/sessions/:sessionId/claude-tools
Lists the tools that the Claude Code CLI reported as available for the project, from the cache keyed by projectPath.
Response 200
{ tools: string[] | null, updatedAt: string | null }
/v1/sessions/:sessionId/claude-tool-permissions
Configures the list of tools Claude can't use in this session.
Body
| Field | Type | Required |
|---|---|---|
| deny | string[] | yes (free-form names, no closed catalog) |
Response 200
{ deny: string[], applied: "pending" }
/v1/sessions/:sessionId/claude-effort-level
Configures the Claude model's reasoning effort for this session.
Body
| Field | Type |
|---|---|
| effort | low | medium | high | xhigh |
Response 200
{ effort, applied: "pending" }
Routes exclusive to sessions with runtime: "codex". They respond with 400 for any other runtime.
/v1/sessions/:sessionId/codex-sandbox-mode
Configures Codex's sandbox/filesystem permission mode for this session.
Body
| Field | Type |
|---|---|
| mode | read-only | workspace-write | danger-full-access |
Response 200
{ mode, applied: "pending" }
/v1/sessions/:sessionId/codex-reasoning-effort
Configures the Codex model's reasoning effort for this session.
Body
| Field | Type |
|---|---|
| effort | minimal | low | medium | high | xhigh |
Response 200
{ effort, applied: "pending" }
/v1/sessions/:sessionId/codex-web-search
Enables/configures Codex's web search for this session.
Body (at least one of the two is required)
| Field | Type |
|---|---|
| mode | disabled | cached | live |
| enabled | boolean |
Response 200
{ mode, enabled, applied: "pending" }
/v1/sessions/:sessionId/codex-additional-directories
Grants Codex access to extra directories outside the session's projectPath.
Body
| Field | Type | Required |
|---|---|---|
| directories | string[] (absolute paths, must exist) | yes |
Response 200
{ directories: string[], applied: "pending" }
400 if any path isn't absolute or doesn't exist as a directory.
OpenCode has no exclusive endpoints. It's treated as just another branch within the shared endpoints listed in Lifecycle, Execution & events and Shared controls, with the following particularities:
| Endpoint | OpenCode particularity |
|---|---|
| history / usage | Queried live from the OpenCode server, no local cache |
| messages | Doesn't accept attachments yet (400 if attachments is populated) |
| permission-mode | Only accepts default (agent "build") or plan (agent "plan") |
| rewind | No dryRun mode — the snapshot revert is always actually applied |
| permissions/approve & reject | Accepts reason in the body for consistency, but the OpenCode server only receives "approved" or "rejected" |