API
Dashboard endpoints live under /api/* and require a signed-in account
unless accounts are off (COSTPILOT_AUTH=off or demo mode). Admin endpoints
under /api/admin/* need the admin role. Ingestion and the agent config live
under /v1/* and are protected by COSTPILOT_INGEST_TOKEN. /health is
always open.
Dashboard
| Endpoint | Purpose |
|---|---|
GET /api/summary?from&to&filter=dim=value | KPIs (incl. turns and turnCostUsd, the spend of calls that belong to a turn), previous-period delta, cost per day (per hour for ranges up to two days), productivity |
GET /api/breakdown?by=<dim>&… | Cost per dimension value (project, issue, actor, actor_type, provider, model, source, repo, session, turn or any metadata key); rows carry sessions, turns and turnCostUsd, actorType (human/agent) for by=actor, and startedAt for by=session and by=turn. The older user dimension still works |
GET /api/timeseries?by=<dim>&… | Cost per day, stacked (top 6 + Other, flagged other: true); per hour for ranges up to two days (bucket field). by defaults to model |
GET /api/records?limit&offset&… | Individual calls (activity feed); limit defaults to 50, max 500 |
GET /api/activity?tz&… | Active time per day (from claude_code.active_time.total) and a weekday × hour punchcard of LLM calls; tz = minutes as JS getTimezoneOffset() reports |
GET /api/tools?from&to | Tool usage (calls, errors, avg. duration) incl. MCP server rollup, from the tools' tool-result events (Claude Code, Gemini CLI, Codex CLI); Claude Code MCP server names require OTEL_LOG_TOOL_DETAILS=1 |
GET /api/alerts · POST /api/alerts · DELETE /api/alerts/:id | Budget alerts: threshold per scope (total or dimension+value) over a rolling window (7/30/90 days); status ok/warning (from 80 %)/breached is computed live. Admin only for writes |
GET /api/analysis/issues?from&to&project&calc | Analysis tab: every issue with spend in the range (in project, or all projects incl. issues without one) with cost, turns and the spend that arose in turns; calc=whole (default) counts everything ever recorded for an issue, calc=range only the range. Plus the group sums and the spend without an issue |
GET /api/dimensions · GET /api/meta | Dimension list, app info (version, demo flag, branding, record count and data range) |
Date ranges and filters
from/to take ISO datetimes or YYYY-MM-DD (UTC day; to is exclusive,
a bare date means end of that day). The dashboard maps its presets (today,
7/30/90 days, this/last month, all time) and the custom picker onto these.
Filters are repeatable:
GET /api/breakdown?by=model&filter=issue=FC-101&filter=actor=alice
An empty value (filter=issue=) selects the "none" bucket.
Budget alert body
{
"name": "Platform squad, monthly",
"dimension": "project",
"value": "PLAT",
"thresholdUsd": 1500,
"days": 30
}
dimension and value are optional; without them the alert covers total
spend. days is 7, 30 or 90.
Accounts
| Endpoint | Purpose |
|---|---|
GET /api/session | Public: whether accounts are on, whether first-run setup is pending, and the signed-in user |
POST /api/setup | Public, once: creates the first admin (needs the setup code from the server log) |
GET /api/invites/:token · POST /api/invites/:token/accept | Public: invite details; accepting creates the account and signs it in |
/api/auth/* | Better Auth: sign-in/out, password change, user management (/api/auth/admin/*) |
Admin
| Endpoint | Purpose |
|---|---|
GET /api/admin/projects · POST /api/admin/projects · PUT /api/admin/projects/:id | Project registry (name, key, issue key pattern, repositories); 409 when a key or repository is taken |
GET /api/admin/agent-settings · PUT /api/admin/agent-settings | Central agent setup (tools, logToolDetails, default issue key pattern) |
PUT /api/admin/branding | Instance branding { name?, color?: "#rrggbb", logo?: data URL } (png/jpeg/webp/svg up to ~256 KB); empty fields reset to default; 403 in demo mode |
GET /api/admin/invites · POST /api/admin/invites · DELETE /api/admin/invites/:id | Pending invites; POST returns the single-use link path |
Ingestion and agent
| Endpoint | Purpose |
|---|---|
POST /v1/logs | OTLP logs (Claude Code api_request and tool events, Gemini CLI, Codex CLI) |
POST /v1/metrics | OTLP metrics (productivity counters; cumulative payloads are diffed) |
POST /v1/traces | Accepted and discarded, so exporters do not log errors |
POST /v1/session-map | Prompt and turn-end reports from the agent hook, see Wire contract |
GET /v1/agent-config | Central agent config: setup, all projects with pattern and repositories, defaultIssueKeyPattern, turnEndReports: true |
GET /v1/projects/:projectKey | A single project in the same shape; 404 for an unknown key |
OTLP payloads are JSON (OTEL_EXPORTER_OTLP_PROTOCOL=http/json), optionally
gzip-encoded. The ingestion is wire-compatible with the FeatureCost PoC
receiver, so existing telemetry setups keep working unchanged.
Health
GET /health returns 200 and is excluded from auth, for platform
healthchecks.