Create a client on the current team
curl -X POST https://api.ai.aikynetix.app/api/clients/ \ -H 'Authorization: Bearer aik_yourPartnerTokenHere' \ -H 'Content-Type: application/json' \ -d '{ "display_name": "Anna Petrova", "gender": "female", "height_cm": 168, "weight_kg": 58, "external_uid": "club-roster-id-2419" }'import requestsr = requests.post( "https://api.ai.aikynetix.app/api/clients/", headers={"Authorization": "Bearer aik_yourPartnerTokenHere"}, json={ "display_name": "Anna Petrova", "gender": "female", "height_cm": 168, "weight_kg": 58, "external_uid": "club-roster-id-2419", }, timeout=30,)if r.status_code == 402: raise RuntimeError(r.json()["detail"]) # client_limit_reachedr.raise_for_status()client = r.json()const r = await fetch("https://api.ai.aikynetix.app/api/clients/", { method: "POST", headers: { Authorization: "Bearer aik_yourPartnerTokenHere", "Content-Type": "application/json" }, body: JSON.stringify({ display_name: "Anna Petrova", gender: "female", height_cm: 168, weight_kg: 58, external_uid: "club-roster-id-2419", }),});if (r.status === 402) throw new Error((await r.json()).detail);if (!r.ok) throw new Error(`HTTP ${r.status}`);const client = await r.json();Creates a new Client (athlete) under the signed-in coach’s active team. display_name is required; everything else (email, gender, height_cm, weight_kg, leg_length_cm, shoe_size, external_uid) is optional but enriches body-aware metric adjustments downstream — a Client without height_cm and weight_kg falls back to default green zones, which are noticeably less accurate. external_uid is your own stable id for cross-system joins; we never interpret it. Returns the created Client.
402 — client_limit_reached when the team has already filled plan.max_client_profiles. Free’s (1) cap makes this fire on the second create; the payload shape is identical to the other billing gates (quota / suspended / seat) — see PaywallErrorResponse.
403 when the caller has no active team selected (a transient state, e.g. mid team-switch or just after the active team was deleted).
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
Athlete’s display name as the coach refers to them. Required.
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.
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.
beginner- Beginnerintermediate- Intermediateadvanced- Advanced
Training experience: beginner / intermediate / advanced / empty string. Drives skill-level-specific green-zone overrides — a beginner and an advanced athlete are judged against different bands. Coach-set; leave blank for all-levels defaults.
beginner- Beginnerintermediate- Intermediateadvanced- Advanced
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).
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).
Optional. Leg length in centimetres (35-150). Used only by specific running analyzers when present; safe to leave null.
Free-text shoe size (e.g. 42 EU, 9 US). Coach reference only.
right- Rightleft- Leftambidextrous- Ambidextrous
right / left / ambidextrous / empty. Durable client-profile attribute; protocol reports (Tennis Serve, Running Gait) key off the dominant side. Distinct from the per-session handedness mapping.
right- Rightleft- Leftambidextrous- Ambidextrous
quarterback- Quarterbackrunning_back- Running Backwide_receiver- Wide Receivertight_end- Tight Endoffensive_line- Offensive Linedefensive_line- Defensive Linelinebacker- Linebackerdefensive_back- Defensive Back
Playing-position group for team-sport athletes: quarterback / running_back / wide_receiver / tight_end / offensive_line / defensive_line / linebacker / defensive_back / empty string. The DB Injury-Risk Battery uses it for its Defensive-Back eligibility check (warns on a non-DB athlete, never blocks). Leave blank if unknown / not a team-sport athlete.
quarterback- Quarterbackrunning_back- Running Backwide_receiver- Wide Receivertight_end- Tight Endoffensive_line- Offensive Linedefensive_line- Defensive Linelinebacker- Linebackerdefensive_back- Defensive Back
List of prior / ongoing injury entries. Each item: { id, bodyPartId, label, side?, date?, severity?, note? } (mirrors the InjuryHistoryEditor item shape; date is yyyy-mm-dd or the sentinel ongoing). Stored verbatim so a Solutions InjuryHistoryBlock renders it with no transform.
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.
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.
Examples
All optional fields populated (best metric accuracy)
{ "display_name": "Anna Petrova", "email": "anna@example.com", "gender": "female", "height_cm": 168, "weight_kg": 58, "shoe_size": "38 EU", "external_uid": "club-roster-id-2419"}Only the required field; metrics fall back to defaults
{ "display_name": "Pavel Sidorov"}object
Athlete’s display name as the coach refers to them. Required.
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.
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.
beginner- Beginnerintermediate- Intermediateadvanced- Advanced
Training experience: beginner / intermediate / advanced / empty string. Drives skill-level-specific green-zone overrides — a beginner and an advanced athlete are judged against different bands. Coach-set; leave blank for all-levels defaults.
beginner- Beginnerintermediate- Intermediateadvanced- Advanced
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).
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).
Optional. Leg length in centimetres (35-150). Used only by specific running analyzers when present; safe to leave null.
Free-text shoe size (e.g. 42 EU, 9 US). Coach reference only.
right- Rightleft- Leftambidextrous- Ambidextrous
right / left / ambidextrous / empty. Durable client-profile attribute; protocol reports (Tennis Serve, Running Gait) key off the dominant side. Distinct from the per-session handedness mapping.
right- Rightleft- Leftambidextrous- Ambidextrous
quarterback- Quarterbackrunning_back- Running Backwide_receiver- Wide Receivertight_end- Tight Endoffensive_line- Offensive Linedefensive_line- Defensive Linelinebacker- Linebackerdefensive_back- Defensive Back
Playing-position group for team-sport athletes: quarterback / running_back / wide_receiver / tight_end / offensive_line / defensive_line / linebacker / defensive_back / empty string. The DB Injury-Risk Battery uses it for its Defensive-Back eligibility check (warns on a non-DB athlete, never blocks). Leave blank if unknown / not a team-sport athlete.
quarterback- Quarterbackrunning_back- Running Backwide_receiver- Wide Receivertight_end- Tight Endoffensive_line- Offensive Linedefensive_line- Defensive Linelinebacker- Linebackerdefensive_back- Defensive Back
List of prior / ongoing injury entries. Each item: { id, bodyPartId, label, side?, date?, severity?, note? } (mirrors the InjuryHistoryEditor item shape; date is yyyy-mm-dd or the sentinel ongoing). Stored verbatim so a Solutions InjuryHistoryBlock renders it with no transform.
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.
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.
object
Athlete’s display name as the coach refers to them. Required.
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.
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.
beginner- Beginnerintermediate- Intermediateadvanced- Advanced
Training experience: beginner / intermediate / advanced / empty string. Drives skill-level-specific green-zone overrides — a beginner and an advanced athlete are judged against different bands. Coach-set; leave blank for all-levels defaults.
beginner- Beginnerintermediate- Intermediateadvanced- Advanced
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).
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).
Optional. Leg length in centimetres (35-150). Used only by specific running analyzers when present; safe to leave null.
Free-text shoe size (e.g. 42 EU, 9 US). Coach reference only.
right- Rightleft- Leftambidextrous- Ambidextrous
right / left / ambidextrous / empty. Durable client-profile attribute; protocol reports (Tennis Serve, Running Gait) key off the dominant side. Distinct from the per-session handedness mapping.
right- Rightleft- Leftambidextrous- Ambidextrous
quarterback- Quarterbackrunning_back- Running Backwide_receiver- Wide Receivertight_end- Tight Endoffensive_line- Offensive Linedefensive_line- Defensive Linelinebacker- Linebackerdefensive_back- Defensive Back
Playing-position group for team-sport athletes: quarterback / running_back / wide_receiver / tight_end / offensive_line / defensive_line / linebacker / defensive_back / empty string. The DB Injury-Risk Battery uses it for its Defensive-Back eligibility check (warns on a non-DB athlete, never blocks). Leave blank if unknown / not a team-sport athlete.
quarterback- Quarterbackrunning_back- Running Backwide_receiver- Wide Receivertight_end- Tight Endoffensive_line- Offensive Linedefensive_line- Defensive Linelinebacker- Linebackerdefensive_back- Defensive Back
List of prior / ongoing injury entries. Each item: { id, bodyPartId, label, side?, date?, severity?, note? } (mirrors the InjuryHistoryEditor item shape; date is yyyy-mm-dd or the sentinel ongoing). Stored verbatim so a Solutions InjuryHistoryBlock renders it with no transform.
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.
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.
Responses
Section titled “Responses”object
Server-assigned UUID for the athlete.
Athlete’s display name as the coach refers to them. Required.
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.
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.
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.
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).
beginner- Beginnerintermediate- Intermediateadvanced- Advanced
Training experience: beginner / intermediate / advanced / empty string. Drives skill-level-specific green-zone overrides — a beginner and an advanced athlete are judged against different bands. Coach-set; leave blank for all-levels defaults.
beginner- Beginnerintermediate- Intermediateadvanced- Advanced
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).
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).
Optional. Leg length in centimetres (35-150). Used only by specific running analyzers when present; safe to leave null.
Free-text shoe size (e.g. 42 EU, 9 US). Coach reference only.
right- Rightleft- Leftambidextrous- Ambidextrous
right / left / ambidextrous / empty. Durable client-profile attribute; protocol reports (Tennis Serve, Running Gait) key off the dominant side. Distinct from the per-session handedness mapping.
right- Rightleft- Leftambidextrous- Ambidextrous
quarterback- Quarterbackrunning_back- Running Backwide_receiver- Wide Receivertight_end- Tight Endoffensive_line- Offensive Linedefensive_line- Defensive Linelinebacker- Linebackerdefensive_back- Defensive Back
Playing-position group for team-sport athletes: quarterback / running_back / wide_receiver / tight_end / offensive_line / defensive_line / linebacker / defensive_back / empty string. The DB Injury-Risk Battery uses it for its Defensive-Back eligibility check (warns on a non-DB athlete, never blocks). Leave blank if unknown / not a team-sport athlete.
quarterback- Quarterbackrunning_back- Running Backwide_receiver- Wide Receivertight_end- Tight Endoffensive_line- Offensive Linedefensive_line- Defensive Linelinebacker- Linebackerdefensive_back- Defensive Back
List of prior / ongoing injury entries. Each item: { id, bodyPartId, label, side?, date?, severity?, note? } (mirrors the InjuryHistoryEditor item shape; date is yyyy-mm-dd or the sentinel ongoing). Stored verbatim so a Solutions InjuryHistoryBlock renders it with no transform.
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.
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.
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.
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.
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.
ISO-8601 UTC timestamp of client creation.
ISO-8601 UTC timestamp of the last mutation.
Examples
{ "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://media.ai.aikynetix.app/teams/…/clients/…/avatar-a1b2c3d4.jpg", "external_uid": "club-roster-id-2419", "session_count": 0, "created_at": "2026-04-29T10:00:00Z", "updated_at": "2026-04-29T10:00:00Z"}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 — Validation error
{ "display_name": [ "This field is required." ]}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
One of five canonical strings. The front-end paywall switches copy + CTA on this value (exact string equality — do not localise). quota_exhausted / seat_limit_reached / client_limit_reached / feature_not_in_plan lead to an upgrade CTA; subscription_suspended leads to a Billing Portal CTA so the buyer can update their payment method.
quota_exhausted- Session quota for the period reached 0subscription_suspended- Stripe dunning in flight (past_due / unpaid)seat_limit_reached- Adding another team member would exceed plan.max_seatsclient_limit_reached- Adding another Client would exceed plan.max_client_profilesfeature_not_in_plan- The tier does not include this whole feature surface
ISO-8601 UTC timestamp the team’s current period ends. Null when the team has no live subscription (legacy un-migrated row). On Free + paid this is the renewal moment; quota refreshes to exactly plan.session_quota (NOT additive — leftover sessions don’t roll over).
The active plan’s role string (free, starter, professional, organization, strategic). Same value as current_role — kept as two separate fields for forward-compatibility with a future named-plan split where current_plan could carry an SKU and current_role the tier label.
The active plan’s role string. See current_plan.
Which feature surface is locked. Present ONLY on feature_not_in_plan, so one modal can name what’s gated without a detail string per feature (#773). The only value today is agents (aikynetix/agents/services.py::AGENTS_FEATURE).
Examples
Free team's `max_client_profiles=1` is already filled
{ "detail": "client_limit_reached", "reset_at": "2026-06-01T00:00:00Z", "current_plan": "free", "current_role": "free"}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"}