Skip to content

List preset exercises, recording guides, and metric specs

GET
/api/exercises/
curl --request GET \
--url http://localhost:8000/api/exercises/ \
--header 'Authorization: Bearer <token>'

The catalogue endpoint. Pass ?activity=running (or any supported activity) to receive the curated preset list, the activity-level recording guide (camera placement, framing, lighting), per-exercise overrides where they exist (use guides_by_exercise[exercise] if present, fall back to guide), the list of MetricSpec dicts the analyzers will emit, and the supported camera views per exercise. Use this to drive a dynamic upload UI — exercise picker, recording instructions, view picker (per pair, not per activity), and a preview of which metrics this session will produce. ?lang=en|ru localises the recording guide text.

Omit activity and the response carries by_activity — every activity mapped to its preset list — with presets / metrics / views_by_exercise left empty. That is the shape a picker grouping exercises under activity headings needs, and it costs one request instead of one per activity.

Both shapes are filtered by the caller’s exercise grants: a team on a per-movement contract sees only the movements it was sold (an activity with none of them drops out of by_activity), and POST /api/sessions/ answers 402 exercise_not_in_plan for anything outside that list. Unrestricted teams — every team without a grant list — get the whole catalogue.

activity
string

Activity to fetch the catalogue for. OMIT it to get the whole catalogue under by_activity instead — one request for a picker that groups exercises by activity (#1354).

lang
string

en (default) or ru — localises the recording guide text.

Media typeapplication/json
object
activity
required
string
presets
required
Array<string>
by_activity
required

Activity → its preset list, the WHOLE catalogue. Populated only when the request omits activity; empty otherwise. Drives the journal’s exercise filter, which groups its options under activity headings and would otherwise need one request per activity to build a single dropdown (#1354).

object
key
additional properties
Array<string>
guide
required

Activity-level default recording guide.

object
key
additional properties
string
guides_by_exercise
required

Per-exercise recording guide. Keyed by exercise name. Falls back to guide when an exercise has no override. Front-ends should prefer this if present.

object
key
additional properties
object
key
additional properties
string
metrics
required

Exercise → list of metric specs

object
key
additional properties
Array<object>
object
key
required
string
label
required
string
unit
required

Unit of value_num (SI; see the metric dictionary).

string
higher_is_better
required

null = depends on the goal.

boolean
nullable
min_good
required
number format: double
nullable
max_good
required
number format: double
nullable
amber_low
required

Lower edge of the outside-normal, not-high-risk band.

number format: double
nullable
amber_high
required
number format: double
nullable
has_series
required
boolean
description
required
string
beta
required

Not yet through clinical or force-plate validation.

boolean
exclude_from_score
required

Shown, but not part of the composite technique score.

boolean
default_visible
required

In the movement’s default visible set — the selection the player’s metric picker opens on before a team curates anything (#2248). The set lives in domain/metrics/defaults.py; a metric outside it is still offered in the picker, just not pre-selected.

boolean
mandatory
required

The team may not switch this metric off. The picker locks the row and the server strips the key from any write that tries anyway.

boolean
frontal_only
required

Only exists on a front/back clip — on the side view the analyzer emits nothing and no card is produced. Pair it with the session’s camera_view to explain a card the recording could never have carried.

boolean
views_by_exercise
required

Per-exercise supported camera views — one of side (sagittal, always present), front, back. Front-end uses this to gate the camera-view picker per (activity, exercise) pair: hide when the list has only one entry, show otherwise. Source of truth is aikmodels’ views_for_pair() — adding a frontal/posterior analyzer in aikmodels flows through here without a Django-side edit.

object
key
additional properties
Array<string>
target_scope_by_exercise
required

The unilateral tests of the activity (#2354), keyed by exercise: {mode, joints_from_session, joints}. mode is limb (only the tested side is reported and scored) or label (the side names the report); joints_from_session means the coach picks the target joints (Joint Angle Mobility). An exercise absent from the map has no tested side — the upload flow shows no side control for it.

object
key
additional properties
object
key
additional properties
max_clip_duration_s
required

Longest clip — or trim window — POST /api/sessions/ accepts for this activity, in seconds; 0 means no limit. Tighter where the activity’s pose work runs on CPU, and routing differs per deployment, so read it here rather than hard-coding it. Without activity, the general ceiling — the most any movement allows; ask per activity for the one that applies. A longer clip is refused with 400 clip_too_long and the same number in max_duration_s.

integer
max_clip_duration_s_by_exercise
required

The same ceiling per preset exercise. Differs from max_clip_duration_s only where an exercise routes on another activity’s backend (fri): prefer it when the exercise is known.

object
key
additional properties
integer
guides_by_exercise_view
required

Per-(exercise, camera_view) recording guide. Nested {exercise: {view: {Camera, Distance, Duration, ...}}}. Front-end shows guides_by_exercise_view[exercise][view] once the user has committed to both — gives a front-camera Bodyweight Squat its own framing recipe instead of the side-view default. Falls back to guides_by_exercise[exercise] (then guide) when a view-specific override doesn’t exist for that pair.

object
key
additional properties
object
key
additional properties
object
key
additional properties
string

Example generated

{
"activity": "example",
"presets": [
"example"
],
"by_activity": {
"additionalProperty": [
"example"
]
},
"guide": {
"additionalProperty": "example"
},
"guides_by_exercise": {
"additionalProperty": {
"additionalProperty": "example"
}
},
"metrics": {
"additionalProperty": [
{
"key": "example",
"label": "example",
"unit": "example",
"higher_is_better": true,
"min_good": 1,
"max_good": 1,
"amber_low": 1,
"amber_high": 1,
"has_series": true,
"description": "example",
"beta": true,
"exclude_from_score": true,
"default_visible": true,
"mandatory": true,
"frontal_only": true
}
]
},
"views_by_exercise": {
"additionalProperty": [
"example"
]
},
"target_scope_by_exercise": {
"additionalProperty": {
"additionalProperty": "example"
}
},
"max_clip_duration_s": 1,
"max_clip_duration_s_by_exercise": {
"additionalProperty": 1
},
"guides_by_exercise_view": {
"additionalProperty": {
"additionalProperty": {
"additionalProperty": "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

Example generated

{
"detail": "example"
}