Skip to content

API Reference

Archon exposes a REST API via a Hono server with OpenAPI spec generation. All endpoints are prefixed with /api/.

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

A machine-readable OpenAPI 3.0 spec is available at:

GET /api/openapi.json

You can feed this into tools like Swagger UI or use it to generate typed API clients.

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.


MethodPathDescription
GET/healthBasic health check
GET/api/healthAPI-level health check
Terminal window
curl http://localhost:3090/health
# {"status":"ok"}
curl http://localhost:3090/api/health
# {"status":"ok","adapter":"...","concurrency":{...},"runningWorkflows":0}

MethodPathDescription
GET/api/conversationsList conversations
GET/api/conversations/{id}Get a single conversation
POST/api/conversationsCreate a new conversation
PATCH/api/conversations/{id}Update a conversation (rename)
DELETE/api/conversations/{id}Soft-delete a conversation
GET/api/conversations/{id}/messagesList messages in a conversation
POST/api/conversations/{id}/messageSend a message to a conversation
Terminal window
curl http://localhost:3090/api/conversations

Query parameters:

  • codebase_id (optional) — Filter by codebase
  • include_deleted (optional) — Include soft-deleted conversations
Terminal window
curl -X POST http://localhost:3090/api/conversations \
-H "Content-Type: application/json" \
-d '{}'

Optionally specify a codebase:

Terminal window
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.

Terminal window
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.

Terminal window
curl http://localhost:3090/api/conversations/{id}/messages

Query parameters:

  • limit (optional) — Number of messages to return
  • before (optional) — Cursor for pagination
Terminal window
curl -X PATCH http://localhost:3090/api/conversations/{id} \
-H "Content-Type: application/json" \
-d '{"title": "My feature discussion"}'
Terminal window
curl -X DELETE http://localhost:3090/api/conversations/{id}

Performs a soft delete — the conversation is hidden but not destroyed.


MethodPathDescription
GET/api/codebasesList registered codebases
GET/api/codebases/{id}Get a single codebase
POST/api/codebasesRegister a codebase (clone or local path)
DELETE/api/codebases/{id}Delete a codebase and clean up resources
GET/api/codebases/{id}/environmentsList isolation environments for a codebase
Terminal window
curl http://localhost:3090/api/codebases

Clone from a URL:

Terminal window
curl -X POST http://localhost:3090/api/codebases \
-H "Content-Type: application/json" \
-d '{"url": "https://github.com/user/repo"}'

Register a local path:

Terminal window
curl -X POST http://localhost:3090/api/codebases \
-H "Content-Type: application/json" \
-d '{"path": "/home/user/projects/my-repo"}'
Terminal window
curl -X DELETE http://localhost:3090/api/codebases/{id}

Removes the codebase registration and cleans up associated worktrees and isolation environments.

Terminal window
curl http://localhost:3090/api/codebases/{id}/environments

Returns the isolation environments (worktrees) associated with a codebase.


MethodPathDescription
GET/api/workflowsList available workflows
GET/api/workflows/{name}Get a single workflow definition
POST/api/workflows/validateValidate 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
Terminal window
curl http://localhost:3090/api/workflows

Query 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? }. parseWarnings contains 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. Unlike parseWarnings, these workflows did not load.
Terminal window
curl http://localhost:3090/api/workflows/archon-assist

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

Terminal window
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.

Terminal window
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 to project (writes to <cwd>/.archon/workflows/). Pass source=global to write to the home-scoped location (~/.archon/workflows/). Returns 400 "Invalid workflow source" if any other value is supplied.

Validates the definition before saving. Returns the saved workflow.

Terminal window
curl -X DELETE http://localhost:3090/api/workflows/my-workflow

Query parameters:

  • cwd (optional) — Target directory (must have .archon/workflows/)
  • source (optional, enum: project | global) — Scope to delete from. Defaults to project. Pass source=global to delete from ~/.archon/workflows/. Returns 400 "Invalid workflow source" if any other value is supplied.

Only user-defined workflows can be deleted. Bundled defaults cannot be removed.

MethodPathDescription
POST/api/workflows/{name}/runRun a workflow (JSON or multipart)
GET/api/workflows/runsList workflow runs
GET/api/workflows/runs/{runId}Get run details with events
GET/api/runs/{runId}/artifactsList 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}/cancelCancel a running workflow
POST/api/workflows/runs/{runId}/resumeResume a failed or paused workflow
POST/api/workflows/runs/{runId}/abandonAbandon a run (running, paused, or failed); cascade-cancels non-terminal workflow: sub-run descendants
POST/api/workflows/runs/{runId}/approveApprove a paused workflow (400 if paused blocked on a workflow: child — approve the child)
POST/api/workflows/runs/{runId}/rejectReject 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
Terminal window
# 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"
Terminal window
curl http://localhost:3090/api/runs/{runId}/artifacts

Walks 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.

Terminal window
curl -X POST http://localhost:3090/api/workflows/runs/{runId}/resume

Resumes 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.

Terminal window
# 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.


MethodPathDescription
GET/api/commandsList available command names
Terminal window
curl http://localhost:3090/api/commands

Query parameters:

  • cwd (optional) — Working directory for project-specific commands

Returns { commands: [{ name, source: "bundled" | "project" }] }.


MethodPathDescription
GET/api/dashboard/runsList enriched workflow runs for the dashboard

Query parameters include status filters, date ranges, and pagination. Used by the Command Center UI.


MethodPathDescription
GET/api/configGet read-only configuration (safe subset)
PATCH/api/config/assistantsUpdate the default assistant and per-provider model defaults
PATCH/api/config/tiersUpdate model-tier presets (small/medium/large)
PATCH/api/config/aliasesUpdate @custom model aliases (per-key merge; null unsets)
GET/api/providers/pi/modelsPi’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.

Terminal window
# Read current config (includes `tiers` + `tierDefaults`)
curl http://localhost:3090/api/config
# Set the default assistant
curl -X PATCH http://localhost:3090/api/config/assistants \
-H "Content-Type: application/json" \
-d '{"assistant": "claude"}'
# Or update per-provider model defaults
curl -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"}}}'

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

MethodPathDescription
GET/api/auth/me/ai-prefsThe current user’s stored prefs (raw layer, not merged)
PATCH/api/auth/me/ai-prefs/tiersUpdate personal tier presets (per-key merge; null unsets)
PATCH/api/auth/me/ai-prefs/aliasesUpdate personal @custom aliases (per-key merge; null unsets)
PATCH/api/auth/me/ai-prefs/defaultSet (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)
Terminal window
# Point YOUR `large` tier at opus without touching the install config
curl -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.


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.

MethodPathDescription
GET/api/auth/providersList 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/startBegin a subscription (OAuth) login
POST/api/auth/providers/{provider}/oauth/pollPoll a subscription login session

Credentials are encrypted at rest; no endpoint ever returns a secret value — responses carry only provider/kind/label metadata.

Terminal window
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). Legacy claude/codex/copilot ids are accepted on writes and normalized.
  • subscriptionAvailable — the subset that supports subscription (OAuth) login: anthropic, openai, and github-copilot. (The ChatGPT/Codex subscription runs an Archon-owned PKCE flow that captures the id_token the 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/ambientConfigured report server-side detection so readiness renders on solo installs too; OpenCode is catalog:'dynamic' (introspect via GET /api/providers/opencode/credentials).
Terminal window
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.

Terminal window
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 is a two-step start -> poll flow held server-side. start returns a mode:

  • manual (anthropic, Claude Pro/Max) — show the returned url; the user authorizes in a browser and pastes the resulting code back via poll.
  • device (github-copilot) — show userCode + verificationUri; poll until connected.
Terminal window
# 1. Start a login session
curl -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.


MethodPathDescription
GET/api/update-checkCheck for available updates (binary builds only)

Returns { updateAvailable, currentVersion, latestVersion, releaseUrl }. For non-binary (source) builds, always returns updateAvailable: false without making external requests.


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

Terminal window
# Listen to a conversation stream
curl -N http://localhost:3090/api/stream/your-conversation-id

Events are JSON-encoded with a type field. See the Web UI documentation for the full list of event types.


Terminal window
# 1. Create a conversation
CONV_ID=$(curl -s -X POST http://localhost:3090/api/conversations \
-H "Content-Type: application/json" \
-d '{}' | jq -r '.platform_conversation_id')
# 2. Send a message
curl -X POST http://localhost:3090/api/conversations/$CONV_ID/message \
-H "Content-Type: application/json" \
-d '{"message": "/status"}'
# 3. Poll for messages
curl http://localhost:3090/api/conversations/$CONV_ID/messages
Terminal window
# 1. Create a conversation scoped to a codebase
CONV_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 workflow
curl -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 SSE
curl -N http://localhost:3090/api/stream/$CONV_ID