HTTP API reference
StackUnderflow API Reference
Section titled “StackUnderflow API Reference”The REST API is served by stackunderflow init on http://localhost:8081 by default. Interactive Swagger docs
(auto-generated by FastAPI) are available at /docs. This file is the human-written companion — it explains
intent, request/response shapes, and status codes in plain language. All endpoints return JSON. Errors use
the shape {"error": "<message>"} with an appropriate non-2xx status code.
Endpoint Overview
Section titled “Endpoint Overview”| Method | Path | Group |
|---|---|---|
| GET | /api/project | Projects |
| POST | /api/project | Projects |
| POST | /api/project-by-dir | Projects |
| GET | /api/projects | Projects |
| GET | /api/recent-projects | Projects |
| GET | /api/global-stats | Projects |
| GET | /api/stats | Dashboard data |
| GET | /api/dashboard-data | Dashboard data |
| GET | /api/messages | Dashboard data |
| GET | /api/messages/summary | Dashboard data |
| POST | /api/refresh | Dashboard data |
| GET | /api/jsonl-files | Sessions |
| GET | /api/jsonl-content | Sessions |
| GET | /api/search | Search |
| POST | /api/search/reindex | Search |
| GET | /api/search/stats | Search |
| GET | /api/qa | Q&A |
| GET | /api/qa/{id} | Q&A |
| POST | /api/qa/reindex | Q&A |
| GET | /api/qa/stats | Q&A |
| GET | /api/tags | Tags |
| GET | /api/tags/browse/{tag} | Tags |
| GET | /api/tags/session/{id} | Tags |
| POST | /api/tags/session/{id} | Tags |
| DELETE | /api/tags/session/{id}/{tag} | Tags |
| POST | /api/tags/reindex | Tags |
| GET | /api/bookmarks | Bookmarks |
| POST | /api/bookmarks | Bookmarks |
| GET | /api/bookmarks/{id} | Bookmarks |
| PUT | /api/bookmarks/{id} | Bookmarks |
| DELETE | /api/bookmarks/{id} | Bookmarks |
| POST | /api/bookmarks/toggle | Bookmarks |
| GET | /api/bookmarks/session/{id} | Bookmarks |
| GET | /api/health | Misc |
| GET | /api/pricing | Misc |
| POST | /api/pricing/refresh | Misc |
Projects / Lifecycle
Section titled “Projects / Lifecycle”GET /api/project
Section titled “GET /api/project”Returns the currently selected project. When no project has been set, status is "no_project".
Response (no project selected)
{"status": "no_project", "message": "No project selected"}Response (project active)
{ "status": "active", "project_path": "Users/yadkonrad/dev/myproject", "log_path": "/Users/yadkonrad/.claude/projects/-Users-yadkonrad-dev-myproject", "log_dir_name": "-Users-yadkonrad-dev-myproject"}Status codes: 200 always.
POST /api/project
Section titled “POST /api/project”Set the active project by filesystem path. The server locates the Claude log directory automatically.
Request body
{"project_path": "/Users/yadkonrad/dev/myproject"}Response
{ "status": "success", "project_path": "/Users/yadkonrad/dev/myproject", "log_path": "/Users/yadkonrad/.claude/projects/-Users-yadkonrad-dev-myproject", "message": "Project set successfully. You can now view the dashboard."}Status codes: 200 success; 400 missing or non-existent project_path;
404 project path exists but no Claude logs were found there.
POST /api/project-by-dir
Section titled “POST /api/project-by-dir”Set the active project using the Claude log directory slug (the ~/.claude/projects/<slug> folder name).
This is the endpoint the React UI calls when the user picks a project from the sidebar.
Request body
{"dir_name": "-Users-yadkonrad-dev-dev-year26-jan26-StackUnderflow"}Response
{ "status": "success", "project_path": "Users/yadkonrad/dev/dev/year26/jan26/StackUnderflow", "log_path": "/Users/yadkonrad/.claude/projects/-Users-yadkonrad-dev-dev-year26-jan26-StackUnderflow", "log_dir_name": "-Users-yadkonrad-dev-dev-year26-jan26-StackUnderflow", "message": "Now analyzing logs from: -Users-yadkonrad-dev-dev-year26-jan26-StackUnderflow"}Status codes: 200 success; 400 missing dir_name or path traversal attempt;
404 directory not found or contains no .jsonl files.
GET /api/projects
Section titled “GET /api/projects”List all known projects from the session store with metadata. Supports sorting and pagination.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
include_stats | bool | false | Include per-project statistics (slower) |
sort_by | string | last_modified | One of last_modified, first_seen, size, name |
limit | int | none | Max results to return |
offset | int | 0 | Skip this many results (for pagination) |
Response
{ "projects": [ { "dir_name": "-Users-yadkonrad-dev-myproject", "log_path": "/Users/yadkonrad/.claude/projects/-Users-yadkonrad-dev-myproject", "file_count": 0, "total_size_mb": 0.0, "last_modified": 1776649168.04, "first_seen": 1772585609.12, "display_name": "-Users-yadkonrad-dev-myproject", "in_cache": false, "url_slug": "-Users-yadkonrad-dev-myproject", "stats": null } ], "total_count": 143, "has_more": true, "cache_status": {"cached_count": 0, "total_projects": 143}}Status codes: 200 always (errors return 500 with {"error": "..."}).
GET /api/recent-projects
Section titled “GET /api/recent-projects”Shorthand for the most recent 20 projects — equivalent to /api/projects?sort_by=last_modified&limit=20
but with a simplified response shape.
Response
{ "projects": [ { "dir_name": "-Users-yadkonrad-dev-myproject", "log_path": "", "last_modified": 1776649168.04, "file_count": 0 } ]}Status codes: 200 always.
GET /api/global-stats
Section titled “GET /api/global-stats”Aggregated statistics across all projects in the store. This is the only dashboard endpoint that does not require a project to be selected. The Overview page calls this endpoint exclusively.
See the Data Shapes Appendix for the full field reference.
Response (abbreviated)
{ "first_use_date": "2025-11-29", "last_use_date": "2026-04-20", "daily_token_usage": [{"date": "2025-11-29", "input": 0, "output": 0}], "daily_costs": [{"date": "2025-11-29", "cost": 0.0, "by_model": {}}], "models": { "claude-opus-4-6": {"count": 57584, "cost": 29402.30} }, "total_cache_read_tokens": 16328970052, "total_cache_write_tokens": 1126449344, "config": {"max_date_range_days": 30}}Status codes: 200 success; 500 on database error.
Dashboard Data
Section titled “Dashboard Data”Project scoping:
/api/stats,/api/dashboard-data,/api/messages, and/api/messages/summaryall act on the current project — the one most recently set viaPOST /api/project-by-dirorPOST /api/project. If no project has been selected they return400 {"error": "No project selected"}./api/global-stats(above) is the only aggregate endpoint that does not require a project.POST /api/refreshrefreshes the current project when one is selected; if no project is set it refreshes all projects instead.
GET /api/stats
Section titled “GET /api/stats”Full statistics object for the current project, computed via the pipeline
(classifier → enricher → aggregator).
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
timezone_offset | int | 0 | Minutes offset from UTC for daily bucketing |
Response — the top-level statistics dict (same as the statistics key in /api/dashboard-data).
Key nested sections include overview, tools, sessions, daily_usage, models, costs.
{ "overview": { "project_name": "StackUnderflow", "log_dir_name": "-Users-yadkonrad-dev-dev-year26-jan26-StackUnderflow", "total_messages": 11341, "date_range": {"start": "2026-01-30T20:58:11.193Z", "end": "2026-04-20T01:39:11.887Z"}, "sessions": 20, "message_types": {"user": 4381, "assistant": 6960}, "total_tokens": { "input": 600390, "output": 3030862, "cache_creation": 86450124, "cache_read": 1466551362 }, "total_cost": 3767.61 }, "tools": { "usage_counts": {"Bash": 1296, "Read": 700, "Edit": 531}, "error_counts": {}, "error_rates": {"Bash": 0.0} }}Status codes: 200 success; 400 no project selected; 404 project not in store (run /api/refresh).
GET /api/dashboard-data
Section titled “GET /api/dashboard-data”Optimised single call for the initial dashboard load. Returns statistics plus the first page of messages in a single round-trip.
Query parameters — same timezone_offset as /api/stats.
Response
{ "statistics": {"overview": {"...": "..."}, "tools": {"...": "..."}}, "messages_page": { "messages": [{"...": "..."}], "page": 1, "per_page": 50, "total": 11341 }, "message_count": 11341, "is_reindexing": false, "config": { "messages_initial_load": 50, "max_date_range_days": 30 }}Status codes: 200 success; 400 no project; 404 project not in store.
GET /api/messages
Section titled “GET /api/messages”Return pipeline-formatted messages for the current project.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
limit | int | none | Cap the number of messages returned |
timezone_offset | int | 0 | UTC offset in minutes |
Response — a JSON array of message objects.
[ { "session_id": "020a13e5-ad60-41e5-9313-7cdf03cecf26", "role": "user", "content": "What does this function do?", "timestamp": "2026-01-30T20:58:11.193Z", "model": null, "input_tokens": 0, "output_tokens": 0 }]Status codes: 200 success; 400 no project; 404 project not in store.
GET /api/messages/summary
Section titled “GET /api/messages/summary”Lightweight summary of the current project’s messages — counts and model breakdown without loading the full message list.
Response
{ "total": 11341, "by_type": {"user": 4381, "assistant": 6960}, "by_model": { "N/A": 4381, "claude-opus-4-6": 3566, "claude-opus-4-7": 1455 }, "total_tokens": 3631252}Status codes: 200 success; 400 no project; 404 project not in store.
POST /api/refresh
Section titled “POST /api/refresh”Runs an incremental ingest pass and updates the store. If a project is active, only that project is re-ingested. If no project is selected, all projects are refreshed.
Request body — any JSON object (can be {}); the body is forwarded to the ingest layer but not
currently validated.
Response (project active)
{ "status": "success", "message": "Files changed - data refreshed successfully", "files_changed": true, "message_count": 12, "refresh_time_ms": 340}Response (no project — all projects)
{ "status": "success", "message": "Ingested 42 new records", "files_changed": true, "refresh_time_ms": 1820, "projects_refreshed": 42, "total_projects": 42}Status codes: 200 always (errors are surfaced inside the JSON body or as 500).
Sessions
Section titled “Sessions”GET /api/jsonl-files
Section titled “GET /api/jsonl-files”List every session (JSONL file) for the current project, with per-session metadata and cost estimates.
Accepts an optional project query parameter to override the current project.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project | string | current project | Log directory slug to query |
Response — array of session objects, sorted by created timestamp ascending.
[ { "name": "020a13e5-ad60-41e5-9313-7cdf03cecf26.jsonl", "path": "020a13e5-ad60-41e5-9313-7cdf03cecf26.jsonl", "is_subagent": false, "created": 1738274291.193, "modified": 1738274291.193, "size": 0, "messages": 120, "user_messages": 40, "assistant_messages": 80, "input_tokens": 24000, "output_tokens": 96000, "model": "claude-opus-4-6", "title": "What does this function do?", "tool_calls": 15, "estimated_cost": 0.4812 }]Status codes: 200 success; 400 no project; 500 internal error.
GET /api/jsonl-content
Section titled “GET /api/jsonl-content”Return the raw parsed messages for a single session, identified by filename.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
file | string | yes | Filename, e.g. 020a13e5-....jsonl |
project | string | no | Log dir slug (defaults to current project) |
Response
{ "lines": [{"type": "user", "message": {"role": "user", "content": "..."}}], "total_lines": 120, "user_count": 40, "assistant_count": 80, "metadata": { "session_id": "020a13e5-ad60-41e5-9313-7cdf03cecf26", "file_size": 0, "created": 1738274291.193, "modified": 1745116760.887, "first_timestamp": "2026-01-30T20:58:11.193Z", "last_timestamp": "2026-04-20T01:39:20.887Z", "duration_minutes": 117.6, "cwd": "/Users/yadkonrad/dev/myproject" }}Status codes: 200 success; 400 no project or invalid file param;
404 project or session not found in store; 500 internal error.
Search
Section titled “Search”GET /api/search
Section titled “GET /api/search”Full-text search across every indexed session from every configured adapter (Claude Code today), using SQLite FTS5.
Returns 503 if the search service failed to initialise on startup.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
q | string | "" | Search query |
project | string | none | Filter to a specific project slug |
date_from | string | none | ISO date YYYY-MM-DD (inclusive lower bound) |
date_to | string | none | ISO date YYYY-MM-DD (inclusive upper bound) |
model | string | none | Filter by model name |
role | string | none | Filter by role (user or assistant) |
page | int | 1 | Result page (1-indexed) |
per_page | int | 20 | Results per page (max 100) |
Response
{ "results": [ { "id": 238980, "session_id": "7f72e05c-93a2-4221-9a3b-ce5648e0b433", "project": "-Users-yadkonrad-dev-myproject", "role": "assistant", "content": "Here is the refactored version...", "timestamp": "2026-03-23T20:58:44.155Z", "model": "claude-opus-4-6", "tokens_input": 1, "tokens_output": 176, "snippet": "...Here is the <mark>refactored</mark> version..." } ], "total": 233, "page": 1, "per_page": 20, "total_pages": 12, "query": "refactor"}Status codes: 200 success; 503 search service unavailable; 500 query error.
POST /api/search/reindex
Section titled “POST /api/search/reindex”Rebuild the full-text search index from all project data in the store. This is a blocking operation.
Request body — empty object {}.
Response
{ "projects_indexed": 98, "total_messages_indexed": 82759, "elapsed_ms": 4230.5}Status codes: 200 success; 503 search service unavailable; 500 reindex error.
GET /api/search/stats
Section titled “GET /api/search/stats”Return metadata about the search index — total message count, known models, and a per-project index log.
Response
{ "total_messages": 82759, "total_projects": 98, "models": ["claude-opus-4-6", "claude-sonnet-4-6", "claude-opus-4-7"], "indexed_projects": [ { "project": "-Users-yadkonrad-dev-myproject", "indexed_at": "2026-04-02T02:48:28.332578+00:00", "message_count": 13525 } ]}Status codes: 200 success; 503 search service unavailable; 500 error.
GET /api/qa
Section titled “GET /api/qa”List extracted Q&A pairs with filtering and pagination. A Q&A pair is a (user question, assistant answer)
tuple extracted from sessions. Returns 503 if the Q&A service failed to initialise.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project | string | none | Filter to a specific project slug |
date_from | string | none | ISO date YYYY-MM-DD |
date_to | string | none | ISO date YYYY-MM-DD |
search | string | none | Text filter applied to question and answer text |
resolution_status | string | none | One of resolved, looped, open |
page | int | 1 | Page number (1-indexed) |
per_page | int | 20 | Results per page (max 100) |
Response
{ "results": [ { "id": "edaf6f427ec0a5de", "session_id": "14b76974-d5c3-4430-b7e6-db7513bb1499", "project": "-Users-yadkonrad-dev-myproject", "question_text": "help me find this chrome tab plugin...", "answer_text": "Let me search for the ObserverTab extension...", "code_snippets": [], "tools_used": [], "timestamp": "2026-04-01T00:07:45.083Z", "model": "claude-opus-4-6", "num_attempts": 1, "resolution_status": "open", "loop_count": 0 } ], "total": 4986, "page": 1, "per_page": 20, "total_pages": 250}Status codes: 200 success; 503 Q&A service unavailable; 500 error.
GET /api/qa/{qa_id}
Section titled “GET /api/qa/{qa_id}”Fetch a single Q&A pair by its hex ID.
Path parameter: qa_id — the hex string ID from the id field in /api/qa results.
Response — same shape as a single element from /api/qa’s results array.
Status codes: 200 success; 404 pair not found; 503 service unavailable; 500 error.
POST /api/qa/reindex
Section titled “POST /api/qa/reindex”Rebuild the Q&A index by re-extracting pairs from all sessions in the store.
Request body — empty object {}.
Response
{ "projects_indexed": 98, "total_qa_indexed": 4986, "elapsed_ms": 8120.3}Status codes: 200 success; 503 service unavailable; 500 error.
GET /api/qa/stats
Section titled “GET /api/qa/stats”Return aggregate statistics about the Q&A index, including a per-project breakdown.
Response (abbreviated)
{ "total_pairs": 4986, "by_project": [ {"project": "-Users-yadkonrad-dev-myproject", "count": 1310} ]}Status codes: 200 success; 503 service unavailable; 500 error.
GET /api/tags
Section titled “GET /api/tags”Return the full tag cloud — all tags with their session counts, category, and display colour.
Returns 503 if the tag service failed to initialise.
Response
{ "tags": [ {"name": "database", "count": 130, "category": "topic", "color": "#dd6b20"}, {"name": "python", "count": 78, "category": "language", "color": "#3572A5"}, {"name": "Bash", "count": 101, "category": "tool", "color": "#718096"} ], "total_sessions": 144}Tag category values observed: topic, language, framework, tool.
Status codes: 200 success; 503 service unavailable; 500 error.
GET /api/tags/browse/{tag}
Section titled “GET /api/tags/browse/{tag}”List every session that carries a given tag.
Path parameter: tag — the tag name (e.g. python, debugging).
Response
{ "tag": "python", "sessions": [ {"session_id": "020a13e5-...", "project": "-Users-yadkonrad-dev-myproject"} ], "count": 78}Status codes: 200 success; 503 service unavailable; 500 error.
GET /api/tags/session/{session_id}
Section titled “GET /api/tags/session/{session_id}”Return all tags attached to a specific session.
Path parameter: session_id — UUID of the session.
Response — illustrative; shape determined by tag service (source: routes/tags.py:28)
["python", "debugging", "fastapi"]Status codes: 200 success; 503 service unavailable; 500 error.
POST /api/tags/session/{session_id}
Section titled “POST /api/tags/session/{session_id}”Manually add a tag to a session.
Path parameter: session_id — UUID of the session.
Request body
{"tag": "my-custom-tag"}Response — illustrative; shape determined by tag service (source: routes/tags.py:55)
{"status": "added", "tag": "my-custom-tag", "session_id": "020a13e5-..."}Status codes: 200 success; 400 missing tag; 503 service unavailable; 500 error.
DELETE /api/tags/session/{session_id}/{tag}
Section titled “DELETE /api/tags/session/{session_id}/{tag}”Remove a manually added tag from a session.
Path parameters: session_id (UUID), tag (tag name).
Response — illustrative (source: routes/tags.py:63)
{"status": "removed", "tag": "my-custom-tag", "session_id": "020a13e5-..."}Status codes: 200 success; 503 service unavailable; 500 error.
POST /api/tags/reindex
Section titled “POST /api/tags/reindex”Rebuild auto-tags for all sessions across all projects.
Request body — empty object {}.
Response
{ "projects_indexed": 98, "total_sessions_tagged": 144, "elapsed_ms": 3100.8}Status codes: 200 success; 503 service unavailable; 500 error.
Bookmarks
Section titled “Bookmarks”GET /api/bookmarks
Section titled “GET /api/bookmarks”List all bookmarks, optionally filtered by tag and sorted.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
tag | string | none | Filter to bookmarks carrying this tag |
sort_by | string | created_at | Sort field |
Response
{ "bookmarks": [ { "id": "bm_abc123", "session_id": "020a13e5-...", "title": "Great refactor session", "notes": "Shows the clean pipeline pattern", "tags": ["python", "refactoring"], "message_index": 42, "created_at": "2026-03-01T12:00:00Z", "session_first_ts": "2026-03-01T09:00:00Z", "session_last_ts": "2026-03-01T11:55:00Z", "session_message_count": 120 } ]}The session_first_ts, session_last_ts, and session_message_count fields are enriched from the session
store and may be absent if the session is not in the store.
Status codes: 200 success; 503 service unavailable; 500 error.
POST /api/bookmarks
Section titled “POST /api/bookmarks”Create a new bookmark.
Request body
{ "session_id": "020a13e5-ad60-41e5-9313-7cdf03cecf26", "title": "Great refactor session", "message_index": 42, "notes": "Shows the clean pipeline pattern", "tags": ["python", "refactoring"]}session_id is required. All other fields are optional (title defaults to "Untitled bookmark").
Response — the created bookmark object (same shape as one element in GET /api/bookmarks).
Status codes: 201 created; 400 missing session_id; 503 service unavailable; 500 error.
GET /api/bookmarks/{bookmark_id}
Section titled “GET /api/bookmarks/{bookmark_id}”Fetch a single bookmark by ID. Illustrative — behaviour delegated to the bookmark service
(source: routes/bookmarks.py).
Status codes: 200 success; 404 not found; 503 service unavailable; 500 error.
PUT /api/bookmarks/{bookmark_id}
Section titled “PUT /api/bookmarks/{bookmark_id}”Update a bookmark’s title, notes, and/or tags.
Path parameter: bookmark_id — the bookmark’s ID string.
Request body — all fields optional; only provided fields are updated.
{ "title": "Updated title", "notes": "New notes", "tags": ["python"]}Response — the updated bookmark object.
Status codes: 200 success; 404 not found; 503 service unavailable; 500 error.
DELETE /api/bookmarks/{bookmark_id}
Section titled “DELETE /api/bookmarks/{bookmark_id}”Remove a bookmark by ID.
Response
{"status": "success", "message": "Bookmark removed"}Status codes: 200 success; 404 not found; 503 service unavailable; 500 error.
POST /api/bookmarks/toggle
Section titled “POST /api/bookmarks/toggle”Add a bookmark if the session is not already bookmarked, or remove it if it is. Useful for a single-click “star” button.
Request body
{ "session_id": "020a13e5-ad60-41e5-9313-7cdf03cecf26", "title": "Interesting session", "message_index": 0}session_id is required. title and message_index are optional.
Response — illustrative (source: routes/bookmarks.py:161)
{"action": "added", "bookmark": {"id": "bm_abc123", "session_id": "020a13e5-..."}}or {"action": "removed", "bookmark_id": "bm_abc123"} when the bookmark existed.
Status codes: 200 success; 400 missing session_id; 503 service unavailable; 500 error.
GET /api/bookmarks/session/{session_id}
Section titled “GET /api/bookmarks/session/{session_id}”List all bookmarks attached to a specific session.
Path parameter: session_id — UUID of the session.
Response
{"bookmarks": [{"id": "bm_abc123", "title": "Great session", "...": "..."}]}Status codes: 200 success; 503 service unavailable; 500 error.
GET /api/health
Section titled “GET /api/health”Liveness and readiness check. Reports whether each optional service initialised successfully.
Response
{ "status": "ok", "services": { "search": true, "tags": true, "qa": true, "bookmarks": true, "pricing": true }}false for a service means it failed to start (the corresponding /api/<service>/* endpoints will
return 503).
Status codes: 200 always.
GET /api/pricing
Section titled “GET /api/pricing”Return the current per-model pricing table. Data is fetched from LiteLLM and cached locally.
Response
{ "pricing": { "claude-opus-4-6": { "input_cost_per_token": 1.5e-05, "output_cost_per_token": 7.5e-05, "cache_creation_cost_per_token": 1.875e-05, "cache_read_cost_per_token": 1.5e-06 } }, "source": "litellm", "timestamp": "2026-04-20T01:39:47.194182+00:00", "is_stale": false}Status codes: 200 success; 503 service unavailable; 500 error.
POST /api/pricing/refresh
Section titled “POST /api/pricing/refresh”Force a re-fetch of pricing data from LiteLLM, bypassing the local cache.
Request body — empty object {}.
Response
{"status": "success", "message": "Pricing updated successfully"}Status codes: 200 success; 500 fetch failed or service error; 503 service unavailable.
Data Shapes Appendix
Section titled “Data Shapes Appendix”Global Stats shape (GET /api/global-stats)
Section titled “Global Stats shape (GET /api/global-stats)”This endpoint is the primary data source for the Overview page. The authoritative implementation is in
stackunderflow/store/queries.py:get_global_stats(). The config key is appended by the route handler.
| Field | Type | Description |
|---|---|---|
first_use_date | string | ISO date YYYY-MM-DD of the earliest message in the store |
last_use_date | string | ISO date YYYY-MM-DD of the most recent message |
daily_token_usage | array | Per-day {date, input, output} token totals across all projects |
daily_costs | array | Per-day {date, cost, by_model} cost rollup; by_model maps model name to cost float |
models | object | Map of model name to {count, cost} across all time |
total_cache_read_tokens | int | Sum of cache_read_tokens across every message in the store |
total_cache_write_tokens | int | Sum of cache_create_tokens across every message in the store |
config | object | Server config values surfaced to the UI; currently {max_date_range_days} |
daily_token_usage element
{"date": "2026-03-23", "input": 24000, "output": 96000}daily_costs element
{ "date": "2026-03-23", "cost": 1.44, "by_model": { "claude-opus-4-6": 1.20, "claude-sonnet-4-6": 0.24 }}models entry
{ "claude-opus-4-6": {"count": 57584, "cost": 29402.30}}count is the number of messages attributed to the model; cost is the cumulative USD cost computed
using the pricing table.