API Reference
Archon exposes a REST API via a Hono server with OpenAPI spec generation. All endpoints are prefixed with /api/.
Base URL
Section titled “Base URL”By default, the API server runs at:
http://localhost:3090/api/Override the port with the PORT environment variable or let Archon auto-allocate when running inside a worktree (range 3190-4089).
OpenAPI Specification
Section titled “OpenAPI Specification”A machine-readable OpenAPI 3.0 spec is available at:
GET /api/openapi.jsonYou can feed this into tools like Swagger UI or use it to generate typed API clients.
Authentication
Section titled “Authentication”None. Archon is a single-developer tool — there is no authentication on the API by default. If you expose Archon on a network, use a reverse proxy or firewall to restrict access.
Health
Section titled “Health”| Method | Path | Description |
|---|---|---|
| GET | /health | Basic health check |
| GET | /api/health | API-level health check |
curl http://localhost:3090/health# {"status":"ok"}
curl http://localhost:3090/api/health# {"status":"ok","adapter":"...","concurrency":{...},"runningWorkflows":0}Conversations
Section titled “Conversations”| Method | Path | Description |
|---|---|---|
| GET | /api/conversations | List conversations |
| GET | /api/conversations/{id} | Get a single conversation |
| POST | /api/conversations | Create a new conversation |
| PATCH | /api/conversations/{id} | Update a conversation (rename) |
| DELETE | /api/conversations/{id} | Soft-delete a conversation |
| GET | /api/conversations/{id}/messages | List messages in a conversation |
| POST | /api/conversations/{id}/message | Send a message to a conversation |
List Conversations
Section titled “List Conversations”curl http://localhost:3090/api/conversationsQuery parameters:
codebase_id(optional) — Filter by codebaseinclude_deleted(optional) — Include soft-deleted conversations
Create a Conversation
Section titled “Create a Conversation”curl -X POST http://localhost:3090/api/conversations \ -H "Content-Type: application/json" \ -d '{}'Optionally specify a codebase:
curl -X POST http://localhost:3090/api/conversations \ -H "Content-Type: application/json" \ -d '{"codebase_id": "your-codebase-id"}'Returns the created conversation with its platform_conversation_id.
Send a Message
Section titled “Send a Message”curl -X POST http://localhost:3090/api/conversations/{id}/message \ -H "Content-Type: application/json" \ -d '{"message": "What does this codebase do?"}'The message is dispatched to the orchestrator asynchronously. The response confirms dispatch — actual AI responses arrive via SSE streaming or can be polled via the messages endpoint.
Get Messages
Section titled “Get Messages”curl http://localhost:3090/api/conversations/{id}/messagesQuery parameters:
limit(optional) — Number of messages to returnbefore(optional) — Cursor for pagination
Update a Conversation
Section titled “Update a Conversation”curl -X PATCH http://localhost:3090/api/conversations/{id} \ -H "Content-Type: application/json" \ -d '{"title": "My feature discussion"}'Delete a Conversation
Section titled “Delete a Conversation”curl -X DELETE http://localhost:3090/api/conversations/{id}Performs a soft delete — the conversation is hidden but not destroyed.
Codebases
Section titled “Codebases”| Method | Path | Description |
|---|---|---|
| GET | /api/codebases | List registered codebases |
| GET | /api/codebases/{id} | Get a single codebase |
| POST | /api/codebases | Register a codebase (clone or local path) |
| DELETE | /api/codebases/{id} | Delete a codebase and clean up resources |
| GET | /api/codebases/{id}/environments | List isolation environments for a codebase |
List Codebases
Section titled “List Codebases”curl http://localhost:3090/api/codebasesRegister a Codebase
Section titled “Register a Codebase”Clone from a URL:
curl -X POST http://localhost:3090/api/codebases \ -H "Content-Type: application/json" \ -d '{"url": "https://github.com/user/repo"}'Register a local path:
curl -X POST http://localhost:3090/api/codebases \ -H "Content-Type: application/json" \ -d '{"path": "/home/user/projects/my-repo"}'Delete a Codebase
Section titled “Delete a Codebase”curl -X DELETE http://localhost:3090/api/codebases/{id}Removes the codebase registration and cleans up associated worktrees and isolation environments.
List Environments
Section titled “List Environments”curl http://localhost:3090/api/codebases/{id}/environmentsReturns the isolation environments (worktrees) associated with a codebase.
Workflows
Section titled “Workflows”Definitions
Section titled “Definitions”| Method | Path | Description |
|---|---|---|
| GET | /api/workflows | List available workflows |
| GET | /api/workflows/{name} | Get a single workflow definition |
| POST | /api/workflows/validate | Validate a workflow definition (in-memory, no save) |
| PUT | /api/workflows/{name} | Save (create or update) a workflow |
| DELETE | /api/workflows/{name} | Delete a user-defined workflow |
List Workflows
Section titled “List Workflows”curl http://localhost:3090/api/workflowsQuery parameters:
cwd(optional) — Working directory to discover project-specific workflows
When cwd is omitted, Archon returns bundled default workflows and any from ~/.archon/workflows/ (home-scoped). Project-specific workflows require either the cwd query param or a registered codebase, so the endpoint is useful on first launch before any project is registered.
Returns { workflows: [...], recommended: [...], errors?: [...] }.
workflows[]— each entry is{ workflow, source, parseWarnings? }.parseWarningscontains warning messages identifying the keys the engine silently dropped from that workflow’s YAML, each with the node it was found on and what to write instead (see Unknown keys); it is omitted entirely when the workflow is clean, so its presence alone is the signal.recommended[]— repo-owner-curated workflow names from.archon/config.yaml, filtered to discovered names and kept in declared order. Empty when there is no project context.errors[]— YAML parsing failures encountered during discovery. UnlikeparseWarnings, these workflows did not load.
Get a Workflow
Section titled “Get a Workflow”curl http://localhost:3090/api/workflows/archon-assistQuery parameters:
cwd(optional) — Working directory for project-specific lookup
Returns { workflow, filename, source: "project" | "global" | "bundled" }. The endpoint auto-discovers across all three scopes in order (project → home-scoped → bundled). source: "global" is returned when the workflow comes from ~/.archon/workflows/.
Validate a Workflow
Section titled “Validate a Workflow”curl -X POST http://localhost:3090/api/workflows/validate \ -H "Content-Type: application/json" \ -d '{"definition": {"name": "my-wf", "description": "Test", "nodes": [{"id": "a", "prompt": "hello"}]}}'Returns { valid: true } or { valid: false, errors: ["..."] }. Does not save anything.
Save a Workflow
Section titled “Save a Workflow”curl -X PUT http://localhost:3090/api/workflows/my-workflow \ -H "Content-Type: application/json" \ -d '{"definition": {"name": "my-workflow", "description": "My custom workflow", "nodes": [{"id": "plan", "prompt": "Plan the feature"}]}}'Query parameters:
cwd(optional) — Target directory (must have.archon/workflows/)source(optional, enum:project|global) — Scope to write the workflow to. Defaults toproject(writes to<cwd>/.archon/workflows/). Passsource=globalto write to the home-scoped location (~/.archon/workflows/). Returns400 "Invalid workflow source"if any other value is supplied.
Validates the definition before saving. Returns the saved workflow.
Delete a Workflow
Section titled “Delete a Workflow”curl -X DELETE http://localhost:3090/api/workflows/my-workflowQuery parameters:
cwd(optional) — Target directory (must have.archon/workflows/)source(optional, enum:project|global) — Scope to delete from. Defaults toproject. Passsource=globalto delete from~/.archon/workflows/. Returns400 "Invalid workflow source"if any other value is supplied.
Only user-defined workflows can be deleted. Bundled defaults cannot be removed.
| Method | Path | Description |
|---|---|---|
| POST | /api/workflows/{name}/run | Run a workflow (JSON or multipart) |
| GET | /api/workflows/runs | List workflow runs |
| GET | /api/workflows/runs/{runId} | Get run details with events |
| GET | /api/runs/{runId}/artifacts | List artifact files produced by a run |
| GET | /api/workflows/runs/by-worker/{platformId} | Look up a run by worker conversation ID |
| POST | /api/workflows/runs/{runId}/cancel | Cancel a running workflow |
| POST | /api/workflows/runs/{runId}/resume | Resume a failed or paused workflow |
| POST | /api/workflows/runs/{runId}/abandon | Abandon a run (running, paused, or failed); cascade-cancels non-terminal workflow: sub-run descendants |
| POST | /api/workflows/runs/{runId}/approve | Approve a paused workflow (400 if paused blocked on a workflow: child — approve the child) |
| POST | /api/workflows/runs/{runId}/reject | Reject a paused workflow (400 if paused blocked on a workflow: child — reject the child) |
| DELETE | /api/workflows/runs/{runId} | Delete a terminal run and its events |
Run a Workflow
Section titled “Run a Workflow”# JSON (no attachments)curl -X POST http://localhost:3090/api/workflows/archon-assist/run \ -H "Content-Type: application/json" \ -d '{"message": "Explain the auth module", "conversationId": "conv-123"}'
# multipart (with file attachments — max 5 files, ≤10 MB each)curl -X POST http://localhost:3090/api/workflows/archon-assist/run \ -F "conversationId=conv-123" \ -F "message=Investigate this trace" \ -F "files=@stacktrace.txt" \ -F "files=@screenshot.png"Supplying declared inputs. A workflow that declares inputs: takes their values through an optional inputs map — a flat object of string values. Omit a name to take its declared default:.
# JSON: inputs is a nested objectcurl -X POST http://localhost:3090/api/workflows/review-block/run \ -H "Content-Type: application/json" \ -d '{"message": "review it", "conversationId": "conv-123", "inputs": {"diff": "...", "style": "terse"}}'
# multipart: form fields are strings, so the same map travels JSON-encodedcurl -X POST http://localhost:3090/api/workflows/review-block/run \ -F "conversationId=conv-123" \ -F "message=review it" \ -F 'inputs={"diff":"...","style":"terse"}' \ -F "files=@context.md"Values are validated against the workflow’s declaration before any worktree, clone, or AI cost: a missing required input and an undeclared name are both refused up front, through the same contract a composing with: map goes through. 400 if inputs is not an object of strings (or, on multipart, not valid JSON). An empty object is the same as omitting the field.
Rebinding models for one run. Optional tiers and aliases maps change only the named tier or existing @alias for this invocation. Every other binding keeps its normal user → repo → global → built-in value.
# JSON: only `large` changescurl -X POST http://localhost:3090/api/workflows/issue-to-pr/run \ -H "Content-Type: application/json" \ -d '{"message":"fix #2481","conversationId":"conv-123", "tiers":{"large":"openai/gpt-5.6"}, "aliases":{"@reviewer":"codex/gpt-5.6-sol"}}'
# multipart: each map is one JSON-encoded form fieldcurl -X POST http://localhost:3090/api/workflows/issue-to-pr/run \ -F "conversationId=conv-123" \ -F "message=fix #2481" \ -F 'tiers={"large":"openai/gpt-5.6"}'Tier keys are small, medium, and large; alias keys start with @. A model spec can name an Archon agent/model, a Pi vendor/model, an unqualified model under the binding’s current provider, or another tier/alias preset. Literal model pins in the workflow remain unchanged. To replace all default tiers, author all three mappings explicitly. The run’s metadata.model_bindings records the effective non-secret bindings for attribution and the sparse resolved overrides for reuse on resume.
Loading inline config for one run. Optional config content uses the same sparse runtime keys as a CLI run config file. JSON sends it as an object; multipart sends the object JSON-encoded in one form field. Explicit tiers and aliases fields are the final model layer and replace only matching names from config.
# JSON contentcurl -X POST http://localhost:3090/api/workflows/issue-to-pr/run \ -H "Content-Type: application/json" \ -d '{"message":"fix #2482","conversationId":"conv-123", "config":{"tiers":{"large":{"provider":"pi","model":"minimax/MiniMax-M3"}}, "env":{"BENCH_MODE":"1"}}, "tiers":{"large":"openai/gpt-5.6"}}'
# multipart contentcurl -X POST http://localhost:3090/api/workflows/issue-to-pr/run \ -F "conversationId=conv-123" \ -F "message=fix #2482" \ -F 'config={"docs":{"path":"handbook"},"workflows":{"quotaMaxAttempts":3}}'Supported inline keys are assistant or defaultAssistant, assistants, tiers, aliases, workflows, docs.path, and env. Unknown or ineffective keys fail with 400 and name the key. configPath is always rejected: HTTP callers cannot ask the server to read a filesystem path. Run metadata stores sealed replay content plus redacted source/key attribution, and resume uses the original layer without accepting replacement content.
List Run Artifacts
Section titled “List Run Artifacts”curl http://localhost:3090/api/runs/{runId}/artifactsWalks the run’s on-disk artifact directory (dotfiles skipped) and returns { files: [{ path, size, modifiedAt }] }. Used by the console UI’s Artifacts tab. Returns { files: [] } when the run has no codebase or the codebase name is not in owner/repo form; 400 on invalid run id or path-escape attempt, 404 if the run does not exist.
Resume a Failed or Paused Run
Section titled “Resume a Failed or Paused Run”curl -X POST http://localhost:3090/api/workflows/runs/{runId}/resumeResumes the workflow from where it left off, skipping already-completed nodes. Equivalent to archon workflow resume <run-id> from the CLI. Plain archon workflow run <name> invocations never resume implicitly.
Approve / Reject a Paused Run
Section titled “Approve / Reject a Paused Run”# Approve (optionally with a comment)curl -X POST http://localhost:3090/api/workflows/runs/{runId}/approve \ -H "Content-Type: application/json" \ -d '{"comment": "Looks good, proceed"}'
# Reject (optionally with a reason)curl -X POST http://localhost:3090/api/workflows/runs/{runId}/reject \ -H "Content-Type: application/json" \ -d '{"reason": "Please add error handling first"}'Sub-run child gates (#2121 Phase 2): when a workflow: sub-run pauses at its own gate, its parent run pauses “blocked on child”. Approve/reject the child run (its id is in the parent’s block message) — the parent auto-resumes when the child completes. A child gate is the exception: it works for a 1:1 sub-run, but a child that pauses inside a fan_out: expansion fails the node instead — a parent has one approval slot and cannot hand it to N children, so gate before or after the fan-out node rather than inside a child of it. Calling approve/reject on the parent’s id while it is blocked on a child returns 400 with a redirect to the child id. abandon on a parent cascade-cancels its non-terminal sub-run descendants; the response’s cascadeFailures is non-zero if part of the tree could not be reached, and blockedParentRunId is set when the abandoned run was itself a child stranding a paused parent.
Commands
Section titled “Commands”| Method | Path | Description |
|---|---|---|
| GET | /api/commands | List available command names |
curl http://localhost:3090/api/commandsQuery parameters:
cwd(optional) — Working directory for project-specific commands
Returns { commands: [{ name, source: "bundled" | "project" }] }.
Dashboard
Section titled “Dashboard”| Method | Path | Description |
|---|---|---|
| GET | /api/dashboard/runs | List enriched workflow runs for the dashboard |
Query parameters include status filters, date ranges, and pagination. Used by the Command Center UI.
Configuration
Section titled “Configuration”| Method | Path | Description |
|---|---|---|
| GET | /api/config | Get read-only configuration (safe subset) |
| PATCH | /api/config/assistants | Update the default assistant and per-provider model defaults |
| PATCH | /api/config/tiers | Update model-tier presets (small/medium/large) |
| PATCH | /api/config/aliases | Update @custom model aliases (per-key merge; null unsets) |
| GET | /api/providers/pi/models | Pi’s model catalog (cost/reasoning metadata; best-effort, [] on failure) |
GET /api/config returns the safe config subset, now including the configured tiers, the built-in tierDefaults for the current default provider (what an unset tier resolves to), and the configured aliases.
These config routes are ungated — they write non-secret model config to ~/.archon/config.yaml and work on solo installs (no TOKEN_ENCRYPTION_KEY required). Contrast with the AI Provider Credentials routes below, which require an identity.
# Read current config (includes `tiers` + `tierDefaults`)curl http://localhost:3090/api/config
# Set the default assistantcurl -X PATCH http://localhost:3090/api/config/assistants \ -H "Content-Type: application/json" \ -d '{"assistant": "claude"}'
# Or update per-provider model defaultscurl -X PATCH http://localhost:3090/api/config/assistants \ -H "Content-Type: application/json" \ -d '{"assistants": {"claude": {"model": "opus"}}}'
# Set a model tier (a `null` tier value unsets it, falling back to the built-in default)curl -X PATCH http://localhost:3090/api/config/tiers \ -H "Content-Type: application/json" \ -d '{"tiers": {"large": {"provider": "claude", "model": "opus"}}}'
# Set a @custom alias (a `null` value unsets it)curl -X PATCH http://localhost:3090/api/config/aliases \ -H "Content-Type: application/json" \ -d '{"aliases": {"@fast": {"provider": "claude", "model": "haiku"}}}'Per-User AI Preferences
Section titled “Per-User AI Preferences”Each user can override the install-wide model config with personal tiers, @custom aliases, and a default assistant — the highest-precedence resolver layer, applied to runs and chats they start. These routes require a resolved web identity (X-Archon-User header or a Better Auth session) but no TOKEN_ENCRYPTION_KEY — model names aren’t secrets. Without an identity they return 401, and model resolution stays config-only (solo installs are unchanged).
| Method | Path | Description |
|---|---|---|
| GET | /api/auth/me/ai-prefs | The current user’s stored prefs (raw layer, not merged) |
| PATCH | /api/auth/me/ai-prefs/tiers | Update personal tier presets (per-key merge; null unsets) |
| PATCH | /api/auth/me/ai-prefs/aliases | Update personal @custom aliases (per-key merge; null unsets) |
| PATCH | /api/auth/me/ai-prefs/default | Set (or clear with null) the personal default assistant + default chat model ({ provider, model? } — written atomically; an omitted model clears any pin, and model without a provider is rejected) |
# Point YOUR `large` tier at opus without touching the install configcurl -X PATCH http://localhost:3090/api/auth/me/ai-prefs/tiers \ -H "X-Archon-User: your-user-id" \ -H "Content-Type: application/json" \ -d '{"tiers": {"large": {"provider": "claude", "model": "opus"}}}'All writes validate the provider (registered), effort (provider vocabulary), and alias names (@ prefix, not a reserved tier keyword), and return the updated prefs. The console exposes the same scopes as the “This install / Just me” toggle on AI Settings; the CLI as archon ai … --scope user.
AI Provider Credentials
Section titled “AI Provider Credentials”Per-user provider credentials let each user bill their runs and chats to their own API key or subscription instead of the shared install key. These endpoints require a resolved web identity (X-Archon-User header or a Better Auth session) — GET /api/auth/providers returns 401 without one. The encryption key is auto-provisioned on every install; TOKEN_ENCRYPTION_KEY is an optional override for managed deployments.
| Method | Path | Description |
|---|---|---|
| GET | /api/auth/providers | List the current user’s connected credentials (metadata only) |
| PUT | /api/auth/providers/{provider} | Connect (upsert) an API key for a provider |
| DELETE | /api/auth/providers/{provider} | Disconnect a provider credential (idempotent) |
| POST | /api/auth/providers/{provider}/oauth/start | Begin a subscription (OAuth) login |
| POST | /api/auth/providers/{provider}/oauth/poll | Poll a subscription login session |
Credentials are encrypted at rest; no endpoint ever returns a secret value — responses carry only provider/kind/label metadata.
List Connected Providers
Section titled “List Connected Providers”curl http://localhost:3090/api/auth/providers \ -H "X-Archon-User: your-user-id"Returns { enabled, connections: [{ provider, kind, label }], available, subscriptionAvailable, agents }:
available— every vendor id you can connect an API key for (anthropic,openai,github-copilot, plus the Pi backends). Legacyclaude/codex/copilotids are accepted on writes and normalized.subscriptionAvailable— the subset that supports subscription (OAuth) login:anthropic,openai, andgithub-copilot. (The ChatGPT/Codex subscription runs an Archon-owned PKCE flow that captures theid_tokenthe Codex CLI requires — see #1924.)agents— the agent -> credential matrix: per registered agent{ id, displayName, catalog: 'static'|'dynamic', ready, credentials: [{ vendor, displayName, kinds, connected, subscriptionAvailable, installEnv, ambientConfigured? }] }.installEnv/ambientConfiguredreport server-side detection so readiness renders on solo installs too; OpenCode iscatalog:'dynamic'(introspect viaGET /api/providers/opencode/credentials).
Connect an API Key
Section titled “Connect an API Key”curl -X PUT http://localhost:3090/api/auth/providers/openrouter \ -H "X-Archon-User: your-user-id" \ -H "Content-Type: application/json" \ -d '{"apiKey": "sk-...", "label": "personal"}'Returns { success, provider, kind: "api_key", label }. An unknown provider or a blank key returns 400.
Disconnect a Provider
Section titled “Disconnect a Provider”curl -X DELETE http://localhost:3090/api/auth/providers/openrouter \ -H "X-Archon-User: your-user-id"Idempotent — disconnecting a provider that was never connected still returns { success: true }.
Subscription Login (OAuth)
Section titled “Subscription Login (OAuth)”Subscription login is a two-step start -> poll flow held server-side. start returns a mode:
manual(anthropic, Claude Pro/Max) — show the returnedurl; the user authorizes in a browser and pastes the resulting code back viapoll.device(github-copilot) — showuserCode+verificationUri;polluntil connected.
# 1. Start a login sessioncurl -X POST http://localhost:3090/api/auth/providers/anthropic/oauth/start \ -H "X-Archon-User: your-user-id"# {"sessionId":"...","mode":"manual","url":"https://...","expiresIn":600}
# 2. Poll (pass the pasted `code` once, for manual flows)curl -X POST http://localhost:3090/api/auth/providers/anthropic/oauth/poll \ -H "X-Archon-User: your-user-id" \ -H "Content-Type: application/json" \ -d '{"sessionId": "...", "code": "the-pasted-code"}'# {"status":"connected"}poll returns { status: "pending" | "connected" | "error", detail? }. A provider that does not support subscription login returns 400 on start.
The CLI equivalent of this whole surface is archon ai. For the end-to-end setup walkthrough, see Per-user credentials and AI Settings.
System
Section titled “System”| Method | Path | Description |
|---|---|---|
| GET | /api/update-check | Check for available updates (binary builds only) |
Returns { updateAvailable, currentVersion, latestVersion, releaseUrl }. For non-binary (source) builds, always returns updateAvailable: false without making external requests.
SSE Streaming
Section titled “SSE Streaming”| Path | Description |
|---|---|
/api/stream/{conversationId} | Real-time events for a conversation |
/api/stream/__dashboard__ | Multiplexed workflow events across all conversations |
These are Server-Sent Events (SSE) endpoints — connect with EventSource in a browser or any SSE client.
# Listen to a conversation streamcurl -N http://localhost:3090/api/stream/your-conversation-idEvents are JSON-encoded with a type field. See the Web UI documentation for the full list of event types.
Common Patterns
Section titled “Common Patterns”Create a Conversation and Send a Message
Section titled “Create a Conversation and Send a Message”# 1. Create a conversationCONV_ID=$(curl -s -X POST http://localhost:3090/api/conversations \ -H "Content-Type: application/json" \ -d '{}' | jq -r '.platform_conversation_id')
# 2. Send a messagecurl -X POST http://localhost:3090/api/conversations/$CONV_ID/message \ -H "Content-Type: application/json" \ -d '{"message": "/status"}'
# 3. Poll for messagescurl http://localhost:3090/api/conversations/$CONV_ID/messagesRun a Workflow via the API
Section titled “Run a Workflow via the API”# 1. Create a conversation scoped to a codebaseCONV_ID=$(curl -s -X POST http://localhost:3090/api/conversations \ -H "Content-Type: application/json" \ -d '{"codebase_id": "your-codebase-id"}' | jq -r '.platform_conversation_id')
# 2. Start the workflowcurl -X POST http://localhost:3090/api/workflows/archon-assist/run \ -H "Content-Type: application/json" \ -d "{\"message\": \"How does auth work?\", \"conversationId\": \"$CONV_ID\"}"
# 3. Monitor via SSEcurl -N http://localhost:3090/api/stream/$CONV_ID