API Reference: Tracking Endpoints
Session lifecycle and activity-ingest endpoints: start-session, track-tick, end-session, get-active-session, link-commit, and verify-api-key.
These endpoints power the tracker clients: they open and close sessions, ingest activity ticks, and link commits to tracked work. All of them authenticate with the x-devclocked-key header (see the API overview) and require a key with the ingest scope — the default for keys created in Settings. Base URL: https://api.devclocked.com/functions/v1.
POST /start-session
Opens a tracked session, or returns the existing active session for the same workspace. Idempotent in practice: trackers call it freely on editor focus.
| Field | Type | Notes |
|---|---|---|
source | string | Required. Client identifier, e.g. vscode, chrome, desktop, cursor-plugin |
client_id | string | Required. Stable per-device identifier chosen by the client |
repo_url | string | Optional git remote URL |
repository_full_name | string | Optional owner/name |
branch | string | Optional current branch |
project_name | string | Optional human project name |
project_id | string (uuid) | Optional known DevClocked project id |
device_name | string | Optional display name for the device |
workspace_fingerprint | string | Optional stable hash of the workspace; lets multiple clients share one session |
workspace_path | string | Optional absolute workspace path |
Response 200:
{
"session_id": "0b6f6c3e-9a71-4a86-b9ed-2f4c11f0a915",
"started_at": "2026-08-09T09:14:02.318Z",
"id": "0b6f6c3e-9a71-4a86-b9ed-2f4c11f0a915"
}
Errors: 400 missing required fields, 401, 403 missing ingest scope, 413, 429, 500.
POST /track-tick
Ingests a batch of activity ticks into the caller's active session. API-key only — this endpoint does not accept browser session tokens. Max 200 ticks per request. An optional x-devclocked-source header labels the batch (vscode, chrome, desktop, cli, daemon, cursor-plugin, claude-plugin).
| Field | Type | Notes |
|---|---|---|
ticks | array | Required. 1–200 tick objects (below) |
session_id | string (uuid) | Optional; when omitted the active session is resolved automatically |
workspace_fingerprint | string | Optional workspace hash |
is_catchup | boolean | Optional; marks a delayed flush of buffered ticks |
agent_runtime_ms | number | Optional measured AI-agent runtime for the batch |
agent_turn_count | number | Optional agent turn count |
agent_measurement_quality | string | Optional: none, estimated, or precise |
agent_turn_events | array | Optional per-turn intervals: request_key, started_at, ended_at, duration_ms, is_sidechain, model, measurement_quality. Max 500 |
Each tick object:
| Field | Type | Notes |
|---|---|---|
entity | string | Required. File path, URL, or window title being worked on |
entity_type | string | Required. file, url, or window |
timestamp | string | Required. ISO-8601 time of the activity |
repo_url / branch / project_name / language | string | Optional context for attribution |
is_write | boolean | Optional; true when the tick represents an edit rather than a read |
code_lines_added / code_lines_deleted / files_changed | number | Optional churn counters |
workspace_fingerprint / workspace_path | string | Optional workspace identity |
activity_context | object | Optional editor/AI-tool signals used for activity classification: debug and refactor flags, file type, and an ai_tool object (tool name, activity type, model, token usage). See the OpenAPI spec for the full shape |
Response 200:
{ "processed_count": 42, "session_updated": true }
processed_count reflects ticks accepted after de-duplication and junk filtering; it is 0 when no session could be attached. Errors: 400 missing/oversized ticks, 401, 403, 413, 429, 500.
POST /end-session
Closes a session. Idempotent: ending an already-ended or unknown session still returns 200.
| Field | Type | Notes |
|---|---|---|
session_id | string (uuid) | Optional; when omitted, the most recent active session is ended |
Response 200:
{
"session_id": "0b6f6c3e-9a71-4a86-b9ed-2f4c11f0a915",
"ended_at": "2026-08-09T11:02:47.901Z",
"duration_seconds": 6525
}
If another tracker source is still actively ticking the same session, the API keeps it alive instead and returns:
{
"session_id": "0b6f6c3e-9a71-4a86-b9ed-2f4c11f0a915",
"kept_alive": true,
"reason": "other_source_active",
"active_source": "desktop"
}
Errors: 400 no session id and no active session, 401, 403, 413, 429, 500.
GET /get-active-session
Returns the caller's currently active tracked session, or null when nothing has ticked recently. Optional query parameter workspace_fingerprint filters to a specific workspace.
Response 200:
{
"session": {
"id": "0b6f6c3e-9a71-4a86-b9ed-2f4c11f0a915",
"started_at": "2026-08-09T09:14:02.318Z",
"ended_at": null,
"is_active": true,
"source": "vscode",
"workspace_fingerprint": "a41c9be27d0f",
"tick_count": 184
}
}
The session object carries the full session record; treat unlisted fields as informational and subject to extension. Errors: 401, 500.
POST /link-commit
Links a local commit to tracked time. If session_id is omitted, the API matches an active session on the same repository within a small time window, or records the commit against a new closed session.
| Field | Type | Notes |
|---|---|---|
sha | string | Required. Commit SHA |
repo | string | Required. Repository name or remote URL |
committed_at | string | Required. ISO-8601 commit time |
session_id | string (uuid) | Optional explicit session to link against |
Response 200:
{
"linked": true,
"session_id": "0b6f6c3e-9a71-4a86-b9ed-2f4c11f0a915",
"message": "Commit successfully linked to session"
}
Errors: 400 missing fields, 401, 403, 404 session not found or not yours, 500.
POST /verify-api-key
Validates an API key before a client stores it. The key travels in the body — no auth header on this one endpoint. Responses always include an ok boolean.
POST /functions/v1/verify-api-key
{ "key": "dck_your_api_key" }
Response 200:
{
"ok": true,
"user_id": "3f7a2c91-55d4-4d6e-8b21-c09e7d64a1b8",
"email": "dev@example.com"
}
Errors: 400 {"ok": false, "error": "Invalid API key format"}, 401 {"ok": false, "error": "Invalid API key"}, 429, 500.
Was this page helpful?