HTTP API for claude-code-chat-browser. All /api/* routes return JSON unless noted. The bundled SPA at GET / is the primary client; these endpoints are also suitable for scripts and integrations on the same machine.
Base URL (default): http://127.0.0.1:5000
Source of truth for error codes: api/error_codes.py
Deprecation and removal: See deprecation-policy.md for how stability labels are applied and fields are removed. Field labels are defined in API field stability below.
Each response field below is labeled:
| Label | Meaning |
|---|---|
| stable | Will not be renamed or removed without a documented deprecation period |
| experimental | May change in any release; do not build long-lived integrations on these fields |
| deprecated | Still returned; use the documented replacement; removal announced in CHANGELOG |
Migration: Breaking changes use additive deprecation first (new field → deprecate old → remove after policy period). Versioned routes (e.g. /api/v2/...) are reserved for future breaking reshapes; none exist today.
None. The server binds to 127.0.0.1 by default and reads ~/.claude/projects/ as the local user. Do not expose it on a public network without adding authentication — there is no per-user authorization model.
Most /api/* error responses use this shape:
{
"error": "Human-readable message",
"code": "MACHINE_READABLE_CODE"
}All documented error paths below use the structured envelope.
Extra fields may appear for specific codes (for example since on invalid bulk-export mode).
| Field | Stability | Notes |
|---|---|---|
code |
Stable | UPPER_SNAKE_CASE string from ErrorCode enum |
error |
May be reworded | Kept for SPA compatibility |
| HTTP status | Stable per code | Use code + status together |
code |
HTTP | Routes | Meaning |
|---|---|---|---|
SEARCH_INVALID_LIMIT |
400 | GET /api/search |
Query param limit is not a positive integer |
INVALID_PATH |
400 | Session, stats, export session | Path traversal or rejected URL segment |
SESSION_NOT_FOUND |
404 | Session, stats, export session | File missing on disk or session excluded by rules |
INVALID_REQUEST_BODY |
400 | POST /api/export |
Body is not a JSON object |
INVALID_SINCE_MODE |
400 | POST /api/export |
since is not all, last, or incremental |
PARSE_ERROR |
500 | Session, stats, export session | JSONL file could not be parsed |
EXPORT_NOTHING_TO_EXPORT |
422 | POST /api/export |
No sessions matched the requested slice |
EXPORT_ALL_FAILED |
422 | POST /api/export |
At least one session was attempted but every candidate failed |
INTERNAL_ERROR |
500 | GET .../stats, export session |
Unexpected failure after parse (e.g. stats computation) |
5xx responses never include exception class names, tracebacks, or file paths. The body is always the generic message documented per route. Full exceptions are logged server-side via logger.exception. See tests/test_error_propagation.py (issue #25).
Sessions can be filtered by an exclusion rules file (default ~/.claude-code-chat-browser/exclusion-rules.txt, overridable with python app.py --exclude-rules PATH). Excluded sessions:
- Are omitted from
GET /api/projects/<name>/sessionsand search results - Return
404withSESSION_NOT_FOUNDon detail, stats, and per-session export routes
Grammar and matching: utils/exclusion_rules.py.
Source: app.py
Serves the single-page application shell (static/index.html). Hash-based client routing handles all UI navigation.
| Response | 200 — text/html |
| Errors | None |
curl -s http://127.0.0.1:5000/ -o /dev/null -w "%{http_code}\n"Source: api/projects.py
Lists every project directory under the Claude projects root that contains at least one .jsonl session file. Counts and last_modified reflect titled sessions only (via quick_session_info peek, not full parse).
None.
application/json — array of project objects:
| Field | Type | Stability | Description |
|---|---|---|---|
name |
string | stable | Directory name under ~/.claude/projects/ (e.g. F--boost-capy) |
path |
string | stable | Absolute path to project directory |
display_name |
string | stable | Friendly name derived from session cwd when available |
session_count |
integer | stable | Count of titled sessions (updated in handler) |
last_modified |
string (ISO 8601) | stable | Latest message timestamp across titled sessions |
[
{
"name": "F--boost-capy",
"path": "/home/user/.claude/projects/F--boost-capy",
"display_name": "Boost-capy",
"session_count": 12,
"last_modified": "2026-05-20T22:14:03+00:00"
}
]Empty projects root → [].
None.
curl -s http://127.0.0.1:5000/api/projects | jq '.[0]'Source: api/projects.py
Lists sessions in one project with summary fields for the workspace sidebar. Skips untitled sessions and sessions matched by exclusion rules.
| Name | Type | Description |
|---|---|---|
project_name |
string | Project directory name; must not contain .. |
application/json — array of session row objects:
| Field | Type | Stability | Description |
|---|---|---|---|
id |
string | stable | Session id (filename without .jsonl) |
path |
string | stable | Absolute path to JSONL file |
size_bytes |
integer | stable | File size |
modified |
number | stable | File mtime (epoch seconds) |
title |
string | stable | Parsed session title |
models |
string[] | stable | Models used in session |
tokens |
integer | stable | Sum of input + output tokens |
tool_calls |
integer | stable | Total tool calls |
first_timestamp |
string | null | stable | First message timestamp |
last_timestamp |
string | null | stable | Last message timestamp |
error |
boolean | stable | Optional; true if parse failed (card shows error state) |
| Status | code |
When |
|---|---|---|
| 400 | INVALID_PATH |
Invalid project_name (path escape) |
curl -s "http://127.0.0.1:5000/api/projects/F--boost-capy/sessions" | jq '.[0]'Source: api/sessions.py
Returns the full parsed session: title, metadata, and messages (including tool calls and thinking blocks).
| Name | Type | Description |
|---|---|---|
project_name |
string | Project directory name |
session_id |
string | JSONL basename without .jsonl extension |
application/json — session object:
| Top-level field | Type | Stability | Description |
|---|---|---|---|
session_id |
string | stable | Session identifier |
title |
string | stable | Inferred title from first human message |
messages |
array | stable | Ordered message objects (role, text/content, tool fields, etc.) |
metadata |
object | stable | Tokens, models, timestamps, file activity, tool counts, cwd, git_branch, … |
Nested keys inside messages[] and metadata follow the parser output; new parser fields may appear as experimental until listed here. See utils/jsonl_parser.py parse_session() for the full metadata shape.
| Status | code |
When |
|---|---|---|
| 400 | INVALID_PATH |
Path traversal in URL |
| 404 | SESSION_NOT_FOUND |
File missing or session excluded |
| 500 | PARSE_ERROR |
Malformed JSONL |
curl -s "http://127.0.0.1:5000/api/sessions/F--boost-capy/session_abc123" | jq '.title'Source: api/sessions.py
Computed aggregates for one session without returning the message list.
Same as session detail.
application/json — stats object from utils/session_stats.py compute_stats():
| Field | Type | Stability | Description |
|---|---|---|---|
files_touched |
object | stable | read, written, created, total_unique file lists |
commands_run |
array | stable | Bash commands with exit metadata |
urls_accessed |
string[] | stable | Web fetch URLs |
conversation_turns |
integer | stable | Human/assistant turn count |
wall_clock_seconds |
number | null | stable | Session duration |
wall_clock_display |
string | null | stable | Human-readable duration |
cost_estimate_usd |
number | stable | Best-effort USD estimate from token usage |
tool_result_summary |
object | stable | Aggregated tool result stats |
stop_reason_summary |
object | stable | Stop reason counts |
entry_type_counts |
object | stable | JSONL entry type counts |
sidechain_message_count |
integer | stable | Sidechain entries |
api_error_count |
integer | stable | API errors in session |
compaction_events |
array | stable | Context compaction markers |
| Status | code |
When |
|---|---|---|
| 400 | INVALID_PATH |
Path traversal |
| 404 | SESSION_NOT_FOUND |
File missing |
| 500 | PARSE_ERROR |
JSONL malformed |
| 500 | INTERNAL_ERROR |
compute_stats failed after successful parse |
curl -s "http://127.0.0.1:5000/api/sessions/F--boost-capy/session_abc123/stats" | jq '.cost_estimate_usd'Source: api/search.py
Case-insensitive substring search across all non-excluded messages in all projects. Linear scan — suitable for local history size, not indexed search.
| Name | Type | Default | Description |
|---|---|---|---|
q |
string | "" |
Search string; whitespace stripped; empty → [] |
limit |
integer | 50 |
Max results; must be ≥ 1; capped at 500 |
application/json — array of hit objects:
| Field | Type | Stability | Description |
|---|---|---|---|
project |
string | stable | Project name |
session_id |
string | stable | Session id |
title |
string | stable | Session title |
role |
string | stable | Message role (human, assistant, …) |
timestamp |
string | null | stable | Message timestamp |
snippet |
string | experimental | ~160 chars around match; length may change |
| Status | code |
When |
|---|---|---|
| 400 | SEARCH_INVALID_LIMIT |
limit not a positive integer (e.g. abc, 0, 1.5) |
curl -s "http://127.0.0.1:5000/api/search?q=parser&limit=10" | jq '.[0]'
curl -s "http://127.0.0.1:5000/api/search?q=test&limit=abc" # → 400Source: api/export_api.py
Read-only snapshot of bulk-export state persisted under ~/.claude-code-chat-browser/export-state.json.
| Field | Type | Stability | Description |
|---|---|---|---|
last_export_time |
string | null | stable | ISO timestamp of last completed bulk export |
last_export_session_count |
integer | stable | Sessions in last bulk export run |
{
"last_export_time": "2026-05-20T18:42:11.123456",
"last_export_session_count": 17
}None.
curl -s http://127.0.0.1:5000/api/export/state | jqSource: api/export_api.py
Bulk-export sessions as a zip of Markdown files (plus manifest.jsonl). Updates export state when at least one session is exported.
application/json
| Field | Type | Required | Values |
|---|---|---|---|
since |
string | no (default "all") |
all — every non-excluded titled session; last — latest UTC activity day; incremental — new/changed since last export |
application/zip with Content-Disposition: attachment
Filename pattern:
since |
Example filename |
|---|---|
all |
claude-code-export-2026-05-21.zip |
last |
claude-code-export-last-05-21-2026-05-21.zip |
incremental |
claude-code-export-incremental-2026-05-21.zip |
Zip contains Markdown per session and optional manifest.jsonl metadata.
When some sessions fail but at least one succeeds, the response is still 200 with the ZIP body (successful sessions only). Skipped sessions are surfaced two ways:
| Channel | When | Value |
|---|---|---|
X-Export-Warnings header |
Partial export (≥1 success, ≥1 failure) | JSON object: { "total_failures", "truncated", "failures" } where failures is a capped sample |
export-warnings.json in the ZIP |
Same | Full array of { "session_id", "code", "message" } |
message is a stable generic string per code (no exception text or paths). code uses the same strings as the error catalog (PARSE_ERROR, INTERNAL_ERROR, etc.).
| Status | code |
When | Extra fields |
|---|---|---|---|
| 400 | INVALID_REQUEST_BODY |
Body is not a JSON object | — |
| 400 | INVALID_SINCE_MODE |
Invalid since value |
since echoes rejected value |
| 422 | EXPORT_NOTHING_TO_EXPORT |
Zero sessions matched (none attempted) | since echoes request mode |
| 422 | EXPORT_ALL_FAILED |
Candidates existed but every attempted session failed | since, failures — flat array of {"session_id", "code", "message"} objects (same item shape as the failures array inside the X-Export-Warnings header) |
curl -X POST -H "Content-Type: application/json" \
-d '{"since":"last"}' \
-o export.zip \
http://127.0.0.1:5000/api/exportSource: api/export_api.py
Download one session as Markdown or JSON.
Same as session detail.
| Name | Type | Default | Description |
|---|---|---|---|
format |
string | md |
md — Markdown attachment; json — JSON attachment |
format |
Content-Type | Disposition |
|---|---|---|
md (default) |
text/markdown |
attachment; filename="<slug>.md" |
json |
application/json |
attachment; filename="<slug>.json" |
JSON body matches GET /api/sessions/.../ session object shape.
| Status | code |
When |
|---|---|---|
| 400 | INVALID_PATH |
Path traversal |
| 404 | SESSION_NOT_FOUND |
Missing or excluded |
| 500 | PARSE_ERROR |
JSONL malformed |
| 500 | INTERNAL_ERROR |
Stats/export pipeline failure |
curl -OJ "http://127.0.0.1:5000/api/export/session/F--boost-capy/session_abc123"
curl -OJ "http://127.0.0.1:5000/api/export/session/F--boost-capy/session_abc123?format=json"- Architecture overview
- Contributing
- README — CLI export and quick start