Skip to main content

Base URL

For self-hosted deployments, the base URL is http://localhost:8080. JSON endpoints return application/json. Errors use:

Tenancy

Most conductor routes are tenant-scoped by the caller’s ao_ bearer API key (Authorization: Bearer ao_...); the tenant is derived from the key, and the server strips any client-supplied X-Tenant-ID. Tenant admin routes are operator-only: they require Postgres, AGENT_ORC_ADMIN_TENANT, and a verified caller whose tenant matches that admin tenant. Non-admin tenants receive 404 Not Found for /api/tenants* routes so the surface is not enumerable. When POSTGRES_DSN or the POSTGRES_* component variables are configured, the conductor verifies that the connecting role is not a SUPERUSER and does not have BYPASSRLS. A privileged role bypasses the row-level security policies that scope tenanted reads by app.tenant_id, so the process fails closed at boot. Use a non-superuser NOBYPASSRLS application role; see Publishing an Agent for the role grants.

List Tenants

Query params: Returns { "total": 1, "tenants": [...] }.

Create Tenant

Get Tenant

Update Tenant

Delete Tenant

Soft-deletes the tenant. The default tenant cannot be deleted.

Profiles

List Profiles

Returns an array of profiles registered on the connected runner or runner pool. When limit is present, returns a page using limit and offset and includes X-Total-Count; without limit, returns the full legacy list.

Get Profile

Returns one stored profile by name. Unknown names return 404 Not Found.
Response 200 OK: the stored profile.

Create Profile

Creates a profile and broadcasts it to all runners. id is assigned when omitted. Profile list and get responses may include workerPlacement for sandbox-worker profiles when the Conductor can resolve the live runner fleet. It reports the resolved substrate, source, and servable state, with requested and detail when the requested placement cannot be honoured or the answer is ambiguous. The field is computed on read, ignored on create or update, and never persisted. See Worker Placement.
Response 201 Created: the stored profile.

Update Profile

Replaces an existing profile and broadcasts the new definition to all runners. The request body uses the same fields as create. Skill validation uses the same source as GET /api/skills: the tenant-scoped Postgres skill store when DB-backed skills are wired, otherwise the in-memory local catalog. Unknown skill names return 400 Bad Request. Response 200 OK: the stored profile.

Delete Profile

Deletes a profile from every runner. Existing sessions keep their snapshot.

Publishing Profiles

Publishing turns a conductor profile into a public chat-gateway route. The publish endpoints require Postgres. Key issuance also requires AGENT_API_KEY_PEPPER; plaintext API keys are returned exactly once and are stored only as HMAC hashes. When pay-first billing is enabled, publishing uses the same billing gate as run creation: the tenant must be registered with Polar and have prepaid credit remaining.

List Published Agents

Returns all active published agents in the current tenant:
publicUrl is present only when CHAT_GATEWAY_PUBLIC_HOST is set.

Get Published Agent

Returns the active published row for a profile, or 404 not_published.

Get Published Agent Metrics

Returns request, run, conversation, cost, and token metrics for the active published agent behind a profile. The response is scoped to one published agent; profiles that are not published return 404 not_published. Query params:
buckets uses the same shape as GET /api/stats/timeseries so dashboard charts can render the scoped view directly. sandboxSeconds and failedRuns are always 0 in this per-published-agent response because those values are not attributable to one published agent from usage_records.

Publish Profile

All fields are optional; omitted values default server-side.

Update Published Agent

Partially updates the active published row. slug is immutable; unpublish and publish again to change it. The patch also accepts enabled.

Unpublish Profile

Soft-deletes the active published row and returns 204 No Content.

List Agent Keys

Returns active and revoked key metadata for the active published row. The key hash and plaintext token are never returned.

Issue Agent Key

Returns 201 Created with key metadata plus one-time plaintext:

Revoke Agent Key

Revokes one key and returns 204 No Content.

Tenant API Keys

Tenant API keys are durable control-plane bearer credentials for SDKs, CI, and scripts calling /api/*. They are separate from published-agent keys under /api/profiles/{name}/keys: published-agent keys authenticate public chat-gateway traffic, while tenant API keys authenticate the conductor API directly. These routes require Postgres. Key issuance also requires AGENT_API_KEY_PEPPER; plaintext tokens are returned exactly once and are stored only as HMAC hashes. A key inherits the minter’s RBAC role, and route-level RBAC still applies.

List Tenant API Keys

Returns key metadata for the current tenant. Admins and owners see every key in the tenant; members see only keys they created. Tokens and hashes are never returned.

Issue Tenant API Key

Returns 201 Created with key metadata plus the one-time plaintext token:
Send the token as Authorization: Bearer <token> on future conductor API requests. The conductor only verifies these keys when it has Postgres and a decodable AGENT_API_KEY_PEPPER.

Revoke Tenant API Key

Revokes one key and returns 204 No Content. Members can revoke only their own keys; admins and owners can revoke any key in the tenant.

Agent Pools

List Pools

Returns an array of named agent pools. When limit is present, returns a page using limit and offset and includes X-Total-Count; without limit, returns the full legacy list.

Create Pool

Creates a pool and broadcasts it to all runners.
Response 201 Created: the stored pool with an assigned id.

Delete Pool


Sessions

List Sessions

Returns all live session records visible to the conductor. When limit is present, returns a page using limit and offset and includes X-Total-Count; paged requests can also pass q to filter by session ID or profile substring.
Session statuses are idle, running, errored, and shutdown.

Get Session

Returns one session DTO with the same shape as list entries.

List VFS Leases

Returns active per-session VirtualFS leases visible to the conductor. In pooled deployments the conductor fans out to runners and merges the results; if no VFS manager is wired the response is [].
An empty allowedMounts array means the VirtualFS session is unrestricted.

Get Session VFS Lease

Returns the VirtualFS lease for one session, or 404 with { "error": "no vfs session" } when no lease exists.

Delete Session

Terminates a session on its owning runner.

List Session Runs

Returns run summaries whose subTask.sessionId matches the session.

Runs

Create Run

Submits a SubTask. The conductor creates a session when sessionId is omitted and returns immediately. Unknown profiles return 400 with an unknown_profile: <name> error. Client-supplied id and sessionId values must be 1-128 characters and contain only letters, numbers, underscores, or hyphens. Invalid values return 400 with invalid run id or invalid session id. Leave either field empty or omit it when you want the conductor to mint a safe identifier. When sessionId is supplied in a Postgres-backed deployment, the conductor first reuses the live runner session if it is still in memory. If the runner registry no longer knows the session after a conductor, runner, or sidecar restart, the conductor hydrates it from the durable session row and any saved state bundle before dispatching the run. A missing or shut-down durable session returns 404 instead of creating a new session under the caller-supplied id. Hydration infrastructure failures, such as a database read failure or runner transport error, return 503 Service Unavailable so clients can retry instead of treating the session as gone. When pay-first billing is enabled, run creation is blocked before session or sandbox allocation unless the tenant is registered with Polar and has prepaid credit remaining. Billing failures return 402 Payment Required with a machine-readable error such as registration_pending, credits_exhausted, or billing_unavailable, plus checkout_path: "/api/billing/checkout".
Response 202 Accepted:

List Runs

Returns newest-first run summaries.
Run statuses are running, ok, error, cancelled, and interrupted. interrupted marks runs orphaned by a conductor restart and closed by the boot reconciliation sweep.

List Profile Runs

Returns run summaries whose subTask.profile matches the profile.

Get Run

Returns a run summary plus the buffered event log.

Cancel Run

Cooperatively cancels a running run and returns 204 No Content. The operation is idempotent: already-finished runs remain in their terminal status. An unknown run in the current conductor’s in-memory registry returns 404 Not Found.

Force-Terminate Run

Force-stops a run when cooperative cancellation does not complete. The conductor independently attempts to cancel its local run context, wait up to two seconds for the session’s run claim to drain, and change a still-running durable row to cancelled. If the claim does not drain, the conductor force-releases it. Once the durable row is cancelled, a late run completion cannot overwrite that status. The endpoint is idempotent and returns 200 OK even when the run is absent from the current conductor’s in-memory registry, allowing it to recover runs stranded by a restart. Inspect every result field and warnings rather than treating the status code alone as proof that all steps completed.

Stream Run Events

Opens a Server-Sent Events stream. Subscribers receive replayed buffered events first, then live events.
Event types are progress, session_init, assistant, tool_call, tool_result, usage, result, and error. The runtime session id is not on this SSE surface; read it from the raw GET /api/runs/{id}/events JSONL stream, which carries the underlying SDK/thread id per event, or from the session row’s metadata.

Sandbox Provider Events

Invalidate Sandbox Worker State

Receives provider notifications that a sandbox may have changed state. daytona is currently the registered provider decoder. The route is public and deliberately does not require Orca authentication: a delivery can only invalidate a cached worker-lease status so the next read asks the provider again. It cannot set state, terminate a session, or destroy a sandbox. Recognized sandbox events return 202 Accepted with the number queued for asynchronous invalidation:
Unknown providers, malformed or irrelevant events, and events without a sandbox ID also return 202 Accepted with { "ignored": true } so provider retries do not turn ignored deliveries into an outage. An unreadable or larger-than-64-KiB body returns 400 Bad Request. The public ingress has a dedicated per-client-IP tier configured by AGENT_ORC_RATELIMIT_SANDBOX_EVENTS_RPM and defaulting to 600 requests per minute; exceeding it returns 429 Too Many Requests. In conductor mode, accepted events are broadcast best-effort to every runner because provider sandbox IDs do not identify the owning runner. At most 32 invalidation fan-outs run concurrently. Further deliveries still receive 202, but their fan-out is shed and sandbox_event.fanout_shed records that cached liveness remains stale until the next provider probe.

Billing

Billing routes are admin-gated. In dev, when the conductor is not wired to the billing service, wallet and checkout calls return 503 Service Unavailable. When AGENT_ORC_ENV is any non-dev value, the conductor instead refuses to start without a valid billing client so runs cannot silently bypass the credit gate.

Get Billing Wallet

Returns the tenant’s live credit balance from Polar plus the purchasable top-up packs.

Create Billing Checkout

Creates a Polar hosted checkout URL for a one-time prepaid credit pack. The cents body field must match one of the packs returned by GET /api/billing/wallet.
Response 200 OK:

Workflow Runs

Workflow run snapshots currently encode status and node status as integer enums. Create/start responses expose string status labels.
Workflow run status mapping: 0=pending, 1=running, 2=paused, 3=completed, 4=failed, 5=cancelled. Node status mapping: 0=pending, 1=ready, 2=running, 3=ok, 4=error, 5=skipped, 6=cancelled.

Create Workflow Run

Creates a DAG workflow run. autoStart defaults to true.
Response 202 Accepted:

List Workflow Runs

Optional filters:

Get Workflow Run

Returns the full workflow run snapshot.

Start Workflow Run

Submits a pending workflow run to the engine. Idempotent for non-pending runs.

Cancel Workflow Run

Cancels pending and running nodes where possible.

Repair Workflow Run

Applies a repair action to a paused workflow run.
Supported action types are retry_node, replace_node, add_dependency, and abort.

Update Node Status

Used by orchestrator-driven execution to mark a pending node terminal.
Accepted statuses are ok, error, skipped, and cancelled.

Stream Workflow Run

SSE stream with a snapshot event followed by plan_status events. Each frame’s payload is keyed by workflowRun:

Create Workflow Definition

Creates a reusable workflow graph template. name and a non-empty nodes array are required.

List Workflow Definitions

Returns all in-memory workflow definitions.

Get Workflow Definition

Returns one workflow definition.

Update Workflow Definition

Updates supplied fields on an existing workflow definition.

Delete Workflow Definition

Deletes one workflow definition. Returns 204 No Content on success.

Create Workflow Schedule

Creates an active schedule for an existing workflow definition. workflowDefinitionId and cron are required.

List Workflow Schedules

Returns all in-memory workflow schedules.

Get Workflow Schedule

Returns one workflow schedule.

Pause Workflow Schedule

Marks a workflow schedule as paused.

Resume Workflow Schedule

Marks a workflow schedule as active.

Delete Workflow Schedule

Deletes one workflow schedule. Returns 204 No Content on success.

Skills Catalog

Skills are named instruction bodies that profiles can attach by name. Responses include source, which is user, imported, or platform, plus Agent Skills metadata such as license, compatibility, allowedTools, metadata, resources, and requiresSandbox. Platform skills are seeded using-* guidance entries tied to capability bundles and may be auto-attached from profile tools. Folder import is a staged flow. First upload exactly one skill folder containing SKILL.md; each multipart files part must use the package-relative filename, such as my-skill/SKILL.md or my-skill/scripts/run.sh. The dry-run response returns validation details, resource metadata (contentType, sha256, executable), requiresSandbox, totalBytes, and a short-lived stagingId. Commit that stagingId to register the package. Deleting a platform-seeded skill records a tombstone so later seed reconciliation does not restore that deleted using-* skill. Filesystem deployments store tombstones in platform_skills_state.json; Postgres deployments store them per tenant and reconcile platform skills lazily on the tenant’s first skills-list or run-dispatch request.

MCP Server Catalog

List Catalog Entries

Returns an array of catalog entries.

Get Catalog Entry

Create Catalog Entry

Update Catalog Entry

managedBy is server-owned. Both create and update ignore a client-supplied value; provider-managed entries are created only through the Connected Apps flow.

Delete Catalog Entry


Connected App Tool Facade

These tenant-scoped endpoints are available only for provider-managed toolkits that are active in the MCP catalog. They proxy discovery and execution through the internal MCP bridge so provider credentials do not enter profiles or worker run envelopes.

List Connected App Tools

Returns tool names, descriptions, and input schemas. Returns 403 Forbidden when the toolkit is not active for the tenant.

Call Connected App Tool

name is required. The response is the provider MCP tool result; bridge failures return 502 Bad Gateway.

Topology

Get Runner Topology

Available whenever the conductor has a runner pool, including a pool of one runner. Returns an array in registry order. In addition to point-in-time health and session data, each entry reports membership state:
  • live — eligible for new sessions
  • draining — existing sessions remain routable, but new sessions avoid the runner
  • unreachable — the last runner probe failed; new sessions avoid the runner
capabilitiesKnown is false until /runner/info has answered successfully; such a runner is not eligible for new sessions. A successful probe with an empty capabilities list retains the backward-compatible meaning “supports all runtimes.” Fields such as process, sessionsByRuntime, and sidecars are null when the runner cannot provide that detail. This tenant-facing response omits runner base URLs, sidecar endpoints, PIDs, and Node versions. Sidecar instanceId values contain only the distinguishing suffix of the underlying process identity; host prefixes are removed. Probe and run errors also redact the associated infrastructure address. The full shape remains available to operator tooling on the internal topology plane. The nested details are intentionally observation-based:
  • process is the runner’s Go-process footprint (allocBytes, sysBytes, heapInuseBytes, numGoroutine, numCPU, and uptimeSeconds).
  • sessionsByRuntime breaks down live sessions on that runner across runtimes.
  • sidecars lists runtime slots. A slot can be an in-process stub or a sidecar with state live, cold, degraded, or unreachable; cold means configured but quiet and is not a fault.
  • Each sidecar’s observedInstances is a lower bound, not a replica count. Connection pooling can hide idle replicas, and slots sharing a sidecar process can report the same process.
  • Instance and process statistics can be null until a health probe reaches that process. Null means not measured and must not be rendered as zero.
The topology fan-out is cached for 30 seconds and shared across concurrent callers. When per-tenant rate limiting is enabled, GET /api/topology also has its own tier, configurable with AGENT_ORC_RATELIMIT_TOPOLOGY_RPM and defaulting to 30 requests per minute. The dashboard’s Runtime page uses this endpoint with a manual Refresh action. It does not poll; a read can trigger a probe when the shared cache has expired.

Manage Runner Membership (Internal)

These machine-facing routes are served on the conductor’s /internal/ service plane, not on the tenant-scoped /api/* surface. They require the internal signing-plane authentication described under Resolve Secret.
Re-probes the supplied runner URLs and adds or refreshes members. The body is optional; an empty body re-reads RUNNER_URLS (or the legacy RUNNER_URL) from the conductor environment. If a URL was previously represented only by an unreachable placeholder, a successful probe replaces that placeholder with the runner’s advertised hash rather than leaving a duplicate topology entry. Reconcile is otherwise additive: URLs no longer listed are not removed because they may still own live sessions.
These routes accept { "hash": "a1b2c3d4" }. Drain stops new sessions while keeping existing sessions routable; undrain returns a member to service when its last probe permits it. Remove deletes the member from this registry and can make its remaining sessions unroutable, so drain first and wait for its session count to reach zero. Membership is per conductor process. In a horizontally replicated conductor, each replica must reconcile or receive the lifecycle action; these routes do not provide fleet-wide membership storage.

Stats

All stats endpoints accept window, a Go duration string such as 5m, 1h, 24h, or 168h. The window is capped at 30 days. Agent sort values: last_activity_desc, tokens_desc, failures_desc, runs_desc, sessions_desc, name_asc.

Stats Timeseries

Returns buckets on one shared time grid. Each bucket includes run counts, token usage, costCents, sandboxSeconds, and ingressRequests. costCents, sandboxSeconds, and ingressRequests are zero when the usage store is not configured. Sandbox seconds are attributed by the sandbox meter row’s recorded_at timestamp, so a bucket contains sessions whose sandbox meter last refreshed inside that bucket rather than exact in-bucket compute.

Usage Meters

Returns the tool-call, sandbox-compute, and published-agent ingress meter totals recorded in usage_records. window uses the same values and 30-day cap as the stats endpoints. totals is always the tenant-wide rollup for the window. Query params:

Storage

Storage is backed by the optional S3-compatible artifact client configured from AWS_* / S3_* environment variables.

Storage Info

Always returns 200 OK. When storage is not configured, configured is false.

List Objects

Query params:

Get Object

Returns inline object content. key may contain multiple path segments. Non-UTF-8 content is base64 encoded.

Upload Object

Stores the raw request body at key. The Content-Type header is preserved. Inline payloads are capped at 8 MiB; oversize requests return 413 Payload Too Large.

Delete Object

Removes one object. When key ends with /, the path is treated as a folder prefix and every object under it is deleted (capped at 1000 entries — one List page).

Secrets

Secrets are tenant-scoped and envelope-encrypted. These routes return 503 Service Unavailable unless both Postgres and AGENT_ORC_MASTER_KEY are configured. Plaintext is required on writes and is never returned by metadata endpoints.

List Secrets

Returns metadata only:

Create Secret

key is optional and names the canonical environment variable or credential slot the value is meant to populate.

Update Secret

Uses the same body as create. If name is present in the body, it must match the path.

Delete Secret

Deletes the secret metadata and ciphertext.

Resolve Secret

Internal-plane endpoint for sibling services, such as VirtualFS, that need to dereference a secret://<name> ref into plaintext under the same tenant boundary. The route is served on the conductor’s INTERNAL_PORT listener under the /internal/ sub-mux and is not exposed on the public /api/* surface. Authentication: chatsig HMAC using the VFS_INTERNAL_SIGNING_KEY_CURRENT key family (distinct from the chat-gateway key family). Required request headers:
  • X-Chat-Gateway-Signature
  • X-Chat-Gateway-Timestamp
  • X-Chat-Gateway-Nonce
  • X-Chat-Gateway-Key-ID (e.g. CURRENT)
  • X-Chat-Gateway-Tenant (the tenant the resolution is scoped to; stamped onto the request context by the verifier)
Request body:
Response:
Literal values and ${ENV_VAR} are rejected; ref must start with secret://. A signed request whose X-Chat-Gateway-Tenant does not own the named secret returns 404 (non-enumerating). Missing, malformed, or wrong-key-family signatures return 401.

VirtualFS Proxy

When VFS_BASE_URL is set on the conductor, it mounts a reverse proxy at /api/vfs/ for the standalone VirtualFS server. The proxy strips the /api prefix before forwarding, so GET /api/vfs/mounts reaches upstream GET /vfs/mounts. GET /api/vfs/leases is the conductor’s runtime lease endpoint documented in Sessions, not a proxied standalone VirtualFS route. Upstream connection failures return 502 Bad Gateway with vfs upstream unavailable.

Internal Chat-Gateway Listener

When INTERNAL_PORT is set, the conductor also starts a private listener for the public chat gateway. The listener serves only: All /api/* requests on this listener must carry the signed chat-gateway headers. The verified X-Chat-Gateway-Tenant drives tenant scoping; inbound X-Tenant-ID is ignored on this private surface.

Memory Bank

Per-profile long-lived memory plus a global admin view. See Memory Bank for the data model, ranking, and prompt injection. All endpoints return 503 Service Unavailable when the bank is not wired.

List Profile Memories

Query: limit (default 100), offset (default 0).

Create Profile Memory

Body fields (MemoryCreateRequest): Either rawInput or processedContent is required. Returns 201 Created with the stored memory. Unknown profile returns 404.

Search Profile Memories

Query: q (search string, empty matches all), limit (default 8), minScore (default 0.05).

Get Profile Memory

Delete Profile Memory

Get Memory Bank

Global admin view used by the dashboard’s Memory Bank page. Without limit, returns the legacy grouped response. With limit, returns a flattened page using limit and offset; q searches memory content, summary, profile name, or ID, and category filters by memory category. Paged responses include total, profilesWithMemory, limit, offset, and items.

Get Memory Bank Stats


Health & Metrics

Health Check

Returns text/plain ok.

Prometheus Metrics

Returns Prometheus text format metrics when the process metrics handler is mounted.