Skip to content

Change the card's height, weight or leg length

PATCH
/api/portal/{client_id}/profile/
curl --request PATCH \
--url http://localhost:8000/api/portal/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/profile/ \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "height_cm": 153, "weight_kg": 42 }'

Each field optional, null clears it, within the profile form’s bounds. Never the date of birth (400 not_editable): it decides whether the athlete is a minor. Later analyses use the new values; every change is stamped with who and when.

The Contribute level only: a View grant gets 403 contribute_required.

The signed-in person’s grant on the card in the path. Every route answers 404 while client access is switched off on the environment. 404 card_not_found for a card the account never held — another athlete’s, a card of the account’s own team, or an id that names nothing. 403 access_ended (reason: revoked · team_plan · minor) for a card it held and can no longer open, consent_required when the consent texts changed since the person accepted them (consent again with POST /api/portal/{client_id}/consent/). A partner API key gets 403. 429 past the account’s rate (client_portal, 120/min; the PDFs client_portal_pdf, 20/min).

Only what a coach released is visible, curated on the server: the team’s hidden metrics are absent, the coach’s AI text and internal fields are not sent, and media URLs are signed for two hours.

client_id
required
string format: uuid

PortalProfileInput (contract change 4: no date_of_birth). Each field optional; null clears it. The bounds are the profile form’s (domain/profile_bounds.py).

object
height_cm
integer
nullable
weight_kg
number format: double
nullable

Examples

ExampleNewHeightAndWeight

New height and weight

{
"height_cm": 153,
"weight_kg": 42
}
Media typeapplication/json

GET / PATCH /api/portal/<client_id>/profile/ (PortalProfile).

object
display_name
required
string
date_of_birth
required

Shown, never editable from the portal (D2).

string format: date
nullable
height_cm
required
integer
nullable
weight_kg
required
number format: double
nullable
editable
required

True on the Contribute level (D2).

boolean
last_change
required

The last change to the body data — from the portal or by a coach; null while nobody changed them since the stamps began.

object
by_name
required

Who made it — from the portal, or a coach of the team: their name, never an address; “The athlete” / “A parent or guardian” / “Your coach” when they gave none.

string
at
required
string format: date-time

Examples

ExampleProfile
{
"display_name": "Sofia Lebedeva",
"date_of_birth": "2012-04-02",
"height_cm": 153,
"weight_kg": 42,
"editable": true,
"last_change": {
"by_name": "Maria Lebedeva",
"at": "2026-11-02T10:15:00Z"
}
}

The body was rejected: errors keyed by field.

Media typeapplication/json

Validation errors keyed by field — each key an offending field of the body, each value its human-readable messages (or a stable code, e.g. not_editable).

object
key
additional properties
Array<string>

Examples

400 — date of birth

{
"date_of_birth": [
"not_editable"
]
}
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

access_ended, consent_required, or DRF’s message for a partner API key.

string
reason

With access_ended: why the card no longer opens — revoked, team_plan or minor.

string
client_name
string
team_name
string
consent_version

With consent_required: the version to accept.

string
kinds

With consent_required: the texts to show.

Array<string>

Examples

Example403—AViewGrant

403 — a View grant

{
"detail": "contribute_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

Examples

Example404—NotACardOfThisAccount

404 — not a card of this account

{
"detail": "card_not_found"
}
Media typeapplication/json
object
detail
required

429 — throttled: this account’s (or address’s) rate for the route is used up; try again after wait seconds, which Retry-After repeats. A route with a limit of its own answers its own code instead, documented there.

string
wait

With throttled: whole seconds until the next request can pass.

integer
nullable

Example generated

{
"detail": "example",
"wait": 1
}