Skip to content

Replace a client (full update)

PUT
/api/clients/{id}/
curl --request PUT \
--url http://localhost:8000/api/clients/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/ \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "display_name": "Anna Petrova", "email": "anna@example.com", "date_of_birth": "1996-03-02", "gender": "female", "height_cm": 168, "weight_kg": 58, "shoe_size": "38 EU", "external_uid": "club-roster-id-2419" }'

Replaces every mutable field on the Client. Use PATCH for partial updates if you only have a few fields to change — PUT requires the complete payload. Updating profile fields (height_cm, weight_kg, gender) takes effect immediately for any newly-uploaded session; existing sessions keep the green zones they were computed under at analysis time.

409 client_changed when expected_updated_at is sent and the athlete has changed since: nothing is stored. Injury entries keep their ids: send each id back (an id-less entry is matched to the stored one; 400 ambiguous_injury_entry when that cannot be told apart).

id
required
string format: uuid
object
display_name
required

Athlete’s display name as the coach refers to them. Required.

string
>= 1 characters <= 200 characters
email
Any of:
string format: email
<= 254 characters
gender

male / female / nonbinary / empty string — case and surrounding whitespace are normalised, anything else is a 400. Drives gender-specific green-zone overrides on running cadence, stride length, jump power, and other normative metrics. Leave blank if unknown — you’ll get the all-genders defaults.

string
<= 16 characters
date_of_birth
required

ISO date (YYYY-MM-DD). Required on write; reads back null on athletes migrated before the requirement. Drives age-band-aware green zones — a masters runner and a junior are judged against age-appropriate bands. Stored as the date, not the age, so it never goes stale.

string format: date
minor_declared

Declare the athlete under 18 when the date of birth doesn’t show it, or there is none (#2751). Default false. Does not change age_band, which stays from the date of birth.

boolean
guardian_consent

The parent’s or guardian’s consent in force, or null. Write an object to record one (on create or update), null to withdraw it. Without one, a minor’s analyses are always face-blurred and no public link to their data is minted or served (#2751).

object
guardian_name
required

The parent or guardian who gave consent.

string
>= 1 characters <= 200 characters
relationship

How they are related to the athlete, e.g. mother, legal guardian.

string
<= 100 characters
method
required

How consent was given: signed_form, email, in_person or other.

  • signed_form - Signed form
  • email - Email
  • in_person - In person
  • other - Other
string
Allowed values: signed_form email in_person other
given_on
required

The day the guardian consented (YYYY-MM-DD).

string format: date
notes

Anything else worth keeping, e.g. where the signed form is filed.

string
<= 1000 characters
skill_level
One of:
  • beginner - Beginner
  • intermediate - Intermediate
  • advanced - Advanced
string
Allowed values: beginner intermediate advanced
reader_mode

Whose words this client’s results are written in (#2867): coach (default) or amateur — Bowler Mode on bowling. Every report, PDF and new share link about the client starts from it; an activity with no amateur copy reads as coach either way.

  • coach - Coach
  • amateur - Amateur
string
Allowed values: coach amateur
height_cm
required

Standing height in centimetres (55-260). Required on write; reads back null on athletes migrated before the requirement. Drives proportional scaling of stride length, vertical oscillation, step width, and trunk sway green zones (legacy rows without it fall back to a 170 cm reference).

integer
<= 32767
weight_kg
required

Body weight in kilograms (20-400). Required on write; reads back null on athletes migrated before the requirement — drives force / power / impulse metrics on jump and agility activities (legacy rows without it fall back to 75 kg, which can introduce 20-30% error on those readings).

number format: double
leg_length_cm

Optional. Leg length in centimetres (35-150). Used only by specific running analyzers when present; safe to leave null.

number format: double
nullable
shoe_size

Free-text shoe size (e.g. 42 EU, 9 US). Coach reference only.

string
<= 16 characters
avatar_url
Any of:
string format: uri
<= 200 characters
dominant_hand
One of:
  • right - Right
  • left - Left
  • ambidextrous - Ambidextrous
string
Allowed values: right left ambidextrous
position
One of:
  • quarterback - Quarterback
  • running_back - Running Back
  • wide_receiver - Wide Receiver
  • tight_end - Tight End
  • offensive_line - Offensive Line
  • defensive_line - Defensive Line
  • linebacker - Linebacker
  • defensive_back - Defensive Back
string
Allowed values: quarterback running_back wide_receiver tight_end offensive_line defensive_line linebacker defensive_back
injury_history

List of prior / ongoing injury entries. Each item: { id, bodyPartId, label, side?, date?, severity?, status?, resolvedDate?, note? } (mirrors the InjuryHistoryEditor item shape; date is yyyy-mm-dd or the sentinel ongoing; status is active / monitoring / resolved; resolvedDate is yyyy-mm-dd, not before date). Stored as written so a Solutions InjuryHistoryBlock renders it with no transform. Send each entry’s id back. An entry without one is matched to the stored entry with the same bodyPartId, side and date and keeps its id, so its follow-up notes and documents stay with it; when that is ambiguous and one of the candidates has notes or documents, the write is a 400 ambiguous_injury_entry. A duplicate id is a 400. Dropping an entry never deletes its notes or documents.

workstation_setup

Reusable Workspace Wellness workstation setup — { posture, measurements_cm: { desktop_height, seat_top_height?, seat_pan_depth?, back_rest_height?, eye_level_height, screen_top_height } } (mirrors WorkstationSetupSerializer; seat fields required only when posture is seated). The ergonomics survey step pre-fills from this so a returning subject doesn’t re-measure their desk; each session snapshots its own point-in-time copy. Empty object {} means unset. See #303 / #530.

notes

Coach’s free-text notes on the athlete — the general-purpose field that replaced the narrow sport_type / nature_of_work questions (#926). Bounded to 2000 chars.

string
<= 2000 characters
expected_updated_at

Optional precondition on an update (#2693): the updated_at the caller read. When the athlete has changed since, the write is refused with 409 client_changed and nothing is stored, so a stale edit cannot drop an injury entry a colleague has just added. Left out, the write behaves as it always has. Ignored on create.

string format: date-time

Examples

ExampleFullProfile

All optional fields populated (best metric accuracy)

{
"display_name": "Anna Petrova",
"email": "anna@example.com",
"date_of_birth": "1996-03-02",
"gender": "female",
"height_cm": 168,
"weight_kg": 58,
"shoe_size": "38 EU",
"external_uid": "club-roster-id-2419"
}
Media typeapplication/json
object
id
required

Server-assigned UUID for the athlete.

string format: uuid
display_name
required

Athlete’s display name as the coach refers to them. Required.

string
<= 200 characters
email
Any of:
string format: email
<= 254 characters
gender

male / female / nonbinary / empty string — case and surrounding whitespace are normalised, anything else is a 400. Drives gender-specific green-zone overrides on running cadence, stride length, jump power, and other normative metrics. Leave blank if unknown — you’ll get the all-genders defaults.

string
<= 16 characters
date_of_birth
required

ISO date (YYYY-MM-DD). Required on write; reads back null on athletes migrated before the requirement. Drives age-band-aware green zones — a masters runner and a junior are judged against age-appropriate bands. Stored as the date, not the age, so it never goes stale.

string format: date
nullable
age
required

Whole-years chronological age, computed live from date_of_birth (never stored). null when no DOB is set. Drives age-band-aware metric green zones.

integer
nullable
age_band
required

Demographic age band derived live from age — under_18 / 18_34 / 35_49 / 50_64, then five-year bands above 65 (65_69 / 70_74 / 75_79 / 80_84 / 85_plus), or "" when no DOB is set. The same slice the green-zone resolver and the threshold configurator use; band cutoffs are server-owned so clients never re-derive them (#1406).

string
minor_declared

Declare the athlete under 18 when the date of birth doesn’t show it, or there is none (#2751). Default false. Does not change age_band, which stays from the date of birth.

boolean
is_minor
required

Under 18 by date_of_birth, or declared so with minor_declared (#2751). India’s DPDP Act counts anyone under 18 as a child.

boolean
guardian_consent

The parent’s or guardian’s consent in force, or null. Write an object to record one (on create or update), null to withdraw it. Without one, a minor’s analyses are always face-blurred and no public link to their data is minted or served (#2751).

object
guardian_name
required

The parent or guardian who gave consent.

string
<= 200 characters
relationship

How they are related to the athlete, e.g. mother, legal guardian.

string
<= 100 characters
method
required

How consent was given: signed_form, email, in_person or other.

  • signed_form - Signed form
  • email - Email
  • in_person - In person
  • other - Other
string
Allowed values: signed_form email in_person other
given_on
required

The day the guardian consented (YYYY-MM-DD).

string format: date
notes

Anything else worth keeping, e.g. where the signed form is filed.

string
<= 1000 characters
recorded_at
required

When it was recorded in the app.

string format: date-time
recorded_by_name
required

Who recorded it (a coach’s name, or API key).

string
guardian_consent_required
required

True for a minor with no guardian’s consent in force: blur is forced on every analysis and sharing answers 403 guardian_consent_required.

boolean
skill_level
One of:
  • beginner - Beginner
  • intermediate - Intermediate
  • advanced - Advanced
string
Allowed values: beginner intermediate advanced
reader_mode

Whose words this client’s results are written in (#2867): coach (default) or amateur — Bowler Mode on bowling. Every report, PDF and new share link about the client starts from it; an activity with no amateur copy reads as coach either way.

  • coach - Coach
  • amateur - Amateur
string
Allowed values: coach amateur
height_cm
required

Standing height in centimetres (55-260). Required on write; reads back null on athletes migrated before the requirement. Drives proportional scaling of stride length, vertical oscillation, step width, and trunk sway green zones (legacy rows without it fall back to a 170 cm reference).

integer
nullable <= 32767
weight_kg
required

Body weight in kilograms (20-400). Required on write; reads back null on athletes migrated before the requirement — drives force / power / impulse metrics on jump and agility activities (legacy rows without it fall back to 75 kg, which can introduce 20-30% error on those readings).

number format: double
nullable
leg_length_cm

Optional. Leg length in centimetres (35-150). Used only by specific running analyzers when present; safe to leave null.

number format: double
nullable
shoe_size

Free-text shoe size (e.g. 42 EU, 9 US). Coach reference only.

string
<= 16 characters
avatar_url
Any of:
string format: uri
<= 200 characters
dominant_hand
One of:
  • right - Right
  • left - Left
  • ambidextrous - Ambidextrous
string
Allowed values: right left ambidextrous
position
One of:
  • quarterback - Quarterback
  • running_back - Running Back
  • wide_receiver - Wide Receiver
  • tight_end - Tight End
  • offensive_line - Offensive Line
  • defensive_line - Defensive Line
  • linebacker - Linebacker
  • defensive_back - Defensive Back
string
Allowed values: quarterback running_back wide_receiver tight_end offensive_line defensive_line linebacker defensive_back
injury_history

List of prior / ongoing injury entries. Each item: { id, bodyPartId, label, side?, date?, severity?, status?, resolvedDate?, note? } (mirrors the InjuryHistoryEditor item shape; date is yyyy-mm-dd or the sentinel ongoing; status is active / monitoring / resolved; resolvedDate is yyyy-mm-dd, not before date). Stored as written so a Solutions InjuryHistoryBlock renders it with no transform. Send each entry’s id back. An entry without one is matched to the stored entry with the same bodyPartId, side and date and keeps its id, so its follow-up notes and documents stay with it; when that is ambiguous and one of the candidates has notes or documents, the write is a 400 ambiguous_injury_entry. A duplicate id is a 400. Dropping an entry never deletes its notes or documents.

workstation_setup

Reusable Workspace Wellness workstation setup — { posture, measurements_cm: { desktop_height, seat_top_height?, seat_pan_depth?, back_rest_height?, eye_level_height, screen_top_height } } (mirrors WorkstationSetupSerializer; seat fields required only when posture is seated). The ergonomics survey step pre-fills from this so a returning subject doesn’t re-measure their desk; each session snapshots its own point-in-time copy. Empty object {} means unset. See #303 / #530.

notes

Coach’s free-text notes on the athlete — the general-purpose field that replaced the narrow sport_type / nature_of_work questions (#926). Bounded to 2000 chars.

string
<= 2000 characters
external_uid
required

Your own stable id for this athlete in your external system (athlete-management software, club ID, etc.). We never interpret this; useful for cross-system joins. Read-only here — set at create time only.

string
session_count
required

Number of sessions tied to this client. Read-only; computed live. The web’s delete-confirm dialog uses this to spell out how much history a delete will wipe.

integer
completed_session_count
required

Number of this client’s COMPLETED sessions — the ones an analysis exists for. Read-only; computed live. The athlete pickers show this count; session_count counts every status.

integer
group_ids
required

UUIDs of the ClientGroups this athlete belongs to (#545) — many-to-many, so an athlete can be in several. Drives the group chips + filter on the Clients page; mutate via the /api/client-groups/<id>/members/ endpoints, never here.

Array<string>
created_at
required

ISO-8601 UTC timestamp of client creation.

string format: date-time
updated_at
required

ISO-8601 UTC timestamp of the last mutation.

string format: date-time

Examples

ExampleAClient

A client

{
"id": "f6d52e64-3aa3-4a26-bac2-0fc9d718448d",
"display_name": "Anna Petrova",
"email": "anna@example.com",
"gender": "female",
"height_cm": 168,
"weight_kg": 58,
"leg_length_cm": null,
"shoe_size": "38 EU",
"avatar_url": "https://s3.ai.aikynetix.app/aikynetix-media/teams/…/clients/…/avatar-a1b2c3d4.jpg?X-Amz-Expires=604800&X-Amz-Signature=…",
"external_uid": "club-roster-id-2419",
"session_count": 12,
"minor_declared": false,
"is_minor": false,
"guardian_consent": null,
"guardian_consent_required": false,
"created_at": "2026-04-29T10:00:00Z",
"updated_at": "2026-05-20T14:31:00Z"
}
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 — Validation error

{
"display_name": [
"This field is required."
]
}
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

Example generated

{
"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

Example generated

{
"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

Example409—TheAthleteChangedSinceItWasRead

`expected_updated_at` is stale: nothing was stored (#2693)

{
"detail": "client_changed"
}