Skip to content

Across-session trends for the session's visible metrics

GET
/api/sessions/{id}/trends/
curl --request GET \
--url http://localhost:8000/api/sessions/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/trends/ \
--header 'Authorization: Bearer <token>'

Returns the across-session trajectory for every single-scalar metric the coach hasn’t hidden, in one call — keyed off the session’s OWN client + activity, filtered by the coach’s server-side curation (/api/me/hidden-metrics/). Powers the player Summary’s Current (delta vs prior + compact sparkline) and Dynamics (across-session chart) views and the PDF trend section, so all three read the same data.

Each metric carries THIS athlete’s resolved target band (the same one her session card, PDF and Solutions report show — domain.target_bands, #1317, not the MetricSpec population default) + higher_is_better (improvement tone, not raw direction) and a series of one point per completed session in the window — with delta_vs_prev, baseline and is_personal_best. Bilateral L/R pairs and the composite score are excluded (the player shows the pair card without a sparkline). metrics is empty when the session has no client, no in-window history, or every trendable metric is hidden. Window defaults to a year; narrow with ?range=30d or ?from=&to=.

id
required
string format: uuid
from
string format: date

Custom window start (YYYY-MM-DD); wins over range.

range
string

Preset window: one of 7d/14d/30d/90d/365d (default 365d). Mutually exclusive with from/to.

to
string format: date

Custom window end (YYYY-MM-DD); defaults to today.

tz
string

IANA timezone for day boundaries (UTC fallback).

Media typeapplication/json

Shape of GET /api/sessions/{id}/trends/ — across-session trajectories for the session’s visible metrics, in one call (#424).

object
session_id
required
string format: uuid
client_id
required

Null when the session has no client — metrics is then empty (no across-session history without a client).

string format: uuid
nullable
activity
required
  • running - Running
  • walking - Walking
  • jump - Vertical Jump
  • weightlifting - Weightlifting
  • mobility - Mobility Assessment
  • workspace_wellness - Workspace Wellness
  • agility - Agility
  • fencing - Fencing
  • cycling - Cycling
  • tennis - Tennis
  • padel - Padel Tennis
  • bowling - Bowling
  • baseball - Baseball
  • football - Football
  • badminton - Badminton
  • cricket - Cricket
  • basketball - Basketball
  • scuba_diving - Scuba Diving
  • ice_hockey - Ice Hockey
  • american_football - American Football
  • fri - Falls Risk Indicator
string
Allowed values: running walking jump weightlifting mobility workspace_wellness agility fencing cycling tennis padel bowling baseball football badminton cricket basketball scuba_diving ice_hockey american_football fri
exercise
required
string
range_days
required
integer
range_start
required
string format: date
range_end
required
string format: date
generated_at
required
string format: date-time
metrics
required
Array<object>

One visible metric’s across-session trajectory.

object
key
required
string
label
required
string
unit
required

Canonical Unit literal (see domain/metrics/base.py).

string
higher_is_better
required

Drives the delta tone — improvement, not raw direction. Null for a directionless metric.

boolean
nullable
target_min
required

Lower green bound, resolved for THIS athlete (domain.target_bands, #1317), mirroring the per-client trend. Null when no curated zone applies.

number format: double
nullable
target_max
required
number format: double
nullable
baseline
required

First recorded value in the window — the progress reference the compact sparkline + Dynamics chart both draw.

number format: double
nullable
personal_best
required
number format: double
nullable
series
required
Array<object>
object
session_id
required
string format: uuid
created_at
required
string format: date-time
value
required

Metric value at this session. Null when the analyzer didn’t emit it (e.g. front-only metric on a side-view session).

number format: double
nullable
score
required

Composite score for the session, when present.

number format: double
nullable
target_min
required

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.

number format: double
nullable
target_max
required
number format: double
nullable
in_zone
required

True / false / null. Null when value or zone is missing.

boolean
nullable
delta_vs_prev
required

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).

number format: double
nullable
is_personal_best
required

True when this value equals the window’s best (by higher_is_better) — drives the personal-best marker. #609 Phase 3.

boolean
Examples
Example200—Across-sessionTrendsForTheVisibleMetrics

One running session's curated metrics, each with its across-session trajectory

{
"session_id": "3f9a…",
"client_id": "c1a2…",
"activity": "running",
"exercise": "Treadmill Run",
"range_days": 365,
"range_start": "2025-06-24",
"range_end": "2026-06-24",
"generated_at": "2026-06-24T12:00:00Z",
"metrics": [
{
"key": "cadence",
"label": "Cadence",
"unit": "spm",
"higher_is_better": true,
"target_min": 165,
"target_max": 180,
"baseline": 151,
"personal_best": 168,
"series": [
{
"session_id": "a1…",
"created_at": "2026-03-04T09:00:00Z",
"value": 151,
"score": 72,
"target_min": 165,
"target_max": 180,
"in_zone": false,
"delta_vs_prev": null,
"is_personal_best": false
},
{
"session_id": "3f9a…",
"created_at": "2026-06-24T09:00:00Z",
"value": 168,
"score": 88,
"target_min": 165,
"target_max": 180,
"in_zone": true,
"delta_vs_prev": 4,
"is_personal_best": true
}
]
}
]
}
Media typeapplication/json
object
detail
required

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).

string
Examplegenerated
{
"detail": "example"
}
Media typeapplication/json
object
detail
required

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).

string
Examplegenerated
{
"detail": "example"
}
Media typeapplication/json
object
detail
required

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).

string
Examples
Example404—NoSuchSessionOnThisTeam

Absent, or owned by another team (collapsed to 404)

{
"detail": "session_not_found"
}