Local API and Report Routes#
Decant exposes two report endpoints — one API route (downloads a file) and one UI route (renders a preview in the React app) for each report type . All routes are served by decant serve on http://127.0.0.1:3000 by default .
Route Summary#
| Route | Type | Purpose |
|---|---|---|
GET /api/reports/analytics.html | API | Download self-contained analytics HTML report |
GET /api/reports/session/{id}.html | API | Download self-contained session HTML report |
GET /reports/analytics | UI | Print-preview of analytics report in the app |
GET /reports/session/:id | UI | Print-preview of session report in the app |
The UI routes (/reports/*) are mapped to the React bundle and render a light, print-ready preview with Back, Download HTML, and Save as PDF controls. The API routes (/api/reports/*.html) return the same HTML directly as a file attachment .
Query Parameters and Validation#
GET /api/reports/analytics.html accepts optional from and to query parameters :
- Format:
YYYY-MM-DD - Both are optional; omitting them returns an unfiltered report
- Parsed by
dateFilterFromSearch(), which discards any value that doesn't match the ISO date pattern or represents an invalid calendar date - The resulting
DateFilteris applied viasessionDatePredicate(), which compares the first 10 characters ofstarted_atagainst the bounds using>=/<=
GET /api/reports/session/{id}.html takes a numeric session ID in the URL path . The server validates it with isValidSessionId() and returns 400 invalid_session_id for non-integer or negative values . A valid but non-existent ID returns 404 .
Response Format and Headers#
Both API routes return text/html with :
Content-Disposition: attachment; filename="decant-analytics-report.html"(analytics) ordecant-session-{id}-{slug}.html(session)Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; font-src data:; frame-ancestors 'none'- Zero external dependencies — no JavaScript, no remote resources; charts are inline SVG
Session report filenames slugify the session title to at most 64 ASCII characters .
Report Data Pipeline#
Each report type is assembled then rendered in two distinct steps:
Analytics report :
assembleAnalyticsReport(db, { filter })— queries totals, sessions-by-day, by-model, by-project, activity, token economics, and insights; omits insights when a date filter is activerenderAnalyticsReport(data)— renders static markup viarenderToStaticMarkup()
Session report :
assembleSessionReport(db, id)— queries session summary, reply count, tool calls, context window timeline, token economics, tool stats, and hot files; loads at most 1 message to intentionally omit transcript contentrenderSessionReport(data)— renders static markup
The AnalyticsReportData and SessionReportData interfaces define the full shape passed between assembly and rendering.
Key Source Files#
| File | Role |
|---|---|
src/server.ts (lines 581–611, 1077–1078, 1787–1807) | Route handlers, reportHtmlResponse(), UI route registration |
src/date-filter.ts | dateFilterFromSearch(), sessionDatePredicate(), ISO date validation |
src/report/data.ts | assembleAnalyticsReport(), assembleSessionReport(), data interfaces |
src/report/render.tsx | renderAnalyticsReport(), renderSessionReport() |
docs/api/openapi.yaml (lines 786–844) | Authoritative parameter and response schema for both API routes |
The full OpenAPI contract is also available at runtime via GET /api/openapi.json .