AgentCore_
PT Home GitHub

$ cat docs/api.md

API Documentation

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

$ cat conventions.md

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
400Invalid body/params, or the operation doesn't apply to the session's runtime
404Session, permission or history not found
409State conflict (session busy, no conversation to fork/rewind yet, etc.)
502Failed 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.

$ cat models.md

AgentSession

{
  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,
}

AgentEvent (format of each SSE message)

{ 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 }] }

Usage formats by runtime

// 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 }

$ ls core/health

GET /health

Liveness check. The only route that doesn't live under /v1.

Response 200

{ status: "ok", version: "0.1.0" }

$ ls core/config

GET /v1/config

Returns the adapter's current configuration: which runtimes are enabled.

Response 200

{
  claude: { enabled: boolean },
  codex: { enabled: boolean },
  opencode: { enabled: boolean },
}
PATCH /v1/config

Enables/disables one or more runtimes at once. Persisted in data/config.json.

Body (at least one key is required)

FieldType
claude{ enabled?: boolean }
codex{ enabled?: boolean }
opencode{ enabled?: boolean }

Response 200

The full, already-updated configuration, same format as GET /v1/config.

$ ls core/agents

GET /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).

$ ls sessions/lifecycle

Valid for all three runtimes, unless otherwise noted on each route.

POST /v1/sessions

Creates a session record. Doesn't start the agent — that only happens when the first message is sent.

Body

FieldTypeRequired
runtime"claude" | "codex" | "opencode"yes
projectPathstringyes

Response 201

AgentSession object.

GET /v1/sessions

Lists known sessions, with status filtering and pagination.

Query params

FieldTypeDefault
statusready | running | waiting_permission | completed | cancelled | error(no filter)
limitpositive integer20
offsetinteger >= 00

Response 200

{ sessions: AgentSession[], total: number, limit: number, offset: number }
GET /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.

PATCH /v1/sessions/:sessionId

Renames the session, changing only the displayed title.

Body

FieldTypeRequired
titlestringyes

Response 200

Updated AgentSession object.

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

GET /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

FieldType
limitpositive integer
offsetinteger >= 0

Response 200

{ events: AgentEvent[] }

404 if the session never exchanged messages with the agent.

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

POST /v1/sessions/:sessionId/fork

Forks the conversation into a new, independent session, without affecting the original.

Body

FieldTypeRequired
upToMessageIdstringno (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).

POST /v1/sessions/:sessionId/tag

Sets or clears a free-form tag on the session.

Body

FieldTypeRequired
tagstring | nullyes (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.

$ ls sessions/execution

POST /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

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

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

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

$ ls sessions/controls

Same endpoint for all three runtimes, each with its own applicability rules.

POST /v1/sessions/:sessionId/permission-mode

Sets the session's permission mode, used as the default for the next executions.

Body

FieldType
modedefault | 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

POST /v1/sessions/:sessionId/model

Sets the session's model. null resets to the CLI's default.

Body

FieldType
modelstring | null

Response 200

{ model, applied: "live" | "pending" }

Live application ("live") currently only exists for running Claude sessions.

POST /v1/sessions/:sessionId/rewind

Reverts file edits made starting from a specific user message.

Body

FieldTypeRequired
userMessageIdstringyes
dryRunbooleanno, 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.

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

POST /v1/sessions/:sessionId/permissions/:permissionId/reject

Rejects a pending permission request.

Body

FieldTypeRequired
reasonstringno (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.

$ ls sessions/claude

Routes exclusive to sessions with runtime: "claude". They respond with 400 for any other runtime.

GET /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 }
POST /v1/sessions/:sessionId/claude-tool-permissions

Configures the list of tools Claude can't use in this session.

Body

FieldTypeRequired
denystring[]yes (free-form names, no closed catalog)

Response 200

{ deny: string[], applied: "pending" }
POST /v1/sessions/:sessionId/claude-effort-level

Configures the Claude model's reasoning effort for this session.

Body

FieldType
effortlow | medium | high | xhigh

Response 200

{ effort, applied: "pending" }

$ ls sessions/codex

Routes exclusive to sessions with runtime: "codex". They respond with 400 for any other runtime.

POST /v1/sessions/:sessionId/codex-sandbox-mode

Configures Codex's sandbox/filesystem permission mode for this session.

Body

FieldType
moderead-only | workspace-write | danger-full-access

Response 200

{ mode, applied: "pending" }
POST /v1/sessions/:sessionId/codex-reasoning-effort

Configures the Codex model's reasoning effort for this session.

Body

FieldType
effortminimal | low | medium | high | xhigh

Response 200

{ effort, applied: "pending" }
POST /v1/sessions/:sessionId/codex-web-search

Enables/configures Codex's web search for this session.

Body (at least one of the two is required)

FieldType
modedisabled | cached | live
enabledboolean

Response 200

{ mode, enabled, applied: "pending" }
POST /v1/sessions/:sessionId/codex-additional-directories

Grants Codex access to extra directories outside the session's projectPath.

Body

FieldTypeRequired
directoriesstring[] (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.

$ ls sessions/opencode

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 / usageQueried live from the OpenCode server, no local cache
messagesDoesn't accept attachments yet (400 if attachments is populated)
permission-modeOnly accepts default (agent "build") or plan (agent "plan")
rewindNo dryRun mode — the snapshot revert is always actually applied
permissions/approve & rejectAccepts reason in the body for consistency, but the OpenCode server only receives "approved" or "rejected"