Skip to content

Reference: dashboard routes ​

Every route below sits behind the dashboard's one identity gate unless noted. Which credential it checks is the dashboard.auth strategy in config.yaml: access — a browser signed in through Cloudflare Access or, for machine callers, a service token (the default when ACCESS_TEAM_DOMAIN and ACCESS_AUD are set); token — an Authorization: Bearer header holding the secret in DASHBOARD_TOKEN (or the env var dashboard.token.env names), which resolves to the one actor dashboard.token.actor names; or none — no credential, served only to loopback callers of a localhost deployment and refused everywhere else (the default without Access). None of them set caching headers that would let a proxy or browser cache a response beyond the request that made it (no-store throughout) — every load is live.

The replacement sidebar offers Home, Delivery, Costs and Metrics; capabilities hide unavailable reports. The same navigation opens as a drawer on phones. Settings is a global destination with Channels, MCP servers, Identities, Runtime and Installation sections; MCP servers and Runtime follow their capabilities. Docs remains available. The account menu selects Mine (default), read-only Person or privileged All people for work pages; this selection changes no sender or grants.

Every page route below also answers its data alone as JSON to a request whose Accept header is application/json — the same status and the same content the page paints from. The dashboard navigates between its pages that way, without a document load; an agent may read a page the same way.

RouteShowsNotes
GET /runsActive runs, newest firstDefault view excludes finished runs; each row's link carries that run's capability token — the index itself is gated specifically because of this
GET /runs?all=1Active and finished runsOnly meaningful with runHistory configured — otherwise there's nothing finished to show
GET /runs?mine=1Compatibility filter for the actual requester, within the authorized global work viewThe replacement has no per-page Show mine switch. Use the global account menu; an email match never makes the account a Slack requester
GET /runs/<id>One run: request → steps (tool calls, results) → answerLive via SSE while the run is active; served as a static page, no token needed, once it's in history
GET /runs/<id>/eventsRaw SSE event stream for that runWhat the run page itself consumes; resumable via Last-Event-ID
GET /runs/<id>/frictionWhy a finished run was slow, if it wasRead-only diagnosis, no side effects
GET /runs/unit/<pipeline>:<unit>One ship unit's story: its one thread's runs in round order, each opening to its timeline, with a search over the whole threadWhat runs unit answers, as a page; a unit you may not see is the same 404 an unknown run gives. The pipeline's own run page lists its units, and a conductor's run page lists the runs it spawned
POST /runs/<id>/stop?mode=soft|hard—Stops a live run; soft lets it wrap up and answer, hard aborts in-flight
GET /residentsEvery onboarded repo, its lifecycle state, its disk gauge (used/total) and how many runs are on it; each row folds open to those runs — stopwatch, run link, the worktree each holds (ref, commit, OS user, deps, size), the idle worktrees, and the room left on the diskThe dashboard twin of repo list; the fold lists the runs the viewer may read, live
GET /residents?stream=1The residents index feed (SSE): run rows as the registry publishes them, and the listing again whenever a run's worktree is bound or releasedWhat the index consumes to stay current; no timer re-reads the resident Worker
GET /residents/<owner>/<name>One repo's resident: mirror status, warm checkout, active thread worktrees, and its disk — used/total, free, the reserve it keeps back, headroom in "more trees", and every component (mirror, deps, checkout, each thread tree, leftover caches)The same numbers the resident's attach gate decides on — see onboard a repo → Disk
GET /costsDaily spend across every configured groupPriced from a snapshot of Cloudflare's and (optionally) Anthropic's billing data, taken daily (costs.snapshot.everyHours) or on request with costs snapshot; the page names the snapshot, its age and when the next is due
GET /costs/<group>Spend for one group?view=users|threads|channels|agents|models opens that dimension's tab — the same dollars laid against who started the runs, the thread or channel they ran in, the agent, or the model; ?stream=1 is the page's status feed (SSE: the snapshot's stamp, a take in flight, the next due time — the page repaints its figures when a new snapshot lands)
GET /costs/<group>.jsonSame data, machine-readableFor scripting/alerting, not for embedding a live dashboard elsewhere; carries snapshot.takenAt; 503 with Retry-After before the first snapshot
GET /costs/<group>/<view>.jsonOne dimension's report, machine-readable<view> is users, threads, channels, agents or models: the rows the tab shows (key, runs, turns, llmUsd, cloudUsd, totalUsd, unpricedTokens, byModel), the coverage and the reconciliation; the same as costs by <dimension> on the command surfaces; 503 with Retry-After before the first snapshot
GET /metricsUsage for the authorized review report?from=YYYY-MM-DD&to=YYYY-MM-DD: inclusive UTC finish days, at most 90; the selected Mine/Person/All people scope filters before aggregation
GET /metrics/healthSystem health from the same recorded review reportShares the exact range; ratios retain their denominators, and unavailable diagnostics are not inferred
GET /metrics/weekWeek over week from the same recorded review reportThe range beside the same range whole weeks earlier, cut at the same time while today is filling; an earlier range outside the 90 days kept is said, not compared
GET /metrics.jsonThe same scoped review report as JSONBoth tabs retain this exact range in their download link; metrics trend remains a separate command/API contract
GET /metrics/week.jsonThe Week over week tab's report and comparison as JSONThe tab's download link keeps its exact range
GET /deliveryDelivery indicators for the first configured repository — issue-to-merge time, first-pass CI, review rounds, the findings and the share resolved with no human edit, per week and per unitFrom the repository's snapshot of GitHub's facts, refreshed on an interval and dated in the footer, plus the run history you may see; a read that stopped at its cap says over the tiles that they cover the newest pull requests only and marks the incomplete weeks; ?weeks=n or ?since=YYYY-MM-DD; ?fresh=1 reads GitHub now
GET /delivery/<owner>/<name>The same for one configured repositoryThe command twin, delivery report --repo, takes any repository
GET /delivery/<owner>/<name>.jsonSame data, machine-readable
GET /planeCompatibility address for HomeThe replacement route resolves to /home; it is not a second dashboard
GET /settingsGlobal Settings: MCP servers when configured, else ChannelsRegistry reads and writes use the actual account and its grants; me remains the Access account's own tier. Missing/mismatched seed shows unavailable and Reload without editable controls
GET /settings/connectSign in to approve a local CLI or MCP client and manage your connected devicesThe link carries a bearer digest, never the bearer; approval requires the personal MCP grant, and revocation removes that digest from the credential store
GET /settings/mcpsEvery MCP server your runs can reach, by scope, with an add form; ?channel=<id> lists that channel's scope beside the org'sAdd and Connect hand back the one-time link below; a credential never passes through the page
GET /settings/channelsThe channels that carry a scope, with the setting names each one hasThe dashboard twin of config overrides
GET /settings/channels/<channel id>One channel's scope as a form: agent, models, effort, the boundary, instructionsSave is config set channel --channel <id>; Clear is config clear; the instructions box is config instructions
GET /threadsCompatibility address for Home thread discoveryThe replacement resolves to /home/threads. Discovery shows its loaded coverage and adds no second composer
GET /threads/<id>One thread: your turns and the runs they started, each with its route, its work folded and its reply; a live run draws from its own stream<id> is a thread of your lane, or another channel's thread by its whole key — read-only there; one you may see nothing of is the same 404 an unknown run gives
POST /threads/<id>/send—{ text } into the pipeline as you: 202 { runId, viewPath } when a run started, 200 { reply } when it answered inline (a hand-back, help, a steer); same-origin JSON only; another channel's thread is refused
GET /settings/installationThe running config.yaml's behaviour knobs with the value in force, and the capabilities that are onRead-only by construction: a projection by allow-list, no env var name or URL ever on the page
GET /mcp/connect/<nonce>The one-time MCP credential-paste formBound to whoever mints it or first opens it; single use, expires in 10 minutes
GET /—Not gated: a 302 to /threads, and nothing else — the Access application lists path prefixes, and / would cover the bearer routes
GET /healthz{ok, inFlight, draining, catchUp}Not gated — this is the process health probe, meant to be hit by the deploy tooling and the container platform

Command routes (/api/<group>.<verb>) ​

Every registered command has an HTTP twin behind the same dashboard gate, plus an MCP tool of the same name — one definition, every surface (explanation). A write is POST-only; a read takes either verb (GET with a kebab-case query string, POST with a camelCase JSON body). The Scope column is what an operator identity or service token must hold.

RouteMethodsActionWhat it does
/api/identity.challengePOSTidentity:execComplete challenge through fresh two-sided account verification.
/api/identity.listGET, POSTidentity:readRead your verified identity operation.
/api/identity.linkPOSTidentity:execComplete link through fresh two-sided account verification.
/api/identity.unlinkPOSTidentity:execComplete unlink through fresh two-sided account verification.
/api/identity.recoverPOSTidentity:execComplete recover through fresh two-sided account verification.
/api/help.showGET, POSThelp:readHow to talk to this bot — the agents, forcing one, changing a route in the thread — for a person asking about the bot itself, never for a task or a thing to show.
/api/help.commandsGET, POSThelp:readEvery chat command by group, the grammar, and the per-request directives.
/api/status.showGET, POSTstatus:readWhich build this process runs and, when known, the base config loaded at startup.
/api/config.showGET, POSTconfig:readThe effective agent/model/effort for you in this channel, the defaults, both scopes, and what is restricted; without a channel (a browser, a token, the CLI), your settings outside any channel.
/api/config.overridesGET, POSTconfig:readWhich channels carry a scope (a config.yaml block or a runtime override) and which settings each one names — never a value; config show --channel <id> reads one.
/api/config.channelsGET, POSTconfig:readThe channels you may pick settings or MCP servers for, by name: the channels the bot is in that you may read, plus any that already carry a scope; listed: false says the bot could not list its channels and only the scoped ones are here.
/api/config.setPOSTconfig:writeSet the agent, model, effort, verbosity, harness, boundary or default repository (--repo owner/name) for a channel (gated), or agent settings for yourself; per-agent forms take --models.<agent>, --efforts.<agent> and --harness.<agent>. Set the intake gate's mode for a thread (gated like the channel), a person's GitHub binding (config set user --user <id> --github <login>, identity admins — never your own: it is not yours to type), or the pull-request watch (config set org|repo --pulls.watch on|off with its caps, repo taking --repo <owner/name>).
/api/config.clearPOSTconfig:writeDrops every runtime override of a channel (gated), of yourself (your GitHub binding stays — it is an identity admin's write), or of a thread (gated like the channel); config clear user --user <id> removes one person's GitHub binding (identity admins). Static config.yaml values show through again.
/api/config.instructionsPOSTconfig:writeCustom instructions for a channel (gated) or for yourself — advisory prompt content that never changes agent, model, or permissions.
/api/credentials.inspectPOSTcredentials:execInspect a live run's credential boundary; reports only counts and booleans, never credential contents. Does not test publication or revocation.
/api/runs.listGET, POSTruns:readList runs (live and persisted, newest first) — metadata only, never message text.
/api/runs.claimsGET, POSTruns:readList persisted unknown native claim observations. These are not current holds or commit receipts; missing observations do not prove absence.
/api/runs.getGET, POSTruns:readOne run's record and cost in dollars per model (or unpriced); --include messages adds events with free text wrapped as untrusted content; --include audience adds a structural refusal diagnostic when recorded.
/api/runs.eventsGET, POSTruns:readA page of one run's events after --after-seq (server-capped); free text wrapped as untrusted content.
/api/runs.frictionGET, POSTruns:readOne run's friction diagnosis (live: computed now; persisted: as stored).
/api/runs.checkGET, POSTruns:readInspect an original recorded check without running it again; saved output is wrapped as untrusted content.
/api/runs.settlecheckPOSTruns:writeRetire one original check after its run finishes, only when saved proof verifies that the original process ended.
/api/runs.stopPOSTstop:writeEnd the current turn, clean up the runtime, and keep the run as stopped by user. Records who requested the stop. Needs stop permission for the run.
/api/runs.killPOSTstop:writeKill the runtime without a graceful turn shutdown and delete the run from live and saved run views. Destructive. Needs stop permission for the run.
/api/runs.unitGET, POSTruns:readA ship unit's runs in round order — its thread's, live and finished, each with its round — from one read.
/api/runs.childrenGET, POSTruns:readThe runs one run spawned — a conductor's children, live and finished — oldest started first.
/api/runs.findingsGET, POSTruns:readA pull request's findings ledger — every review finding by id with its severity, where it was raised, what the coding run recorded against it and whether the next review agreed — read from the run records alone.
/api/runs.searchGET, POSTruns:readSearch one session's log — a thread's conversation on one agent, every run of it — for words: the matching turns in relevance order, each with its run; snippets wrapped as untrusted content.
/api/steer.runPOSTsteer:writeFold words into a live run at its next step boundary, by run id.
/api/review.abridgePOSTreview:writeAbridge a finished PR review's reading diff with meat.dev on the bot host (one Opus-class call) and store it on the run; idempotent — a stored one is answered, not recomputed.
/api/review.statusGET, POSTruns:readRead the progress of an abridged review diff without starting or recomputing it.
/api/friction.reportGET, POSTfriction:readRanked recurring friction patterns across recent runs — read-only, GitHub never consulted.
/api/friction.proposePOSTfriction:writeClusters recent friction, deduplicates against open issues, and files the top proposals as labeled issues.
/api/repo.listGET, POSTrepo:readEvery registered repo, including cold entries; resident state, ref, sha, last refresh, and disk gauge.
/api/repo.workloadGET, POSTrepo:readRead one resident's workload count, owner records and missing evidence. Does not change or release work.
/api/repo.onboardPOSTrepo:writeRegister a repo, optionally without a resident (--no-resident); resident provisioning is billable and admin-gated.
/api/repo.offboardPOSTrepo:writeRemove a repo registration and tear down its resident when present (admin-gated; --dry-run plans only).
/api/repo.reconfigurePOSTrepo:writeChange a registered repo's branch, commands or warm resident count (admin-gated; additional warm capacity is billable).
/api/repo.rebuildPOSTrepo:writeRebuild the container and snapshot to repair provisioning; keep bindings and operation records. --full also clears all resident state and backups for a clean start or stale hold. Both discard container files and keep repo settings. --dry-run plans only (admin-gated).
/api/repo.testPOSTrepo:execExecutes the repo's onboarded test command with zero model turns (needs coding-agent access; the ref must be a plausible branch).
/api/repo.buildPOSTrepo:execExecutes the repo's onboarded build command with zero model turns (needs coding-agent access; the ref must be a plausible branch).
/api/memory.listGET, POSTmemory:readYour own memory records and the shared org / repo / channel records, with ids — what influences your runs.
/api/memory.forgetPOSTmemory:writeSoft-delete one memory record so it no longer influences any run (yours freely; shared org/repo/channel records need repo-management rights).
/api/memory.sweepPOSTmemory:writeRetire the stored status records the write gate rejects today (soft delete, per scope; yours freely, shared org/repo/channel scopes need repo-management rights); --dry-run lists the marked ids and changes nothing.
/api/mcp.listGET, POSTmcp:readExternal MCP servers your runs in this channel can use — org-wide, this channel's, and your own — with state and agents; never a credential. --all (admins): every tier.
/api/mcp.addPOSTmcp:writeRegister an external MCP server for yourself, this channel, or the org — auth is detected from the server; sign-in or a token happens on a one-time link, never in chat.
/api/mcp.connectPOSTmcp:writeA fresh one-time link to sign in to an OAuth server or enter (or replace) a bearer server's token — only you can complete it; it expires in 10 minutes.
/api/mcp.showGET, POSTmcp:readOne MCP server's entry plus a live probe of the tools it offers (names, read-only flags); never a credential.
/api/mcp.removePOSTmcp:writeRemove an MCP server you added and its stored credential (yours freely; channel ones need channel-config rights, org-wide ones admin rights).
/api/mcp.promotePOSTmcp:writePromote a person's MCP server into the org tier (admins): the same name, URL and auth, added by you; a bearer/oauth server gets a fresh org connect link for you to complete — the person's credential is never copied.
/api/schedule.listGET, POSTschedule:readEvery scheduled job (cron, UTC), which Worker fires it, its next firing, and what its last firing did.
/api/deploy.planGET, POSTdeploy:readThe production deploy plan: checks, Worker order, preflight handling — computed, nothing executed. With --affected, also which Workers this tree actually needs deployed and why.
/api/delivery.reportGET, POSTdelivery:readDelivery indicators per week and per unit — issue-to-merge time, first-pass CI, review rounds, findings and the share resolved with no human edit — from the repository's snapshot of GitHub's facts (--fresh reads GitHub now) and the run history; nothing written.
/api/costs.byGET, POSTcosts:readWhat the runs cost by user, thread, channel, agent or model over the range — LLM from their tokens through the price table, cloud allocated by run wall-clock — the costs page's tabs as text or JSON, from the snapshot; nothing written.
/api/costs.snapshotPOSTcosts:writeTake the costs snapshot now: read both billing sources and the run history once over the page's widest range, store the result, and serve it to every reader of the costs page from then on.
/api/metrics.trendGET, POSTmetrics:readThe run trend from the metrics dataset: runs, failure rate, p50/p95 wall and dollars per day and per agent over the range, weighted for sampling as text or JSON; review dashboard tabs use metrics reviews; nothing written.
/api/metrics.reviewsGET, POSTmetrics:readThe review dashboard's report from the metrics dataset: review runs by requester and surface, time spent, reviews per pull request, verdicts and findings, and the system tiles (run success, warm starts, setup losses), weighted for sampling; with --compare, the same range whole weeks earlier; nothing written.
/api/metrics.feedbackGET, POSTmetrics:readFind human feedback on native PR reviews or retrieve an exact retained reaction snapshot by run ID. Returns PR/review links, reviewed head, snapshot status and stable reaction/actor IDs for investigation. Stored reads only; no review, model run or GitHub write is started.
/api/providers.checkGET, POSTproviders:readRead the provider's own endpoints for each aggregator model the configuration names and report where the resolved model card disagrees — supported parameters, context length, modalities — with the override that would pin each.
/api/pulls.rebasePOSTpulls:writeRebase the pipeline's open pull requests (or one named) onto their bases: git alone first — the repository's own merge drivers, rerere, an unchanged patch carries its approval — then one bounded fix round for a conflict git leaves; one line per pull request.
/api/pulls.mergePOSTmerge:writeSquash-merge one approved pull request at exactly its reviewed head, under your own name: refused when no review approves the head, when a review there requests changes, when a check at it is red, still running or none has reported yet, and for the release pull request, which is answered with its card — that merge stays a person's click.
/api/pulls.enqueuePOSTmerge:writePut one approved pull request at exactly its reviewed head in the base branch's merge queue, recorded under your own name: refused when no review approves the head, when a review there requests changes, when a check at it is red, or when the head moves before enqueue, and for the release pull request, which is answered with its card — that merge stays a person's click; the queue runs the still-pending checks itself.
/api/plane.showGET, POSTruns:readWhat is happening: every live and recently ended run, every tracked pull request and every ship unit, each with its owner and its health — the plane's table, as text or JSON; nothing written.
/api/plane.stopPOSTruns:writeStop a pipeline and end its live children in one move: it starts no more units, its run is sealed, and every live child in its unit threads is aborted — recorded on the pipeline's run and the unit threads.

Screenshots ​

Residents index — every onboarded repo, its state, last activity; each row folds open to the runs on it and their worktrees:

Residents index

Resident detail — one repo's mirror, warm checkout, and per-thread worktrees:

Resident detail

What's not on the dashboard yet ​

Personal settings (config set me, mcp add for yourself) use the signed Access account's own scope and current grants. Email matches and explicit identity links do not borrow Slack Settings ownership; links change future conversation projection only. A service token has no human me scope. The org scope for agent, model and effort (config set org) is not offered here; Installation shows the defaults in force. The Vue frontend renders from the protected JSON seed and reads a fresh seed for in-app navigation.