CClawMetryDocs

Data & storage

Query API#

The read surface is a declared contract called q/1. It is not a loose collection of endpoints: every shape, its arguments, its defaults and its limits are registered, and the HTTP API, the daemon proxy, the MCP server and the cloud relay all serve the same registry.

That is why an agent asking over MCP and a script calling curl get identical rows.

Evolution rule#

Inside q/1, evolution is additive only. New shapes and new optional arguments may appear. Renaming or removing a shape, an argument, or a response field requires a new contract version.

Live shapes#

ShapeArgumentsReturns
sessionsagent_id, since, until, limit (100, max 2000)One row per session with start/end, event count, cost
eventssession_id, agent_id, event_type, since, until, limit (200, max 5000)Raw event rows, newest first
transcriptsession_id required, limit (500, max 5000)Events scoped to one session
aggregatesagent_id, since, untilPer-day rollup of events, tokens and cost
healthStore health: engine, size, ring depth, flush age
spanstrace_id, session_id, agent_type, since, until, limit (200, max 2000)OTel span rows
tracessession_id, agent_type, since, until, limit (100, max 1000)One row per trace with aggregate span stats
external_callssession_id, since, until, limit (200, max 2000)Non-LLM API calls captured by the interceptor
modelsruntime, since, until, limit (1000, max 10000)Per-model daily token and cost rollup
runtimessince, until, limit (1000, max 10000)Per-runtime daily activity and cost rollup
rollup_sessionsruntime, limit (200, max 2000)Materialised per-session summary: title, status, totals, stuck flag
searchq required, model, status, since, until, limit (50, max 500)Full-text search over session titles and eval reasons
agent_graphsince, until, limit (500, max 2000)Spawn graph: nodes plus spawn edges

Arguments are strictly coerced: anything not in a shape's allowed-key set is dropped, and limits are clamped to their range rather than erroring. An agent asking for a million events gets the cap.

Trust classes#

Every shape is classified, and the classification governs what may leave the machine in plaintext:

ClassMeaning
plaintextAggregate counters or metadata the server may see in cleartext
e2eSession and content-bearing payloads. These only ever leave the machine AES-256-GCM encrypted, and must never appear on a plaintext push list.

aggregates, health, models, runtimes and agent_graph are plaintext. Everything content-bearing — sessions, events, transcript, spans, traces, external_calls, rollup_sessions, search — is e2e.

End-to-end encryption

Calling it — HTTP#

Convenience routes:

bash
curl -s 'localhost:8900/api/local/sessions?limit=20'          | jq '.rows'
curl -s 'localhost:8900/api/local/events?event_type=error'    | jq '.rows'
curl -s 'localhost:8900/api/local/transcript/<session_id>'    | jq '.rows'
curl -s  localhost:8900/api/local/aggregates                  | jq '.rows'
curl -s  localhost:8900/api/local/health                      | jq
curl -s 'localhost:8900/api/local/spans?session_id=<sid>'     | jq '.rows'
curl -s 'localhost:8900/api/local/traces?limit=20'            | jq '.rows'
curl -s 'localhost:8900/api/local/external-calls'             | jq '.rows'
curl -s 'localhost:8900/api/local/search?q=migration'         | jq '.rows'
curl -s 'localhost:8900/api/local/agent-graph'                | jq

The generic form, which reaches every shape:

bash
curl -s localhost:8900/api/local/query -X POST \
  -H 'content-type: application/json' \
  -d '{"shape":"rollup_sessions","args":{"runtime":"claude_code","limit":50}}' | jq

An unknown shape returns 400 with the list of allowed shapes, which makes the API self-describing:

bash
curl -s localhost:8900/api/local/query -X POST \
  -H 'content-type: application/json' -d '{"shape":"nope"}' | jq '.allowed_shapes'

Calling it — the daemon directly#

The dashboard is a convenience. The daemon serves the same contract:

bash
PORT=$(jq -r .port  ~/.clawmetry/local_query.json)
TOK=$(jq -r .token ~/.clawmetry/local_query.json)

curl -s "http://127.0.0.1:$PORT/api/local/query" \
  -H "Authorization: Bearer $TOK" \
  -H 'content-type: application/json' \
  -d '{"shape":"sessions","args":{"limit":5}}' | jq

This works with no dashboard running at all, which makes it the right path for a monitoring script.

Calling it — MCP#

Five of these shapes are exposed as MCP tools so an agent can query them directly. → MCP tool reference

Planned shapes#

The registry also declares shapes that are targets rather than served surfaces: usage, session, brain, approvals and glance. A planned shape is a declared intent; shipping one flips its registry entry, and a drift test enforces both directions so the documentation cannot claim a method the code does not serve.

Practical recipes#

bash
# yesterday's errors, grouped by tool
curl -s 'localhost:8900/api/local/events?event_type=tool_result&limit=5000' \
  | jq '[.rows[] | select(.data.is_error) | .tool_name] | group_by(.) | map({tool: .[0], n: length}) | sort_by(-.n)'

# the ten most expensive sessions this month
curl -s localhost:8900/api/local/query -X POST -H 'content-type: application/json' \
  -d '{"shape":"rollup_sessions","args":{"limit":2000}}' \
  | jq '[.rows[] | select(.cost_usd != null)] | sort_by(-.cost_usd) | .[:10] | map({title, cost_usd, runtime})'

# per-runtime cost for the last 30 days
curl -s localhost:8900/api/local/runtimes | jq \
  '[.rows[]] | group_by(.runtime) | map({runtime: .[0].runtime, cost: (map(.cost_usd) | add)})'
Cookie preferences