A short guide for anyone integrating with the Plyomat Open API. It covers the two ways a coach can capture data, why display_mode is not the test name, what each measurement mode actually records, and how to handle a null assessment_id.
There are two ways a coach can run and save a test, and both save into a session.
The coach picks a mode (Vertical, RSI, Drop Jump, PPS, and so on), picks an athlete, and saves. This is normal training or a quick check. No formal protocol is enforced.
The coach picks a named assessment (Standing Vertical Jump, 10/5 RSI, SL Vertical Jump, and so on), picks an athlete, and saves. Rep count, kept reps, sidedness, and start position are fixed by the protocol, so the test stays standardized and repeatable.
Every set carries a session_id either way, so session_id is not how you tell them apart. The single field that distinguishes the two paths is assessment_id.
display_mode is the measurement mode: what the mat measured and which metric is primary.
The test name lives on the assessment.
A set carries an assessment_id. Resolve it to get the real protocol:
set.assessment_id → GET /v1/assessments → { name, display_mode, sidedness,
rep_count, reps_kept, drop_height_cm }Why it matters: display_mode: "rsi" is a family, not a test. A 10-rep bilateral rebound protocol and a single-leg 5-hop both report rsi, but they are completely different assessments and should never land in the same bucket.
Map on assessment.name for the test. Use display_mode for the category.
| display_mode | What the mat measures | Primary metric |
|---|---|---|
| vertical | Flight time of a vertical jump | jump_height_cm |
| contact | Ground contact time, repeated hops | contact_time_ms |
| rsi | Rebound jumps, flight ÷ contact | rsi |
| drop_jump | Drop from a set box height, then rebound | rsi |
| pps | Power, using body mass and any added load | jump height + body weight |
| bounce_factor | Two-jump bounce protocol | rsi |
| timer | Open recording on a countdown | not a test |
| free | Raw flight and contact capture | not a test |
Note that DRI is not a mode. The mode is drop_jump, and DRI is a scoring option within it.
Treat display_mode as an open string, not a fixed enum. New modes get added over time. An integration that hard-fails on an unrecognized value will break, so fall back to the assessment name and carry the raw value through.
The closest standard equivalent to vertical is a countermovement jump measured by flight time, and the built-in Standing Vertical Jump protocol is performed with a countermovement.
Be precise in your model, though: the mat measures flight time and cannot detect whether the athlete used a countermovement, an arm swing, or a static squat jump. That is coach-controlled protocol, not something the data guarantees. So if a coach names their assessment CMJ, that exact string is what assessment.name returns, and that is your reliable signal, not the mode.
Plyomat ships built-in assessments: Standing Vertical Jump, SL Vertical Jump, Repeat Vertical Jump, 10/5 RSI, 4/2 RSI, Max RSI, SL Max RSI, SL 5-Hop RSI, SL Lateral to Vertical RSI, Drop Jump DRI at 6″, 12″, 18″ and 24″, Speed Box Hop x 15, Load Power Profile, and High Load Power Profile. But every organization can create and name its own.
GET /v1/assessments per organization and map dynamically.is_system: true means a Plyomat built-in. false means that organization created it.assessment.id and treat the name as a display label.Every set carries a tags array of strings. Coaches apply these on Display Mode saves to annotate what they actually ran, and they are often exactly the detail a measurement mode cannot capture on its own. Real examples in use today: CMJ, SJ, Seated Jump, Hands on hip, Trap Bar, Box 18in, preseason, post-injury.
Two things to know before you build on them:
If an organization wants a variant like CMJ or Squat Jump to be reliable, structured data, the right home is a named assessment. Tags are the annotation layer on top.
A null assessment_id is normal and expected. It means the coach used Display Mode rather than selecting a formal assessment: normal training, a warm-up, or a quick check.
Those sets are real captured data, but they are not a named test. Handle it explicitly:
test_name = assessment_id
? assessments[assessment_id].name // a named test
: null // Display Mode capture, use display_modeDepending on your product, either label those as ad-hoc or exclude them from formal test reporting. What you should not do is silently treat them as an untitled test of type display_mode.
jump_height_cm in centimeters, all times in milliseconds.is_kept. Reps the coach discarded come through with is_kept: false. Exclude them before computing bests or averages.sidedness: "unilateral" and each rep carries side: "left" or "right". Bilateral reps have side: null.vertical set has no contact_time_ms and no rsi, because that mode does not measure them. Do not treat null as missing data.body_weight_kg_at_capture is a snapshot taken at test time, not the athlete's current roster weight. Use it for any mass-dependent math so historical results stay reproducible.{
test_id: set.assessment_id, // null = Display Mode
test_name: assessment?.name ?? null, // organization-defined string
test_family: set.display_mode, // open string, not an enum
is_builtin: assessment?.is_system ?? null,
tags: set.tags, // coach annotations, free text
laterality: set.laterality,
performed_at: set.started_at,
athlete_id: set.athlete_id,
reps: set.reps.filter(r => r.is_kept)
}The full interactive reference lists every endpoint, parameter, and response shape.