Skip to content

HTTP API

TTDash exposes a same-origin API below /api. It primarily serves the bundled dashboard, but authenticated clients can use it for local automation. Response fields can evolve across major releases.

Every API route requires authentication except POST /api/auth/session, which performs its own remote-token validation.

The startup summary’s local authentication URL contains a per-process ttdash_token. A browser exchanges it for an HttpOnly cookie. A scripted local client can extract the query value and send it as a bearer token while that process is running.

Send the configured master token with either header:

Authorization: Bearer <TTDASH_REMOTE_TOKEN>
X-TTDash-Remote-Token: <TTDASH_REMOTE_TOKEN>

Browser sign-in uses POST /api/auth/session to exchange the master token for an in-memory session represented by an HttpOnly cookie.

Unauthorized responses include status 401, WWW-Authenticate, and X-TTDash-Auth-Mode: local|remote. Remote session attempts are rate-limited; 429 includes Retry-After.

  • every request’s Host must satisfy the trusted-host policy
  • state-changing usage/settings/import routes and session creation require a trusted Origin matching Host for every client, including scripts
  • JSON usage and settings mutations require Content-Type: application/json
  • request bodies are limited to 10 MiB
  • errors are JSON objects such as { "message": "..." }
  • unsupported methods return 405; unknown API routes return 404
  • X-Forwarded-Host is not trusted
MethodPathSuccessful response
POST/api/auth/session204 and a browser session cookie
GET/api/usageNormalized usage object
DELETE/api/usage{ "success": true }
POST/api/uploadImported day count and total cost
POST/api/usage/importAdded, unchanged, conflict, skipped, and total counts
GET/api/systemsLocal hostname and imported-system summaries
DELETE/api/systemsDelete all additional systems
GET/api/systems/exportLocal-host system-transfer JSON download
POST/api/systems/import/previewValidated hostname, filename, and collision state
POST/api/systems/importImported or replaced system summary
DELETE/api/systems/:hostnameDelete one additional system
GET/api/settingsNormalized settings and runtime load status
PATCH/api/settingsUpdated normalized settings
DELETE/api/settingsSuccess flag and normalized defaults
POST/api/settings/importImported normalized settings
POST/api/auto-import/streamServer-Sent Events stream
GET/api/runtimeProcess runtime identity and listening metadata
GET/api/toktrack/version-statusPinned/latest toktrack version status
POST/api/report/pdfPDF download

Returns the aggregate normalized usage object plus a systems array containing the local dataset and every imported host. Daily rows with the same date and model are summed in the aggregate. With no local or imported data, daily and systems are empty and every total is zero. An unreadable local data.json returns 500; unreadable imported system files are skipped and reported through unreadableSystemFiles so readable usage stays available.

Terminal window
curl \
--header "Authorization: Bearer $TTDASH_REMOTE_TOKEN" \
http://127.0.0.1:3000/api/usage

Normalizes and replaces the complete stored dataset. Send one of the accepted input formats.

Terminal window
curl \
--request POST \
--header "Authorization: Bearer $TTDASH_REMOTE_TOKEN" \
--header "Origin: http://127.0.0.1:3000" \
--header "Content-Type: application/json" \
--data-binary @usage.json \
http://127.0.0.1:3000/api/upload

Success:

{ "days": 31, "totalCost": 42.75 }

Invalid JSON or an unsupported shape returns 400; a body above 10 MiB returns 413.

Conservatively merges a usage backup by valid calendar date. Existing conflicting days stay unchanged.

{
"importedDays": 12,
"addedDays": 8,
"unchangedDays": 2,
"conflictingDays": 1,
"conflictingDates": ["2026-07-20"],
"skippedDays": 1,
"totalDays": 90
}

Deletes data.json, every imported-system file below systems/, persisted system defaults, and usage load metadata. This is destructive and cannot be undone without a backup or the original system-transfer files.

System transfers keep usage from other hosts separate from the destination’s local dataset. All mutation requests use the same origin, authentication, JSON content-type, and request-size rules as other API imports.

Downloads a versioned ttdash-system-export envelope for local data.json. The Content-Disposition filename is ttdash-system-<hostname>.json and contains no timestamp. Imported systems are never included. A host without local usage receives 400.

Validates a system-transfer envelope without writing it:

{
"hostname": "workstation-b",
"filename": "ttdash-system-workstation-b.json",
"exists": true
}

The local hostname is rejected with 409. Unsupported envelopes or invalid hostnames return 400.

Stores the validated envelope under the deterministic filename. An existing hostname returns 409 and code SYSTEM_EXISTS. Append ?replace=1 only after an explicit replacement decision:

Terminal window
curl \
--request POST \
--header "Authorization: Bearer $TTDASH_REMOTE_TOKEN" \
--header "Origin: http://127.0.0.1:3000" \
--header "Content-Type: application/json" \
--data-binary @ttdash-system-workstation-b.json \
'http://127.0.0.1:3000/api/systems/import?replace=1'

Returns the destination hostname plus summaries for additional imported systems. The response includes each canonical hostname, deterministic filename, export timestamp, day count, and total cost; it does not repeat the complete daily datasets. Skipped corrupt files are listed separately in unreadableSystemFiles.

Deletes one additional system and removes it from persisted default filters. The local hostname is not an imported-system target.

Deletes all additional system files and clears all persisted system defaults while retaining local data.json.

Returns normalized persisted preferences plus the runtime-only cliAutoLoadActive flag. When no settings file exists, it returns normalized defaults.

Accepts any subset of these fields:

  • language: de or en
  • theme: dark or light
  • reducedMotionPreference: system, always, or never
  • providerLimits: provider-keyed subscription price and monthly limit objects
  • defaultFilters: view mode, date preset, systems, providers, and models
  • sectionVisibility: booleans keyed by dashboard section ID
  • sectionOrder: ordered dashboard section IDs
Terminal window
curl \
--request PATCH \
--header "Authorization: Bearer $TTDASH_REMOTE_TOKEN" \
--header "Origin: http://127.0.0.1:3000" \
--header "Content-Type: application/json" \
--data '{"language":"en","theme":"dark"}' \
http://127.0.0.1:3000/api/settings

Unknown or malformed values are normalized through the shared settings contract rather than persisted verbatim.

Requires a ttdash-settings-backup envelope, validates its settings payload, replaces persisted settings, and returns the normalized result.

Deletes settings.json, then returns:

{
"success": true,
"settings": {}
}

The real settings value contains the full normalized defaults; the abbreviated example is not a schema definition.

Starts the singleton toktrack import and returns text/event-stream with these named events:

EventPayload
checkRunner discovery/status object
progressStructured message key and variables
stderr{ "line": "..." }
success{ "days": number, "totalCost": number }
errorStructured error message key and variables
doneEmpty object; final event
Terminal window
curl --no-buffer \
--request POST \
--header "Authorization: Bearer $TTDASH_REMOTE_TOKEN" \
--header "Origin: http://127.0.0.1:3000" \
http://127.0.0.1:3000/api/auto-import/stream

Example frame:

event: progress
data: {"key":"loadingUsageData","vars":{"command":"toktrack daily --json"}}

A concurrent import returns 409 instead of starting another process. Closing the request aborts the running import.

Returns the composed process runtime snapshot:

{
"id": "runtime-id",
"mode": "foreground",
"port": 3000,
"url": "http://127.0.0.1:3000"
}

Checks the npm registry through a bounded, cached lookup:

{
"configuredVersion": "<pinned-toktrack-version>",
"latestVersion": "<latest-toktrack-version>",
"isLatest": true,
"lookupStatus": "ok"
}

On lookup failure, the successful payload can instead contain latestVersion: null, isLatest: null, a status such as timeout or failed, and a message.

Sends the selected dashboard state; the server reads persisted usage and returns an application/pdf attachment.

{
"viewMode": "daily",
"selectedMonth": null,
"selectedProviders": [],
"selectedModels": [],
"startDate": "2026-07-01",
"endDate": "2026-07-31",
"language": "en"
}

It returns 400 when no usage data is available, 503 when Typst is missing, and 500 for other rendering failures.

For safe network exposure, read Remote access and security.