Accept a client invite
const url = 'http://localhost:8000/api/client-invites/accept/';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"token":"Gu7Qk2n0bVw1sXyZ-4hRt9pLmA3cE6dF8jK0oPqS5uW","consent_version":"2026-10-29-draft","confirm_adult":true}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url http://localhost:8000/api/client-invites/accept/ \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "token": "Gu7Qk2n0bVw1sXyZ-4hRt9pLmA3cE6dF8jK0oPqS5uW", "consent_version": "2026-10-29-draft", "confirm_adult": true }'Turns the invite into access to the athlete’s card for the signed-in account, and records the consent it gave (consent_version must be the one the preview names). The account’s e-mail must be the invited address (409 invite_email_mismatch) and verified by something other than the link — the e-mail code flow (/api/me/send-verification/, /api/me/verify-email/) or the sign-in’s own verified address (403 email_unverified): a forwarded link must not open the card for whoever signs up with the address. 409 consent_outdated for an old consent version, minor_athlete_invite / date_of_birth_required when the athlete can no longer be invited themselves, already_on_team for a coach of the card’s team, access_exists when the account already has the card. 400 unless confirm_adult is true (minors never hold an account; a guardian is an adult too). Answers the card, as the portal lists it. Creates no team. Rate-limited (429). Answers 404 while client access is switched off on the environment.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”POST /api/client-invites/accept/ body (ClientInviteAcceptInput).
object
The person confirms they are 18 or over (or the age of majority where they live). Must be true.
The version of the consent texts the person agreed to.
Examples
Accept with the current consent
{ "token": "Gu7Qk2n0bVw1sXyZ-4hRt9pLmA3cE6dF8jK0oPqS5uW", "consent_version": "2026-10-29-draft", "confirm_adult": true}POST /api/client-invites/accept/ body (ClientInviteAcceptInput).
object
The person confirms they are 18 or over (or the age of majority where they live). Must be true.
The version of the consent texts the person agreed to.
Example generated
confirm_adult=true&token=example&consent_version=examplePOST /api/client-invites/accept/ body (ClientInviteAcceptInput).
object
The person confirms they are 18 or over (or the age of majority where they live). Must be true.
The version of the consent texts the person agreed to.
Responses
Section titled “Responses”One card the person can open in the portal (PortalCard).
object
The athlete’s photo, signed for two hours; empty when there is none.
The team’s own logo for the portal header (S5), signed for two hours; null when the team set none — the deployment’s brand shows instead.
athlete- Athleteguardian- Parent or guardian
view- Viewcontribute- Contribute
The person’s unread messages on the card’s conversation.
Whether the card opens now. False: opening it answers the inactive_reason page. /api/portal/cards/ lists the active cards first. A revoked grant is not listed at all.
Examples
The card the accept opened
{ "client_id": "a1f1c0a2-0000-4000-8000-0000000000aa", "client_name": "Sofia Lebedeva", "avatar_url": "", "team_name": "North Track Academy", "team_logo_url": null, "relation": "guardian", "level": "view", "unread_count": 0, "active": true, "inactive_reason": null}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).
Example generated
{ "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).
Example generated
{ "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
403 — address not verified
Verify the address with the e-mail code first, then accept again.
{ "detail": "email_unverified"}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 invite
{ "detail": "invite_not_found"}object
409 — the request conflicts with current state. detail is a stable code (e.g. reanalyze_in_flight, already_on_plan, email_in_use). Some 409s carry extra context fields alongside detail; those are documented on the specific endpoint that emits them.
Examples
409 — another address
{ "detail": "invite_email_mismatch"}409 — old consent
{ "detail": "consent_outdated"}409 — the athlete is a minor
{ "detail": "minor_athlete_invite"}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
410 — expired
The 14-day link lapsed: ask the coach to resend it.
{ "detail": "invite_expired"}410 — revoked
The coach withdrew the invite.
{ "detail": "invite_revoked"}410 — already used
The invite was accepted; sign in instead.
{ "detail": "invite_used"}object
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.
With throttled: whole seconds until the next request can pass.
Example generated
{ "detail": "example", "wait": 1}