Sessions timeline in a rolling window
VIEWER+. `?range=` matches the activity histogram (`6h` / `24h` / `7d`). Server computes `from = now - windowMs` itself — sending raw ISO from the client would shift the TanStack Query key on every render. Sessions overlap the window if they `startedAt ≤ to AND (endedAt ≥ from OR endedAt IS NULL)`.
/api/tracked-users/{userId}/timelineVIEWER+. ?range= matches the activity histogram (6h / 24h / 7d). Server computes from = now - windowMs itself — sending raw ISO from the client would shift the TanStack Query key on every render. Sessions overlap the window if they startedAt ≤ to AND (endedAt ≥ from OR endedAt IS NULL).
Path Parameters
TrackedUser id (cuid)
Query Parameters
"6h" | "24h" | "7d"Response Body
application/json
curl -X GET "https://loading/api/tracked-users/string/timeline"{
"range": "24h",
"from": "2026-04-25T10:30:00.000Z",
"to": "2026-04-26T10:30:00.000Z",
"sessions": [
{
"id": "ses_p1q2r3s4",
"externalId": "9b2e1d4c-6a3f-4f8e-9b1d-2c8a4e6f7a90",
"startedAt": "2026-04-26T10:30:00.000Z",
"endedAt": "2026-04-26T10:33:04.320Z",
"duration": 184320,
"url": "https://your-app.com/checkout",
"periods": [
{
"url": "https://your-app.com/checkout",
"pathname": "/checkout",
"startedAt": "2026-04-26T10:30:00.000Z",
"endedAt": "2026-04-26T10:31:18.420Z",
"duration": 78420
},
{
"url": "https://your-app.com/checkout/payment",
"pathname": "/checkout/payment",
"startedAt": "2026-04-26T10:31:18.420Z",
"endedAt": "2026-04-26T10:33:04.320Z",
"duration": 105900
}
]
}
],
"pages": [
"/checkout",
"/checkout/payment"
]
}Online-status heartbeat GET
VIEWER+. Lightweight — `MAX(Session.endedAt)` keyed on the indexed `trackedUserId`, no `Event` table scan. Online flag flips when the most recent session ended within `ONLINE_THRESHOLD_MS`. `Cache-Control: no-store` so polling sees fresh values.
KPI aggregate for the users list stats strip GET
VIEWER+. Four KPIs scoped to the caller's active org: total tracked users, online now, active in last 24 h, new this week. Single SQL round-trip via `$queryRaw` with conditional aggregation + a `LATERAL` join to resolve `MAX(Session.endedAt)` per user.