Update min/max on a team threshold (audited)
const url = 'http://localhost:8000/api/thresholds/example/';const options = { method: 'PATCH', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"min_good":178,"max_good":184,"reason":"Narrowed after week-4 data"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request PATCH \ --url http://localhost:8000/api/thresholds/example/ \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "min_good": 178, "max_good": 184, "reason": "Narrowed after week-4 data" }'Any team member; audited. Partial update — only min_good and max_good are mutable. reason is required (sign-off note), notes optional. Writes a MetricThresholdAudit entry (action=update) with before/after values in the same transaction.
Cannot be used to flip the underlying metric_key / activity / exercise — those are immutable on an existing row; delete + recreate if that’s what you need.
Returns 400 on min ≥ max, or on a band that overlaps an ordered metric pair (walking stride_length vs stance_length, #1236) — same rule and the same two body shapes as POST (bound-keyed at the row’s own slice, detail-keyed for an overlap in a slice the row inherits into). A partial update is checked against the band that would actually be stored, so sending only min_good is validated against the row’s existing max_good.
As with POST, norms on the returned row is ALWAYS null; the reference band comes from the list endpoint (#1406).
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Request Body
Section titled “Request Body”Body shape for POST / PATCH. The audit row’s reason and
notes ride alongside the threshold fields so we never write a
threshold change without its sign-off context.
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
Tighten the band
{ "min_good": 178, "max_good": 184, "reason": "Narrowed after week-4 data"}Body shape for POST / PATCH. The audit row’s reason and
notes ride alongside the threshold fields so we never write a
threshold change without its sign-off context.
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
Body shape for POST / PATCH. The audit row’s reason and
notes ride alongside the threshold fields so we never write a
threshold change without its sign-off context.
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
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
404 — No such threshold on this team
{ "detail": "threshold not found"}