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"],
"tokensIn": 84210,
"tokensOut": 12045,
"messageCount": 37
}
}
]POST /api/detect/adopt
{id, target, start}
Lift a detected session's intent into a continuation brief (persisted
under .relay/adopted/), targeting provider
target. With "start": true also launches a
Relay session that continues the work.
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"}