Analyse an uploaded clip
const url = 'http://localhost:8000/api/portal/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/uploads/';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"session_id":"7c1e2d3f-4a5b-4c6d-8e7f-90a1b2c3d4e5","activity":"running","exercise":"Treadmill"}'};
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/portal/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/uploads/ \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "session_id": "7c1e2d3f-4a5b-4c6d-8e7f-90a1b2c3d4e5", "activity": "running", "exercise": "Treadmill" }'Debits one analysis from the TEAM’s quota and queues it; the coach is told in the conversation, and the result reaches the portal once a coach shares it. 404 upload_not_found for an id this person’s intent did not reserve on this card (or one lapsed after 45 minutes, or refused for good); 409 upload_used (sent already), upload_cap_reached, exercise_not_offered (outside the allowance), team_quota_exhausted (the coach is told; never the 402 paywall) — the cap and the analyses are checked before the clip is read; 400 for the clip itself — clip_too_long, clip_too_large, content_blocked (these three free the slot: the id cannot be sent again), clip_not_found, activity_mismatch_confirm (send again with activity_mismatch_confirmed: true), activity_mismatch; 403 access_ended team_plan while the team cannot run analyses; 503 enqueue_failed (nothing was debited; send again).
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.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Request Bodyrequired
Section titled “Request Bodyrequired”PortalUploadInput: the clip is in the bucket — analyse it.
object
The intent’s upload id.
The person confirmed the clip is what they said after a activity_mismatch_confirm 400, as the coach’s create takes it.
Examples
A treadmill run
{ "session_id": "7c1e2d3f-4a5b-4c6d-8e7f-90a1b2c3d4e5", "activity": "running", "exercise": "Treadmill"}PortalUploadInput: the clip is in the bucket — analyse it.
object
The intent’s upload id.
The person confirmed the clip is what they said after a activity_mismatch_confirm 400, as the coach’s create takes it.
PortalUploadInput: the clip is in the bucket — analyse it.
object
The intent’s upload id.
The person confirmed the clip is what they said after a activity_mismatch_confirm 400, as the coach’s create takes it.
Responses
Section titled “Responses”object
pending: the analysis is queued.
Examples
{ "session_id": "7c1e2d3f-4a5b-4c6d-8e7f-90a1b2c3d4e5", "status": "pending"}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 — too long
{ "detail": "clip_too_long", "max_duration_s": 60}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
access_ended, consent_required, or DRF’s message for a partner API key.
With access_ended: why the card no longer opens — revoked, team_plan or minor.
With consent_required: the version to accept.
With consent_required: the texts to show.
Examples
403 — access ended
{ "detail": "access_ended", "reason": "team_plan", "client_name": "Sofia Lebedeva", "team_name": "North Track Academy"}403 — a View grant
{ "detail": "contribute_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).
Examples
404 — not this person's upload
{ "detail": "upload_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 — not offered
{ "detail": "exercise_not_offered"}409 — the team is out of analyses
{ "detail": "team_quota_exhausted"}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}object
503 — a downstream dependency was unavailable and the request could not be served (e.g. enqueue_failed when the analysis queue can’t accept the job). Safe to retry with backoff.
Examples
503 — not queued
{ "detail": "enqueue_failed"}