Skip to content

Update min/max on a team threshold (audited)

PATCH
/api/thresholds/{id}/
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).

id
required
string

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
activity
  • running - Running
  • walking - Walking
  • jump - Vertical Jump
  • weightlifting - Weightlifting
  • mobility - Mobility Assessment
  • workspace_wellness - Workspace Wellness
  • agility - Agility
  • fencing - Fencing
  • cycling - Cycling
  • tennis - Tennis
  • padel - Padel Tennis
  • bowling - Bowling
  • baseball - Baseball
  • football - Football
  • badminton - Badminton
  • cricket - Cricket
  • basketball - Basketball
  • scuba_diving - Scuba Diving
  • ice_hockey - Ice Hockey
  • american_football - American Football
  • fri - Falls Risk Indicator
string
Allowed values: running walking jump weightlifting mobility workspace_wellness agility fencing cycling tennis padel bowling baseball football badminton cricket basketball scuba_diving ice_hockey american_football fri
exercise
string
nullable
metric_key
string
>= 1 characters
gender
One of:
  • male - Male
  • female - Female
  • nonbinary - Non-binary
  • `` -
string
Allowed values: male female nonbinary
segment
string
nullable
age_band
One of:
  • under_18 - Under 18
  • 18_34 - 18–34
  • 35_49 - 35–49
  • 50_64 - 50–64
  • 65_69 - 65–69
  • 70_74 - 70–74
  • 75_79 - 75–79
  • 80_84 - 80–84
  • 85_plus - 85+
  • `` -
string
Allowed values: under_18 18_34 35_49 50_64 65_69 70_74 75_79 80_84 85_plus
skill_level
One of:
  • beginner - Beginner
  • intermediate - Intermediate
  • advanced - Advanced
string
Allowed values: beginner intermediate advanced
min_good
number format: double
nullable
max_good
number format: double
nullable
higher_is_better
boolean
nullable
unit
string
reason
string
>= 1 characters
notes
string
Examples
ExampleTightenTheBand

Tighten the band

{
"min_good": 178,
"max_good": 184,
"reason": "Narrowed after week-4 data"
}
Media typeapplication/json

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
id
required
string
inherited
required
boolean
activity
required
string
exercise
required
string
nullable
metric_key
required
string
metric_label
required
string
unit
required
string
higher_is_better
required
boolean
nullable
min_good
required
number format: double
nullable
max_good
required
number format: double
nullable
is_height_scaled
required
boolean
default_min_good
required
number format: double
nullable
default_max_good
required
number format: double
nullable
norms
required
object
min
required
number format: double
nullable
max
required
number format: double
nullable
evidence_basis
required
string
source
required
string
scope
required
  • cohort - Cohort
  • base - Base
string
Allowed values: cohort base
overrides
required
Array<object>
object
kind
required
  • gender - gender
  • age_band - age_band
  • skill_level - skill_level
  • height_scaled - height_scaled
  • segment - segment
string
Allowed values: gender age_band skill_level height_scaled segment
label
required
string
detail
required
object
key
additional properties
number format: double
nullable
slice

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
gender
required
string
age_band
required
string
skill_level
required
string
observed_distribution
required
object
bins
required
Array<object>
object
bin_min
required
number
bin_max
required
number
count
required
integer
total_sessions
required
integer
axis_min
required
number
axis_max
required
number
session_window_days
required
integer
Examples
ExamplePersistedRow

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
}
}
Media typeapplication/json
object
detail
required

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).

string
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."
]
}
Media typeapplication/json
object
detail
required

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).

string
Examplegenerated
{
"detail": "example"
}
Media typeapplication/json
object
detail
required

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).

string
Examplegenerated
{
"detail": "example"
}
Media typeapplication/json
object
detail
required

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).

string
Examples
Example404—NoSuchThresholdOnThisTeam

404 — No such threshold on this team

{
"detail": "threshold not found"
}