Server-sent events stream of AI coaching tokens
const url = 'http://localhost:8000/api/sessions/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/coaching/';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url http://localhost:8000/api/sessions/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/coaching/ \ --header 'Authorization: Bearer <token>'Returns an SSE stream of LLM-generated coaching tokens for the session, written in plain language for the athlete. Frame format: event: meta (one-time prelude with session/lang/system metadata), then a sequence of event: token frames each carrying {text}, then event: done {ok: true}. Optional query params: focus to bias the analysis at one metric/area, lang (en / ru) to switch the LLM’s output language, units (metric / imperial) to control km/h vs mph + cm vs in. The completed text is cached on the session and reused by the PDF endpoint. A single event: error frame with HTTP 404 is sent for an unknown session; 402 if the team’s coaching quota is exhausted.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Query Parameters
Section titled “Query Parameters”Optional metric/area to bias the analysis toward.
en (default) or ru.
metric (default) or imperial.
Responses
Section titled “Responses”A text/event-stream (SSE) response. Frames arrive as event: <name>\ndata: <json>\n\n: one meta prelude, a sequence of token frames each carrying {text}, then a final done {ok: true}. On error a single error frame {detail} is sent and the HTTP status is set to 404 (unknown session/token).
Examples
SSE frame sequence (illustrative)
Server-sent events; the actual response is text/event-stream. The example shows the concatenated frames as they arrive — meta first, a sequence of token, done last.
event: metadata: {"session_id":"5c836d7d","activity":"running","client":"Anna Petrova","focus":null,"lang":"en","system":"metric","model":"…","provider":"…","mode":"live"}
event: tokendata: {"text":"Cadence "}
event: tokendata: {"text":"of "}
event: tokendata: {"text":"169 spm "}
event: tokendata: {"text":"is on the low side; "}
event: tokendata: {"text":"target 175-180 for this pace.\n"}
event: donedata: {"ok":true}object
One of five 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.
quota_exhausted- Session quota for the period reached 0subscription_suspended- Stripe dunning in flight (past_due / unpaid)seat_limit_reached- Adding another team member would exceed plan.max_seatsclient_limit_reached- Adding another Client would exceed plan.max_client_profilesfeature_not_in_plan- The tier does not include this whole feature surface
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).
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.
The active plan’s role string. See current_plan.
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).
Example
{ "detail": "quota_exhausted"}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).
Examplegenerated
{ "detail": "example"}