Skip to content

Progress over released results

GET
/api/portal/{client_id}/progress/
curl --request GET \
--url http://localhost:8000/api/portal/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/progress/ \
--header 'Authorization: Bearer <token>'

One activity at a time — the one asked for, else the most recent with a released result. A series per metric the released sessions of the window measured (the team’s hidden metrics left out), with the athlete’s good range and the best value across every released result of the activity. range: 90d · 180d · all (default). 400 for an unknown activity or range.

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
activity
string
range
string

90d · 180d · all.

Media typeapplication/json

GET /api/portal/<client_id>/progress/ (PortalProgress).

object
activities
required

Activities with released results on this card, most recent first.

Array<string>
activity
required
string
nullable
series
required
Array<object>
object
metric_key
required
string
unit
required
string
points
required
Array<object>
object
at
required
string format: date-time
value
required
number format: double
session_id
required

The released result the value comes from.

string format: uuid
min_good
required
number format: double
nullable
max_good
required
number format: double
nullable
higher_is_better
required
boolean
nullable
best
required

The best value across every released result of the activity, whatever the window.

object
at
required
string format: date-time
value
required
number format: double
session_id
required

The released result the value comes from.

string format: uuid

Examples

ExampleRunning
{
"activities": [
"running"
],
"activity": "running",
"series": [
{
"metric_key": "cadence",
"unit": "spm",
"points": [
{
"at": "2026-11-02T10:00:00Z",
"value": 172,
"session_id": "5e2b9c41-7d3a-4f80-9b6e-1c0d2e3f4a51"
}
],
"min_good": 165,
"max_good": 185,
"higher_is_better": null,
"best": null
}
]
}
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

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

403 — access ended

The card no longer opens; the page says why and names nothing else of it.

{
"detail": "access_ended",
"reason": "revoked",
"client_name": "Sofia Lebedeva",
"team_name": "North Track Academy"
}
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
}