Trends around a released session
const url = 'http://localhost:8000/api/portal/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/sessions/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/trends/';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url http://localhost:8000/api/portal/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/sessions/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/trends/ \ --header 'Authorization: Bearer <token>'The coach’s session trends, over the card’s RELEASED sessions only and without the team’s hidden metrics. ?range= or ?from=&to=, as on the coach’s route; a year by default.
The signed-in person’s grant on the card in the path. Every route answers 404 while client access is switched off on the environment. 404 card_not_found for a card the account never held — another athlete’s, a card of the account’s own team, or an id that names nothing. 403 access_ended (reason: revoked · team_plan · minor) for a card it held and can no longer open, consent_required when the consent texts changed since the person accepted them (consent again with POST /api/portal/{client_id}/consent/). A partner API key gets 403. 429 past the account’s rate (client_portal, 120/min; the PDFs client_portal_pdf, 20/min).
Only what a coach released is visible, curated on the server: the team’s hidden metrics are absent, the coach’s AI text and internal fields are not sent, and media URLs are signed for two hours.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Query Parameters
Section titled “Query Parameters”First day, inclusive (YYYY-MM-DD).
Last day, inclusive (YYYY-MM-DD).
The reader’s IANA zone for the days.
Responses
Section titled “Responses”Shape of GET /api/sessions/{id}/trends/ — across-session
trajectories for the session’s visible metrics, in one call (#424).
object
Null when the session has no client — metrics is then empty (no across-session history without a client).
running- Runningwalking- Walkingjump- Vertical Jumpweightlifting- Weightliftingmobility- Mobility Assessmentworkspace_wellness- Workspace Wellnessagility- Agilityfencing- Fencingcycling- Cyclingtennis- Tennispadel- Padel Tennisbowling- Bowlingbaseball- Baseballfootball- Footballbadminton- Badmintoncricket- Cricketbasketball- Basketballscuba_diving- Scuba Divingice_hockey- Ice Hockeyamerican_football- American Footballfri- Falls Risk Indicatorpickleball- Pickleball
One visible metric’s across-session trajectory.
object
Canonical Unit literal (see domain/metrics/base.py).
Drives the delta tone — improvement, not raw direction. Null for a directionless metric.
Lower green bound, resolved for THIS athlete (domain.target_bands, #1317), mirroring the per-client trend. Null when no curated zone applies.
First recorded value in the window — the progress reference the compact sparkline + Dynamics chart both draw.
object
Metric value at this session. Null when the analyzer didn’t emit it (e.g. front-only metric on a side-view session).
Composite score for the session, when present.
Lower green bound, resolved for THIS athlete (domain.target_bands, #1317) — the same band the session card, the PDF and the Solutions report show, not the MetricSpec population default. Null when no curated zone applies.
True / false / null. Null when value or zone is missing.
This value minus the previous recorded (non-null) value in the window — null for the first reading or a null value. The per-session Δ. #609 Phase 3 (shared with #424).
True when this value equals the window’s best (by higher_is_better) — drives the personal-best marker. #609 Phase 3.
False when the session was analysed under an earlier definition of its exercise’s metrics (domain/metric_versions.py, #2297). Drawn, but left out of delta_vs_prev, the baseline and the personal best — its number measured something else. Re-analysing the session makes it comparable.
On a non-comparable point: the key of what changed in the exercise’s current definition (clientTrend.definitionChange.<key> in the web locales), so the chart can say WHY the session is not compared. Null when comparable or when the change has no note (#1818).
Examples
{ "session_id": "5e2b9c41-7d3a-4f80-9b6e-1c0d2e3f4a51", "client_id": "a1f1c0a2-0000-4000-8000-0000000000aa", "activity": "running", "exercise": "Treadmill", "range_days": 365, "range_start": "2025-11-03", "range_end": "2026-11-02", "generated_at": "2026-11-02T10:20:00Z", "metrics": []}object
Human-readable message, or a stable machine code for the cases a client branches on. The standard envelope for 400 (validation — a field-keyed object may appear instead), 401 (missing / invalid credentials), 403 (authenticated but not permitted), and 404 (absent — cross-team records are collapsed to 404 so the API never leaks the existence of another team’s data).
Example generated
{ "detail": "example"}object
Human-readable message, or a stable machine code for the cases a client branches on. The standard envelope for 400 (validation — a field-keyed object may appear instead), 401 (missing / invalid credentials), 403 (authenticated but not permitted), and 404 (absent — cross-team records are collapsed to 404 so the API never leaks the existence of another team’s data).
Example generated
{ "detail": "example"}object
access_ended, consent_required, or DRF’s message for a partner API key.
With access_ended: why the card no longer opens — revoked, team_plan or minor.
With consent_required: the version to accept.
With consent_required: the texts to show.
Examples
403 — access ended
The card no longer opens; the page says why and names nothing else of it.
{ "detail": "access_ended", "reason": "revoked", "client_name": "Sofia Lebedeva", "team_name": "North Track Academy"}403 — consent required
{ "detail": "consent_required", "consent_version": "2026-10-29-draft", "kinds": [ "terms", "privacy", "processing", "guardian_declaration" ]}object
Human-readable message, or a stable machine code for the cases a client branches on. The standard envelope for 400 (validation — a field-keyed object may appear instead), 401 (missing / invalid credentials), 403 (authenticated but not permitted), and 404 (absent — cross-team records are collapsed to 404 so the API never leaks the existence of another team’s data).
Examples
404 — not a card of this account
{ "detail": "card_not_found"}404 — not a released session of this card
{ "detail": "session_not_found"}object
429 — throttled: this account’s (or address’s) rate for the route is used up; try again after wait seconds, which Retry-After repeats. A route with a limit of its own answers its own code instead, documented there.
With throttled: whole seconds until the next request can pass.
Example generated
{ "detail": "example", "wait": 1}