Skip to content

Change a client's portal settings

PATCH
/api/clients/{id}/access/
curl --request PATCH \
--url http://localhost:8000/api/clients/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/access/ \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "auto_share": true }'

auto_share releases each new completed session to the client’s side as it finishes (off by default). upload_monthly_cap is how many clips the card’s Contribute people may send per calendar month, 0–100; 0 turns uploads off. Either field may be omitted. Answers the whole panel. Turning auto_share off, or uploads off (a cap of 0), is open on every plan — it only takes access away; turning auto-share on, or a cap above 0, needs a plan with client access.

A coach of the team, on a card of the active team. Every route answers 404 while client access is switched off on the environment. A partner API key and a client’s own account get 403. 402 trial_expired / access_ended on a read-only team (its trial or plan ended), else feature_not_in_plan (feature: client_access) on a plan without client access — every paid tier and Strategic include it; a trial does not.

id
required
string format: uuid

PATCH /api/clients/<id>/access/ body. Either field may be omitted.

object
auto_share
boolean
upload_monthly_cap
integer
<= 100

Examples

Turn on auto-share

{
"auto_share": true
}
Media typeapplication/json

GET /api/clients/<id>/access/ — the App access panel (ClientAccessOverview).

object
auto_share
required

Release new completed sessions to the client’s side automatically (off by default).

boolean
upload_monthly_cap
required

Clips the card’s Contribute people may send per calendar month; 0 turns uploads off.

integer
minor
required

True for a minor (under 18 by the date of birth, or declared so): only a guardian can be invited. Null when the card has no date of birth and nothing declared — the athlete invite waits for one.

boolean
nullable
people
required
Array<object>

One person with access to a card, as the coach sees it (ClientAccessRecord).

object
id
required
string format: uuid
email
required

The address the invite went to.

string format: email
display_name
required

The person’s name once they accepted; empty until then.

string
relation
required
string
Allowed values: athlete guardian
level
required
string
Allowed values: view contribute
status
required

invited · active · expired · revoked. expired is an invite whose 14-day link lapsed; the coach can resend it.

  • invited - Invited
  • active - Active
  • expired - Expired
  • revoked - Revoked
string
Allowed values: invited active expired revoked
language
required

The invite’s language. Later e-mails to this person use it too.

  • en - English
  • ru - Русский
  • es - Español
string
Allowed values: en ru es
invited_by_name
required

Who sent the invite, for the team’s coaches: the inviting coach’s name, else their e-mail address; once their account is gone, the name it left, else “A coach” in ?lang (English by default).

string
invited_at
required
string format: date-time
expires_at
required

When the invite link lapses — set only while the status is invited.

string format: date-time
nullable
accepted_at
required
string format: date-time
nullable
revoked_at
required
string format: date-time
nullable
last_sent_at
required

When the last invite or sign-in e-mail left; null when none ever did.

string format: date-time
nullable
email_sent
required

Whether the e-mail reached the person (false: the transport refused it — the grant stands, resend it). On an invite or resend answer: this call’s e-mail, null when the call sent nothing on purpose (a second resend within a minute). On every other answer: the latest attempt’s, null when none was ever made.

boolean
nullable
can_invite
required

Whether inviting, resending, changing a level and sharing would pass the plan gate now: the team is not read-only AND its plan includes client access. False: those answer 402 — show the upgrade instead of the button.

boolean

Examples

ExampleAGuardianOnAMinor'sCard

A guardian on a minor's card

{
"auto_share": true,
"upload_monthly_cap": 4,
"minor": true,
"people": [
{
"id": "0b9e4d21-7a3c-4f6e-8d15-3c2a9b7e6f58",
"email": "olga.lebedeva@example.com",
"display_name": "Olga Lebedeva",
"relation": "guardian",
"level": "contribute",
"status": "active",
"language": "ru",
"invited_by_name": "Kim Larsen",
"invited_at": "2026-10-06T09:00:00Z",
"expires_at": null,
"accepted_at": "2026-10-07T18:20:00Z",
"revoked_at": null,
"last_sent_at": "2026-10-06T09:00:00Z",
"email_sent": true
}
],
"can_invite": true
}
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"
}

The team is read-only, or its plan does not include client access.

Media typeapplication/json
object
detail
required

One of eight 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; exercise_not_in_plan leads to a contact-us CTA — the movement list is sales-managed, no tier adds to it (#2030). trial_expired / access_ended mean the team is read-only (#2901): everything opens, nothing new is created; they lead to a subscribe CTA and always carry reset_at: null.

  • quota_exhausted - Session quota for the period reached 0
  • subscription_suspended - Stripe dunning in flight (past_due / unpaid)
  • seat_limit_reached - Adding another team member would exceed plan.max_seats
  • client_limit_reached - Adding another Client would exceed plan.max_client_profiles
  • feature_not_in_plan - The tier does not include this whole feature surface
  • exercise_not_in_plan - The (activity, exercise) is not in the team’s exercise grants
  • trial_expired - The team is read-only: its self-serve trial ended
  • access_ended - The team is read-only: its paid plan or granted access ended
string
Allowed values: quota_exhausted subscription_suspended seat_limit_reached client_limit_reached feature_not_in_plan exercise_not_in_plan trial_expired access_ended
reset_at
required

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

string format: date-time
nullable
current_plan
required

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.

string
current_role
required

The active plan’s role string. See current_plan.

string
feature

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

string

Examples

Example402—ClientAccessNotInPlan

A Free team opens the App access panel

{
"detail": "feature_not_in_plan",
"reset_at": null,
"current_plan": "free",
"current_role": "free",
"feature": "client_access"
}
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

Example403—AClient'sOwnAccount

403 — a client's own account

The coach side is for a coach of the team; a client uses the portal.

{
"detail": "client_account"
}
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—NoSuchClientInTheTeam

404 — no such client in the team

{
"detail": "client_not_found"
}