Skip to content

Core concepts

Six objects carry almost everything. Understanding how they relate is the difference between calling endpoints and building an integration.

One video, analysed. The central object. A session is created against an (activity, exercise) pair, optionally attached to a Client, and moves through pending → processing → completed | failed.

A session owns its results: metrics, metric_targets, the annotated video at analyzed_url, a thumbnail, and capture_findings — quality warnings about the footage itself.

Creating a session is what costs quota. Not uploading, not polling.

Who the movement belongs to. A Client carries a profile: height, weight, gender, leg length. Those are not decoration. They feed the analysers directly:

  • Anything mass-dependent — ground reaction force, power — is computed from the athlete’s mass. Without it, those metrics are suppressed rather than guessed.
  • Distances in the frame are converted to real units using the athlete’s height. Without it, the analyser falls back to an assumed torso length, which carries roughly ±20 % scale error.
  • Green zones shift by gender and body size.

Attaching a Client is optional and almost always worth it. It is also what makes a sequence of sessions into a history rather than a pile of clips.

What movement was performed. activity is a family — running, weightlifting, mobility. exercise is the specific movement within it — Treadmill, Back Squat, Overhead Squat.

Together they select the analyser. They are exact-match strings, not free text: "Overhead Squat" works, "overhead squat" does not. There are 21 activities and 107 pairs. Read them from the catalogue at runtime rather than hardcoding — see Activities and exercises.

Which plane the movement was filmed fromside, front or back. This is not a label; it picks a different analyser.

The sagittal (side) analyser computes stride, ground contact, speed and sagittal joint angles. The frontal analyser computes pelvic drop, hip adduction, step width and left/right asymmetry. Send the wrong one and you get a technically complete report about the wrong thing.

Each (activity, exercise) pair supports a specific set of views. Sending an unsupported one is rejected at create time rather than producing an empty report.

One measurement. A { key, value_num, value_json } triple.

value_num is a scalar in SI units — centimetres, metres per second, milliseconds, degrees. value_json carries anything that is not a single number: a time series, a 2D trajectory, a categorical reading.

There are 620 distinct metric keys across the platform, but any one session emits only the ones its (activity, exercise) pair defines — typically 10–40.

What “good” means for this athlete. For each metric, a min_good / max_good band, already resolved for the athlete’s gender and body size.

This is why metric_targets is a separate object from metrics: the measurement is a fact, the target is a judgement, and the judgement moves with who is being measured. Rendering a value without its target tells a coach a number; rendering both tells them something actionable.

Several sessions rolled into one report. A Solution is a named protocol — a running gait assessment, a jump battery — that binds specific sessions to specific slots and produces a combined report with its own insights.

Most integrations never touch Solutions. If yours produces multi-test assessments, they are the object you want.


flowchart TB
    Client["Client<br/>the athlete"]
    Session["Session<br/>one video"]
    Metric["Metric ×N<br/>value_num | value_json"]
    Target["MetricTarget ×N<br/>the good range, per athlete"]
    Findings["capture_findings<br/>'this footage has a problem'"]
    Video["analyzed_url<br/>the annotated video"]
    Solution["Solution<br/>a protocol"]

    Client -->|"has many"| Session
    Session --> Metric
    Session --> Target
    Session --> Findings
    Session --> Video
    Solution -->|"binds several"| Session

Everything hangs off a team: an API key belongs to one, and sees exactly that team’s data.