Create a team threshold override
const url = 'http://localhost:8000/api/thresholds/';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"activity":"running","exercise":"Treadmill","metric_key":"cadence","gender":"","min_good":175,"max_good":185,"reason":"Club-specific target after Sibhashri review 2026-05"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url http://localhost:8000/api/thresholds/ \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "activity": "running", "exercise": "Treadmill", "metric_key": "cadence", "gender": "", "min_good": 175, "max_good": 185, "reason": "Club-specific target after Sibhashri review 2026-05" }'Any team member; audited. Persists a new team-scoped MetricThreshold row for the given (activity, exercise, metric_key, gender, segment) tuple — overriding the system / MetricSpec default for the team. Writes a MetricThresholdAudit entry (action=create) in the same transaction; reason is required (sign-off note).
Returns 409 if a team override already exists for that tuple (use PATCH instead). Returns 400 on min ≥ max, unknown metric_key, or a band that overlaps an ordered metric pair — walking stride_length must stay strictly above the stance_length band and vice versa, since a full gait cycle always covers more ground than a single stance (#1236).
The pair check has two shapes. An overlap with the band that applies at the same demographic / exercise slice is keyed on the offending bound (min_good / max_good) so a client can point at the field. An overlap only visible in a slice the row inherits into — e.g. a gender=female stride row against a base stance_length row — is keyed on detail, because the conflicting row is one this request never touched. Either way the row is not stored.
norms on the returned row is ALWAYS null — the Movement-Norms reference band is a list-shape concern resolved per requested slice (#1406), and the page refetches the list after a write. Read the band from GET /api/thresholds/, not from this response.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”POST requires the natural-key fields so we can build a fresh row.
Inherits validation; tightens required-ness via __init__.
object
running- Runningwalking- Walkingjump- Vertical Jumpweightlifting- Weightliftingmobility- Mobility Assessmentworkspace_wellness- Workspace Wellnessagility- Agilityfencing- Fencingcycling- Cyclingtennis- Tennispadel- Padel Tennisbowling- Bowlingbaseball- Baseballfootball- Footballbadminton- Badmintoncricket- Cricketbasketball- Basketballscuba_diving- Scuba Divingice_hockey- Ice Hockeyamerican_football- American Footballfri- Falls Risk Indicator
Examples
Override cadence for treadmill running
{ "activity": "running", "exercise": "Treadmill", "metric_key": "cadence", "gender": "", "min_good": 175, "max_good": 185, "reason": "Club-specific target after Sibhashri review 2026-05"}POST requires the natural-key fields so we can build a fresh row.
Inherits validation; tightens required-ness via __init__.
object
running- Runningwalking- Walkingjump- Vertical Jumpweightlifting- Weightliftingmobility- Mobility Assessmentworkspace_wellness- Workspace Wellnessagility- Agilityfencing- Fencingcycling- Cyclingtennis- Tennispadel- Padel Tennisbowling- Bowlingbaseball- Baseballfootball- Footballbadminton- Badmintoncricket- Cricketbasketball- Basketballscuba_diving- Scuba Divingice_hockey- Ice Hockeyamerican_football- American Footballfri- Falls Risk Indicator
POST requires the natural-key fields so we can build a fresh row.
Inherits validation; tightens required-ness via __init__.
object
running- Runningwalking- Walkingjump- Vertical Jumpweightlifting- Weightliftingmobility- Mobility Assessmentworkspace_wellness- Workspace Wellnessagility- Agilityfencing- Fencingcycling- Cyclingtennis- Tennispadel- Padel Tennisbowling- Bowlingbaseball- Baseballfootball- Footballbadminton- Badmintoncricket- Cricketbasketball- Basketballscuba_diving- Scuba Divingice_hockey- Ice Hockeyamerican_football- American Footballfri- Falls Risk Indicator
Responses
Section titled “Responses”Read shape — either a materialised team row (inherited=False,
id populated) or a synthetic catalog entry sourced from
MetricSpec (inherited=True, id=None). Plain Serializer
rather than ModelSerializer because inherited rows have no DB
backing — the container POSTs to materialise on first edit.
object
object
cohort- Cohortbase- Base
object
gender- genderage_band- age_bandskill_level- skill_levelheight_scaled- height_scaledsegment- segment
object
The demographic slice an override row lives in — the FE’s jump target when the coach clicks the badge to land on that cohort. Empty strings are the “all” sentinel for each dimension.
object
object
object
Examples
Persisted row
{ "id": "7b3d9e1a-2c4f-4a6b-8d0e-1f2a3b4c5d6e", "inherited": false, "activity": "running", "exercise": "Treadmill", "metric_key": "cadence", "unit": "spm", "higher_is_better": false, "min_good": 175, "max_good": 185, "is_height_scaled": false, "norms": { "min": 170, "max": 180, "evidence_basis": "validated", "source": "Running cadence norms (Sport Science Insider)", "scope": "base" }, "overrides": [], "observed_distribution": { "bins": [], "total_sessions": 0, "axis_min": 0, "axis_max": 1, "session_window_days": 30 }}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).
Examples
400 — band overlaps an ordered metric pair
{ "min_good": [ "Stride Length min_good (0.6) must be strictly above the Stance Length max_good (0.9) that applies to this team. A full gait cycle always covers more ground than a single stance, because a foot is planted for less time than the cycle it belongs to — the two bands cannot overlap." ]}400 — band overlaps the pair in an inherited slice
{ "detail": "Saving this band would leave the Stance Length and Stride Length bands overlapping for Treadmill on this team: Stance Length reaches 1.0 while Stride Length starts at 0.95. A full gait cycle always covers more ground than a single stance, because a foot is planted for less time than the cycle it belongs to — the two bands cannot overlap. Adjust Stance Length down or Stride Length up — including any gender / age / skill overrides you have saved for either, since the widest of them is what an athlete can be scored against."}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).
Examplegenerated
{ "detail": "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).
Examplegenerated
{ "detail": "example"}object
409 — the request conflicts with current state. detail is a stable code (e.g. reanalyze_in_flight, already_on_plan, email_in_use). Some 409s carry extra context fields alongside detail; those are documented on the specific endpoint that emits them.
Examples
409 — Override already exists (use PATCH)
{ "detail": "threshold override already exists for this metric"}