API Reference
Authentication, activity ingestion, category overrides, extension remote control, focus sessions and settings.
All routes are prefixed with https://api.timelens.app/api. Every route except /auth/* and the health checks requires a bearer token.
Authentication
Authorization: Bearer <accessToken>
Content-Type: application/jsonThe desktop app and the extension both authenticate through the same token service. Tokens are account scoped, so a request only ever reads or writes the authenticated user's own rows.
Authentication routes
| Method | Path | Description |
|---|---|---|
| POST | /auth/register | Create an account |
| POST | /auth/verify-email | Confirm an email address with the code sent at registration |
| POST | /auth/login | Email and password sign-in |
| POST | /auth/logout | Invalidate the current session |
| POST | /auth/oauth | Sign in with Google or GitHub |
| POST | /auth/forgot-password | Request a password reset |
| POST | /auth/reset-password | Complete a password reset |
| POST | /auth/desktop-register | Register from the desktop app |
| POST | /auth/desktop-verify-email | Verify the email used by the desktop app |
| POST | /auth/desktop-resend-code | Resend a verification code |
| POST | /auth/extension-login | Exchange extension credentials for a token |
| POST | /auth/extension-oauth | OAuth sign-in originating from the extension |
| GET | /auth/me | Current user profile |
| PATCH | /auth/me | Update the current user profile |
Activity
| Method | Path | Description |
|---|---|---|
| POST | /activities | Submit a completed activity block |
| GET | /activities | List activities in a range with filters |
| GET | /activities/summary | Totals and aggregates for the dashboard |
| PUT | /activities/override | Reclassify a website for this user only |
| DELETE | /activities/override | Remove a user classification override |
/activitiesSubmit a completed activity block. Accepts desktop application activity or browser domain activity.
Request
{
"source": "desktop",
"appName": "Visual Studio Code",
"startedAt": "2026-01-14T09:00:00.000Z",
"endedAt": "2026-01-14T09:42:00.000Z",
"durationMinutes": 42
}Response
{
"success": true,
"activity": { "id": "clx...", "category": "Development" }
}/activitiesBrowser activity uses a domain instead of an application name. The extension resolves the category locally and the server applies any per-user override on top.
Request
{
"source": "browser",
"domain": "github.com",
"title": "Pull requests",
"startedAt": "2026-01-14T09:42:00.000Z",
"endedAt": "2026-01-14T10:00:00.000Z",
"durationMinutes": 18
}Extension remote control
The desktop app can pause tracking remotely. The extension heartbeats so the dashboard knows it is alive, and polls a pending control command that it then acknowledges.
| Method | Path | Description |
|---|---|---|
| POST | /activities/heartbeat | Extension liveness signal |
| GET | /activities/extension-status | Read the last heartbeat and control state |
| PUT | /activities/extension-control | Set tracking state for the extension |
| GET | /activities/extension-control/pending | Poll for a control command |
| POST | /activities/extension-control/ack | Acknowledge a command |
Focus sessions and settings
| Method | Path | Description |
|---|---|---|
| POST | /focus-sessions | Record a completed or elapsed focus session |
| GET | /settings | Read focus durations and tracking preferences |
| PUT | /settings | Update focus durations and tracking preferences |
/focus-sessionsA focus session contributes to Focused Time only. Total and Productive time are derived from activity, not from this payload.
Request
{
"mode": "pomodoro",
"plannedMinutes": 25,
"elapsedMinutes": 25,
"completedAt": "2026-01-14T10:15:00.000Z"
}Health
| Method | Path | Description |
|---|---|---|
| GET | /health | Liveness, no authentication |
| GET | /db-test | Round-trips a query to verify database connectivity |
Error format
{ "error": "Invalid credentials.", "field": "email" }