Base URL
http://localhost:8080.
JSON endpoints return application/json. Errors use:
Tenancy
Authenticate with your tenant API key usingAuthorization: Bearer ao_.... The tenant is derived from the credential; client-supplied X-Tenant-ID headers are ignored. Access is limited by your role. See Roles & Access.
Who Am I
Returns the caller’s resolved identity:tenantId, role, and authKind (api_key, run_token, or session).
Caller Preferences
Profiles
List Profiles
Returns a page of profiles registered on the connected runner or runner pool.limit defaults to 200 when omitted; the response always includes
X-Total-Count. Pass limit/offset to page explicitly.
Get Profile
Returns one stored profile by name. Unknown names return404 Not Found.
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.
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. Like create,PUT /api/profiles/{name} does not validate skill names against any skill store: an unknown or not-yet-installed skill name is accepted, and readiness reporting (not a 400) is how the caller learns a skill dependency is missing.
Response 200 OK: the stored profile.
Delete Profile
Deletes a profile from every runner. Existing sessions keep their snapshot.Pin Profile
Unpin Profile
List Profile Changes
Returns the profile’s change history.Get Profile Usage
Returns usage totals for one profile.Attach Skill
Attaches a skill to a profile by name.Detach Skill
Removes a skill from a profile.Seed Orca Profile
Idempotently (re-)creates the canonicalsonar helper profile. Used by the dashboard’s Quickstart “Restore Orca” action; the route keeps the orca name because it is the platform’s. Returns { "profileName": "sonar", "created": <bool> }.
Publishing Profiles
Publishing turns a conductor profile into a public chat-gateway route. The publish endpoints require Postgres. Key issuance also requiresAGENT_API_KEY_PEPPER; plaintext API keys are returned exactly once and are stored only as HMAC hashes.
Publishing has no billing or credit check. Only run creation and plan autostart/execution are gated by the credit gate; see Create Run and Billing.
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, or404 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 return404 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 and monthlyCostCapUsdCents (integer, must be >= 0; monthly spend cap for this published agent in USD cents).
Unpublish Profile
Soft-deletes the active published row and returns204 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
201 Created with key metadata plus one-time plaintext:
Revoke Agent Key
Revokes one key and returns204 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
201 Created with key metadata plus the one-time plaintext token:
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 returns204 No Content. Members can revoke only their own keys; admins and owners can revoke any key in the tenant.
Tenants
Operator-only control-plane CRUD for tenant records, restricted to the single tenant named by the conductor’sAGENT_ORC_ADMIN_TENANT operator configuration; every other caller, including an admin of any other tenant, gets 404 Not Found. Returns 503 Service Unavailable when no database is wired. Tenant IDs are slugs matching the X-Tenant-ID middleware format.
Pods
List Pods
Returns a page of named agent pods.limit defaults to 200 when omitted; the
response always includes X-Total-Count. Pass limit/offset to page
explicitly.
Get Pod
Returns one stored pod by name.Create Pod
Creates a pod and broadcasts it to all runners.201 Created: the stored pod with an assigned id.
Update Pod
Updates supplied fields on an existing pod and broadcasts the new definition to all runners.Pin Pod
Unpin Pod
Add Pod Member
Adds a profile to a pod.Remove Pod Member
Removes a profile from a pod.Delete Pod
Sessions
List Sessions
Returns a paginated page of session records visible to the conductor.limit
defaults to 200 when omitted; the response always includes X-Total-Count.
Supports q (substring match on session ID or profile), profile (exact
profile-name filter), and group (filter by rail group, applied after
pagination).
idle, running, errored, and shutdown.
Create Session
Mints a runtime session bound to the named profile, used by the chat gateway’s first-turn path so a session id exists before dispatching the run.201 Created:
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[].
allowedMounts array means the VirtualFS session is unrestricted.
Get Session VFS Lease
Returns the VirtualFS lease for one session, or404 with { "error": "no vfs session" } when no lease exists.
Delete Session
Terminates a session on its owning runner.List Session Runs
Returns run summaries whosesubTask.sessionId matches the session.
Get Session Rail
Returns the session rail’s grouped view in one call, so the dashboard rail renders from one request instead of several filtered session queries.Runs
Create Run
Submits aSubTask. 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 the billing client is wired, run creation is blocked before session or sandbox allocation if the tenant’s wallet balance is exhausted or the credit check itself fails. The response is 402 Payment Required with { "error": "credits_exhausted" | "credit_check_unavailable", "scope": "tenant", "balance_micro_usd": <number>, "message": "..." }.
202 Accepted:
409 Conflict: returned when the profile’s dependencies are not
ready, such as a connected app, secret, or catalog:// skill reference that is
missing or not yet installed:
List Runs
Returns newest-first run summaries.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 whosesubTask.profile matches the profile.
Get Run
Returns a run summary plus the buffered event log.Cancel Run
Cooperatively cancels a running run and returns204 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 tocancelled. 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.progress, session_init, assistant, tool_call, tool_result, usage, result, error, thinking, capability_span, capability_decision, and capability_context_savings. 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.
Billing
Billing routes are not uniformly admin-gated:GET /api/billing/wallet requires member role or higher, while POST /api/billing/checkout requires owner role. 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. Thecents body field must match one of the packs returned by GET /api/billing/wallet.
200 OK:
Get Spend Cap
Reads the tenant’s monthly spend cap.Update Spend Cap
Sets the tenant’s monthly spend cap. Requires admin role or higher. The enforcing gate lives in run creation.Billing Webhooks
POST /api/clerk/webhook and POST /api/polar/webhook are signature-verified
webhook targets (Svix and Standard Webhooks HMAC respectively, checked inside
the handler), not endpoints an SDK or CLI calls directly.
Notifications
Send Notification Email
Sends a notification email, used by the@notify email_me tool.
Get Notify Settings
Update Notify Settings
Requires admin role or higher.Workflow Runs
Workflow run snapshots currently encode
status and node status as integer enums. Create/start responses expose string status labels.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.
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.retry_node, replace_node, add_dependency, and abort.
Update Node Status
Reports one node’s outcome and drives orchestrator-controlled transitions.status must be one of ok, error, skipped, or cancelled.
202 Accepted with the updated workflow run snapshot. Unknown run or node returns 404 Not Found; an invalid status or a node not in a pending state returns 400 Bad Request.
Stream Workflow Run
SSE stream with asnapshot 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 a page of the tenant’s workflow definitions.limit/offset query params page the list, defaulting to and capped at 200; the total count is returned in X-Total-Count.
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. Returns204 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 a page of the tenant’s workflow schedules.limit/offset query params page the list, defaulting to and capped at 200; the total count is returned in X-Total-Count.
Get Workflow Schedule
Returns one workflow schedule.Pause Workflow Schedule
Marks a workflow schedule aspaused.
Resume Workflow Schedule
Marks a workflow schedule asactive.
Delete Workflow Schedule
Deletes one workflow schedule. Returns204 No Content on success.
Automations
Agents and pods on a schedule. Requires Postgres; every route returns503 Service Unavailable when POSTGRES_DSN is not configured. See Automations for the domain model, lifecycle, and how a firing relates to an ordinary run.
/api/schedules/* is a product-facing alias over the same handlers as /api/automations/*, not a second implementation. GET, POST, GET /{id}, PUT /{id}, DELETE /{id}, POST /{id}/run, POST /{id}/start, and POST /{id}/stop share handlers under both prefixes. POST /api/schedules/parse exists only under /api/schedules; GET /api/automations/firings, POST /api/automations/{id}/pause (a deprecated alias for stop), and GET/PUT /api/automations/{id}/memory exist only under /api/automations.List Automations
Returns a page of automations.limit defaults to 200 when omitted; the response always includes X-Total-Count. Query params: q (free-text filter), includeDeleted=true (also page over soft-deleted rows).
Create Automation
Created active, on the schedule it was given. Returns
201 Created with the stored automation, or 400 Bad Request when the cron expression, timezone, or window is invalid, or would never fire.
Get Automation
Returns one automation, including soft-deleted rows.Update Automation
Same body as create, minusname (an automation cannot be renamed). Editing an active automation recomputes its next run; a change that would leave the window already closed stops the automation instead. Returns 409 Conflict when the automation is disabled.
Delete Automation
Soft-deletes the automation once its worker, delegated children, and automation-agent update have all finished. Firings and sessions remain as read-only history.Start Automation
Sets the automation toactive and computes its next run from now (no catch-up for time spent stopped). Also clears its consecutive-failure count. Returns 409 Conflict when the automation’s end date has already passed.
Stop Automation
Sets the automation tostopped and clears nextRunAt. Starting again computes a fresh next run rather than resuming a suspended countdown.
Run Automation Now
Fires one slot immediately, through the same code path a scheduled firing takes. Works on a stopped automation without starting it, so credentials and target can be tested before switching the schedule on. Does not touchnextRunAt. Returns 202 Accepted with the firing.
List Automation Firings
Query params:automation (id, scopes to one automation’s history), outcome (ok, failed, or skipped), limit, offset. Serves both the global history view and a single automation’s runs tab.
Get Automation Memory
Returns the short note the automation’s own automation-agent keeps between firings:body with no updatedAt.
Set Automation Memory
Body:{ "body": "..." }, capped at 8KB. An empty body clears the note. Overwrites what the automation agent would otherwise keep unsupervised.
Parse Schedule Text
Turns a plain-language cadence into a cron expression, for the dashboard’s schedule popover.422 Unprocessable Entity when the text cannot be parsed as a cadence.
Skills Catalog
Skills are named instruction bodies that profiles can attach by name. Responses includesource, 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.
Templates and Kits
A template is a snapshot of a pod, a single agent, or an automation (with whatever it runs) into a copyable record: skills, sanitized profiles, and optionally a pod and its schedules. A kit is the same record addressed by a public share id (kit-...) instead of a slug, which is how it stays reachable after a rename. See Shared kits for the dashboard’s browse and install flow, and Prompts for the profile side of what the record carries. The API routes keep the older templates spelling; the product calls the thing a kit.
Env values, literal credential-shaped MCP header values, and computed or row-identity fields are stripped at snapshot time regardless of what the manifest shows, so a template payload never carries a secret.
GET /api/public/templates/{slug}, GET /api/public/kits/{id}, and POST /api/public/kits/{id}/events are public: no session or API key is required. They serve the sanitized share-landing read (and its view/click log) that a visitor with no workspace follows from a shared link. GET /api/templates/{slug}, GET /api/kits/{id}, and POST /api/kits/{id}/events are the same reads and the same event log for a signed-in caller, who is then stamped with their own workspace. Everything else on this page requires at least member role; POST, PATCH, and DELETE /api/templates (create, edit metadata, delete) and both republish routes require admin role or higher.Preview Template
Dry-run manifest of what a snapshot would contain, before creating it. Names exactly one ofpool, agent, or automation; schedules names automations to attach (valid only alongside pool or agent).
stripped list documenting what the sanitizer removed. Automations installed by a kit always arrive stopped, with no prompts attached; the manifest says so in each automation’s detail line.
List Templates
Lists the caller’s own templates by default.?scope=saved lists templates bookmarked from other workspaces’ share links; ?scope=copied lists templates this workspace has installed. Each row carries authorHandle and authorName (the author’s public identity, never their tenant id) and, on the own-templates list, view/copy/save counts when available.
Create Template
Requires admin role or higher: publishing a template shares its content with anyone who has the link.
Returns
201 Created with the stored template (a fresh id and public kit-... id), or 409 Conflict when the slug is already taken in your workspace.
Get Template
Readable by any authenticated workspace, which is what makes the link shareable. Accepts?org={handle} to disambiguate a slug two organizations both publish, and ?id={id} to read an exact saved snapshot. Returns 409 Conflict when the slug is ambiguous without ?org=, and 410 Gone for a template its owner deleted (unless the caller has it saved).
Update Template
Requires admin role or higher. Author-only edit oflabel and description; payload changes go through republish so they always pass the snapshot sanitizer.
Delete Template
Requires admin role or higher, author-only. Returns204 No Content. A platform-seeded built-in kit cannot be deleted.
Get Template Copy Plan
Computes what copying now would do, asset by asset:new, changed (differs from what you installed before), or unchanged, plus a suggested target name when the caller already has something with that name.
Copy Template
Instantiates the snapshot into the caller’s tenant: skills first, then profiles, then the pod, then any included workspace files. Idempotent on the pod or agent name: an existing same-named asset means “already copied” and is never overwritten. With no body, copies everything. With a body namingassets (from the copy plan), copies and renames only the selected assets:
200 OK with { "copied": false, "reason": "already_exists", ... } when nothing new was installed, or 200 OK with { "copied": true, ... } naming what was added. Any automation the kit carries installs stopped. Returns 424 Failed Dependency if a skill fails to install.
Republish Template
Requires admin role or higher. Re-snapshots one of the caller’s own live sources through the same sanitizer and size checks as create, and advances the template’s version. Same body as create.Republish Saved Template
Requires admin role or higher. Forks the exact frozen snapshot a caller saved into a new template and link they own, under a newslug and label (query param id selects which saved snapshot). Returns 403 Forbidden for a template the caller has not saved, or one they authored themselves. Returns 409 Conflict when the caller’s workspace has no public org handle.
Save Template
Bookmarks someone else’s template without copying it. Returns400 Bad Request for the caller’s own template.
Unsave Template
Removes the bookmark. Returns204 No Content, or 404 Not Found if it was not saved.
Get Kit
The same read as Get Template, addressed by public kit id (kit-..., or a built-in kit’s name) instead of slug. Mine, save state, and counts follow the same rules.
Record Kit Event
Logs a view of, or a click on, a kit’s page, for the author’s own counts. Best effort: returns204 No Content even when the event log itself is unavailable, so a page view never fails on this call.
Capabilities
A bounded capability facade for tenants using the capability router.discover and run require an active run-token context (they operate on behalf of the run that calls them).
Capability Bundles
Lists capability bundles available to attach to a profile.MCP Server Catalog
List Catalog Entries
Returns a page of catalog entries.limit/offset query params page the list, defaulting to and capped at 200; the total count is returned in X-Total-Count.
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
Test Catalog Entry
Body is an MCP server spec, the same shape as create/update. The server validates it and performs a dry-run connection attempt (5 second timeout) using the tenant’s resolved secrets, returning the connection test result.Connected Apps
These tenant-scoped endpoints manage provider-managed app connections and, once connected, proxy tool discovery and execution through the internal MCP bridge so provider credentials do not enter profiles or worker run envelopes.Connection Management
The tool discovery and execution endpoints below are available only for provider-managed toolkits that are active in the MCP catalog.
List Connected App Tools
Returns tool names, descriptions, and input schemas. Returns403 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 sessionsdraining: existing sessions remain routable, but new sessions avoid the runnerunreachable: the last runner probe failed; new sessions avoid the runner
capabilitiesKnown is false until the runner has reported its capabilities;
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:
processis the runner’s Go-process footprint (allocBytes,sysBytes,heapInuseBytes,numGoroutine,numCPU, anduptimeSeconds).sessionsByRuntimebreaks down live sessions on that runner across runtimes.sidecarslists runtime slots. A slot can be an in-processstubor asidecarwith statelive,cold,degraded, orunreachable;coldmeans configured but quiet and is not a fault.- Each sidecar’s
observedInstancesis 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
nulluntil a health probe reaches that process. Null means not measured and must not be rendered as zero.
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 does not use this endpoint. It shows the workspace’s own sandbox worker leases, polled every 10 seconds. GET /api/topology remains available as a published SDK surface.
Stats
All stats endpoints acceptwindow, 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 inusage_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:
Admin Pricing
Requires admin role. Returns the compiled-in price floor plus live reconciler rates.Storage
Storage is backed by the optional S3-compatible artifact client configured fromAWS_* / S3_* environment variables.
Storage Info
Always returns200 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 atkey. 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. Whenkey 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 return503 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. Ifname is present in the body, it must match the path.
Delete Secret
Deletes the secret metadata and ciphertext.Context Documents
Context documents are human-authored markdown a run can be seeded with. Do not store secrets here; use Secrets instead.VirtualFS Proxy
WhenVFS_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.
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 return503 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. Withoutlimit,
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
Contact
Submit Contact Enquiry
Public, unauthenticated contact-form submission endpoint. Sends the enquiry and a branded confirmation email to the submitter.Device Authorization
The RFC 8628 device-authorization flow behindorca login and other headless CLI or agent sign-ins. See Headless authentication and Authentication and contexts for the end-to-end flow; this section documents only the wire format.
All routes return 503 Service Unavailable when the flow is not configured (Postgres, the tenant key pepper, and AGENT_ORC_DASHBOARD_URL are all required).
POST /api/device/code and POST /api/device/token are unauthenticated: the caller has no credential yet, which is the point. GET /api/device/authorization, POST /api/device/approve, and POST /api/device/deny require a signed-in session (member role or higher) in the browser tab that is completing the approval.Request Device Code
client_label is optional, free text up to 100 characters, shown on the dashboard’s approval screen and stored as the name of the key it eventually mints.
device_code is the 256-bit secret the CLI polls with; it is never shown to a human. user_code is the 8-character code (from a 20-character no-vowel, no-lookalike alphabet) a person types or confirms in the browser. The authorization expires in expires_in seconds (900, 15 minutes) if nobody approves or denies it.
Poll For Token
grant_type is optional (its absence is tolerated); when present it must be exactly urn:ietf:params:oauth:grant-type:device_code. Poll no more than once every interval seconds (5).
On success, 200 OK:
access_token is minted only at this point, inside the same transaction that consumes the authorization, so exactly one poll ever receives it. Every other outcome is 400 Bad Request with a bare { "error": "<code>" } body, deliberately not the platform’s usual error shape, so a poll loop can branch on the standard RFC 8628 code names:
expired_token deliberately covers unknown and already-consumed codes as well as truly expired ones, so this endpoint is not an existence oracle for device_code values.
Look Up Device Authorization
Powers the dashboard’s confirm screen:?user_code=BCDF-GHJK. Returns 404 Not Found for an unknown, expired, or already-decided code.
Approve Device Authorization
POST /api/api-keys would. Returns 204 No Content, or 404 Not Found for an unknown or already-decided code.
Deny Device Authorization
204 No Content. Denial is terminal: the next poll reports access_denied once, and any poll after that reports expired_token.
Health & Metrics
Health Check
Returns text/plainok. A liveness probe: it does not check dependencies.
Readiness Check
Readiness probe, distinct from/healthz’s liveness check.
Prometheus Metrics
Returns Prometheus text format metrics when the process metrics handler is mounted. The endpoint is gated behind a static bearer token: setMETRICS_AUTH_TOKEN and send Authorization: Bearer <METRICS_AUTH_TOKEN>. The gate fails closed, so with no token configured every request, including unauthenticated ones, returns 401, and a missing or incorrect token also returns 401.