List preset exercises, recording guides, and metric specs
const url = 'http://localhost:8000/api/exercises/';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/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.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Query Parameters
Section titled “Query Parameters”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).
en (default) or ru — localises the recording guide text.
Responses
Section titled “Responses”object
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
Activity-level default recording guide.
object
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
object
Exercise → list of metric specs
object
object
Unit of value_num (SI; see the metric dictionary).
null = depends on the goal.
Lower edge of the outside-normal, not-high-risk band.
Not yet through clinical or force-plate validation.
Shown, but not part of the composite technique score.
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.
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.
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.
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
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
object
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.
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
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
object
object
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" } } }}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"}