API reference
The daemon serves a JSON HTTP API on 127.0.0.1:4748
(configurable). Authentication: none. The API is loopback-only. UIs and
integrations talk to this.
Health & status
GET /api/health
{"ok": true, "port": 4748}GET /api/status
{
"sessionId": "f3a8c2…",
"taskId": "t-f3a8c2-1748906412",
"taskGoal": "add refund flow to orders service",
"role": "developer",
"activeProvider": "claude",
"tokensUsed": 74200,
"graphNodes": 342,
"handoffsDone": 2,
"hfsScore": 0.94,
"hfsHistory": [0.40, 0.50, …],
"fsmState": "RUNNING"
}Providers
GET /api/providers
Live runtime status per provider (active/standby/exhausted/error).
GET /api/config/providers
Full metadata + install/auth capability flags + probe result.
POST /api/config/providers
Body:
{
"name": "claude",
"enabled": true,
"declaredCap": 40000,
"model": "qwen2.5-coder:32b",
"baseUrl": "http://localhost:11434"
}Patches [providers.<name>] in
relay.toml and reloads.
POST /api/providers/install
{name}
Spawns a terminal running the per-OS install command.
POST /api/providers/oauth
{name}
Spawns the provider's OAuth subcommand (e.g.
claude login).
POST /api/providers/api-key
{name, value}
Writes key to .relay/.env, reloads daemon env.
Accounts
Multiple logins per provider (account-aware handoff). Each account is a label plus an isolated config dir; the adapter is pointed at the active account's dir.
POST /api/providers/account
{provider, label}
Switch the active account for a provider. The next session (or handoff) uses it.
POST /api/providers/account/add
{provider, label, configDir}
Register a login. configDir optional, defaults to an
isolated per-label dir.
POST /api/providers/account/remove
{provider, label}
Remove a login.
POST /api/providers/account/login
{provider, label}
Spawns a terminal running the provider's login flow against that account's config dir.
Accounts are returned inside GET /api/config/providers
as accounts: [{label, active, configDir}] +
activeAccount.
Detection & adoption
GET /api/detect?sinceHours=<n>
Scan for AI coding agents already running on this machine (process scan + on-disk transcript stores). Default window 24h.
[
{
"id": "claude_6d569909",
"provider": "claude",
"displayName": "Claude Code",
"surface": "cli",
"pid": 14032,
"running": true,
"workDir": "C:/dev/my-project",
"account": "personal",
"detectedVia": ["process", "transcript"],
"lastActive": 1752666000,
"session": {
"sessionId": "6d569909…",
"model": "claude-sonnet-5",
"initialPrompt": "add a refund flow…",
"plan": ["…"],
"tasksRemaining": ["…"],
"filesTouched": ["orders/refund.go"],
"skills": ["backend-patterns"],
"mcps": ["github"],
"recentTurns": [{"role": "user", "text": "…"}, {"role": "assistant", "text": "…"}],
"tokensIn": 84210,
"tokensOut": 12045,
"messageCount": 37
}
}
]recentTurns carries a redacted, verbatim tail of the
conversation (last 16 user/assistant turns, oldest first), so a handoff
moves real recent context rather than only the first and last
prompts.
POST /api/detect/adopt
{id, target, start, interactive}
Lift a detected session's intent into a continuation brief (persisted
under .relay/adopted/), targeting provider
target. Two mutually exclusive continue modes:
"interactive": trueopens the target provider's own CLI in a new terminal, in the adopted project directory, under the active account, seeded with a prompt that reads the full brief. Relay then steps back and the developer drives the session. The response includesopened(bool) and, on failure,openError."start": true(headless) launches a Relay-orchestrated session that continues the work and streams events to the dashboard. The response includesstartedand, on failure,startError.
With neither flag, the brief is rendered and persisted but nothing is launched.
Quota wallet
GET /api/quota/wallet
Per provider+account: remaining quota, reset time, live burn rate, and forecast ETA (feeds predictive handoff).
[
{
"provider": "claude",
"account": "personal",
"remaining": 12400,
"total": 40000,
"fractionUsed": 0.69,
"resetsAt": "2026-07-16T21:00:00Z",
"source": "transcript",
"burnPerMin": 310.5,
"etaMinutes": 39.9
}
]Pipelines
Multi-agent DAGs: one provider+task per node, explicit
dependsOn ordering, fallback providers on
failure, verify acceptance commands (verifier gate) per
node.
GET /api/pipelines
[
{
"name": "feature",
"nodes": [
{
"id": "build",
"provider": "claude",
"task": "implement the endpoint",
"dependsOn": [],
"fallback": ["codex"],
"verify": ["go build ./...", "go test ./..."]
}
]
}
]POST /api/pipelines
Saves the full pipeline list (same shape as GET response). Persisted
to .relay/pipelines.json.
POST /api/pipelines/run
{name}
Executes the pipeline's nodes in dependency order. Honours the
single-session guard: errors with "session already running"
if one is active.
Time machine
GET /api/history
The handoff timeline, read from the audit log.
[
{"seq": 3, "ts": "2026-07-16T09:41:03Z", "event": "handoff", "provider": "claude", "summary": "quota breach → codex"}
]GET /api/history/commits
Snapshot commits on the session branch:
[{sha, short, subject, when}].
GET /api/history/diff?sha=<sha>
The diff for one snapshot commit.
POST /api/history/rewind
{sha}
Non-destructive rewind: creates a new branch at the snapshot so current work is never lost.
Profiles
GET /api/profiles
[
{
"name": "backend",
"chain": ["claude", "codex"],
"kinds": ["api", "service"],
"skills": ["go", "ts"],
"contextHint": "production backend services"
}
]POST /api/profiles
{
"name": "backend",
"chain": ["claude", "codex"],
"kinds": ["api", "service"],
"skills": ["go", "ts"],
"contextHint": "production backend services",
"delete": false
}Upserts (or deletes when "delete": true).
Session control
POST /api/run
{task, threshold, maxHandoffs}
Starts a session in the running daemon. Single-session: 409 if one is already active.
POST /api/handoff
Trigger immediate handoff.
POST /api/session/pause
{pause}
Toggle agent execution (cooperative; honoured at event boundaries).
POST /api/session/reply
{reply}
Send a stdin reply to the active adapter (if it implements
StdinReplier).
GET /api/session/diff
{
"summary": " orders/refund.go | 42 +++\n orders/routes.go | 18 +-",
"diff": "diff --git a/orders/refund.go …"
}Returns git diff main...relay/<sid> against the
user's main branch.
GET /api/session/cost
{
"sessionUsd": 0.0042,
"tokensIn": 1820,
"tokensOut": 411,
"provider": "claude"
}Approvals
GET /api/approvals
Pending requests waiting for user resolution.
[
{
"id": "abc123",
"action": "write 250 lines to orders/refund.go",
"reason": "exceeds 200-line threshold",
"severity": "warn",
"createdAt": "2026-06-03T15:41:03Z"
}
]POST /api/approvals/<id>
{approved, note}
Resolve a pending request.
Events stream
GET /api/events?since=<id>
Pull events newer than the given ID:
[
{"id": 412, "ts": "09:41:27", "tag": "quota", "msg": "claude: 74% used"},
{"id": 413, "ts": "09:41:28", "tag": "tool_use", "msg": "Edit orders/routes.go"}
]Tag values: tool_use, tool_result,
text, system, quota,
handoff, waiting, error.
WS /ws
WebSocket push of the same events as they happen. Reduces latency from 1500ms (poll) to ~50ms.
Graph & retrieval
GET /api/graph
{"nodes": 342, "edges": 418}
GET /api/graph/detail
Full node + edge list (~200 most recent). Includes
module and symbol nodes from the codebase scan
plus session-specific decision, constraint,
file, do_not_redo nodes from agent
activity.
GET /api/graph/project?path=<repo>
One-shot codegraph scan of an arbitrary repo path (no session
needed): returns {nodes, edges} of module /
symbol nodes. Used by the desktop Graph page's project
picker.
GET /api/retrieval?q=<text>&limit=<n>
FTS5 full-text search over indexed code chunks. Default limit 20.
[
{
"ID": "chunk:orders/refund.go:1-40",
"Path": "orders/refund.go",
"Lang": "go",
"StartLine": 1,
"EndLine": 40,
"Body": "package orders\n\nimport ...\n\nfunc (s *RefundService) execute(...) error {\n ...\n}",
"SHA": "a3f8c2d4",
"Dim": 0
}
]Used by the MCP relay_retrieve tool. Chunks are
populated by the codegraph scanner at session start; future
embedding-based ranking will use the embedding blob
column.
Audit & contract
GET /api/contract
The current continuation contract as JSON.
GET /api/instructions
Discovered CLAUDE.md / AGENTS.md / Cursor rules + profile skills.
Vision
GET /api/vision/config
POST /api/vision/config
POST /api/vision/probe
See Vision fallback.
Ollama bridge
GET /api/ollama/models?baseUrl=...
Installed models + curated vision pull list.
POST /api/ollama/pull
{baseUrl, tag}
Stream-pulls a model from ollama.com. Progress emitted to event log.
POST /api/ollama/launch
{provider, model}
Spawns ollama launch <tool> --model <model>
in a new terminal.
Observability
GET /api/circuit
Per-provider circuit breaker snapshots.
[
{"name": "claude", "state": "closed", "failures": 0},
{"name": "codex", "state": "open", "failures": 3, "openedAt": "..."},
{"name": "ollama", "state": "half-open", "failures": 0}
]GET /api/outcomes
Profile success aggregates.
[
{
"profile": "backend",
"runs": 12,
"successes": 10,
"successRate": 0.833,
"avgTokens": 18420,
"avgCostUsd": 0.087,
"lastRun": "2026-06-03T15:41:03Z"
}
]GET /api/redactions
Counts of secrets scrubbed this session.
{"summary": "openai_key:2, env_secret_pair:1"}