Skip to main content

Coach API

The Coach API exposes a coach’s Saturday surface over a coach-scoped REST API, and over an MCP connector: the per-athlete fueling rollup, AI reports, the roster’s needs-attention markers, and the notification and alerting settings. It is built for a coach who wants to automate fueling monitoring: read who needs attention, pull a report, and configure alert rules and digests across a whole roster programmatically.
Tier requirements. Coach endpoints require the Pro Coach tier or above (Pro Coach, Head Coach, Business, Enterprise). Two further gates sit above that line: minting a coach API key in the portal requires Business or Enterprise plus an org-admin role, and webhook delivery requires Business or Enterprise. A Pro Coach or Head Coach therefore reaches the whole surface through the OAuth connector, and reaches it through an API key only if someone on a Business plan minted one.
When a tier lapses. On an OAuth token, the coach tools stop resolving and coach endpoints return 404, while the coach keeps their own athlete data. There is no error and no separate “downgrade” call. On a coach API key there is no athlete-self facet to fall back to, so the request is refused outright with 403 coach_tier_required.

Authentication

Two ways to authenticate as a coach. Both resolve to the same coach identity and roster. Coach API keys are minted in the coach portal under Admin → API Keys. Pass the key as a Bearer token:
OAuth2 coaches use the connector OAuth flow with coach scopes (coach:roster, coach:reports, coach:alerts, coach:webhooks). Because a coach is also an athlete, the same OAuth token acts as an athlete-self token on /v1/athletes/*.

Scopes and capabilities

The coach’s token or key carries scopes that map to capabilities. A route is reachable only when the token confers the matching capability; otherwise it returns the uniform 404. The two vocabularies are unioned, never translated, so a key keeps exactly the reach its scopes confer. roster:read alone does not open the report routes; pick org:read or higher when a key needs reports. Keys minted before scoped keys existed carry a single * scope and hold all four capabilities.
Roster confinement. Every {athlete_uid} you pass is checked against your roster. An athlete who isn’t on your roster, and an athlete who doesn’t exist, both return 404 resource_not_found. The two cases are indistinguishable, so the API never reveals whether an athlete exists.

Reads

List the roster

GET /v1/coach/roster returns every athlete you coach, each with a needs-attention summary over the look-back window.
The markers come from Saturday’s single shared concern definition, so they match the coach portal table and the digest exactly. An athlete who fueled well shows flagged: false.

Roster digest (flagged-only)

GET /v1/coach/roster/digest returns the same data for athletes who crossed a concern bar this window, most-flagged first. Athletes who did not cross a bar are omitted, which is what makes this the useful call on a large roster.
flagged carries the same entry shape as GET /v1/coach/roster above, ordered most-flagged first. flagged_count counts the entries in it; total_count counts the whole roster.

Per-athlete fueling rollup

GET /v1/coach/athletes/{athlete_uid}/fueling-rollup returns one athlete’s in-window sessions (the same table the portal renders) plus the concern summary and the resolved cutoffs that produced the markers.
The session object above is abbreviated. Each entry also carries the as-used and suggested prescription totals, prep fidelity, leftover reuse, report source and completeness, intensity, the profile snapshot, opaque vessel_reports and weather maps, and the derived adherence_vs_suggested, dial_down_gap, and per_hour triples. The adherence fractions are uncapped, so a value above 1.0 is genuine over-consumption rather than an error. Every missing value stays null, never 0, and an absent symptoms key is not a zero.

AI report

GET /v1/coach/athletes/{athlete_uid}/report returns the AI-generated fueling report: a third-person narrative grounded only in the athlete’s own numbers, plus the structured concern summary behind it, so an agent can quote the prose or compute on the data.
Served from cache unless a newer session has landed or refresh=true.

Session detail

GET /v1/coach/athletes/{athlete_uid}/sessions/{activity_id} drills into one session: the full per-session projection (planned-vs-actual fueling, symptoms, vessel reports, weather, sleep) plus the concern markers that session crossed.

Configuration

Most of the Coach API is a configuration surface. Anything a coach can configure in the portal is configurable here: channels, triggers, per-nutrient thresholds, the overall/group/athlete scope hierarchy, consolidation, cadence, quiet hours, and AI-report defaults. The portal UI and the API are two views of one config model.
Scope precedence. Config applies at one of three scopes: overall (the whole roster), group (a coach group), or athlete (one athlete). When resolving what an athlete sees, the most specific scope wins (athlete over group over overall). scope_id is required for group and athlete, and omitted for overall.

Notification rules

GET /v1/coach/config/notification-rules?scope=overall reads the rules set at exactly that scope, not the merged resolution. PUT /v1/coach/config/notification-rules replaces the rule set at a scope. It is an idempotent upsert: re-running with the same body is a no-op, so no per-request idempotency key is needed.
Triggers: under_fuel, symptom, low_rating, hyponatremia_pattern, dial_down, sleep_trend, went_quiet. Channels: in_portal, email, push, webhook. (SMS is not yet supported.) Cadence: realtime (sent as it happens) or digest (bundled into one daily item per athlete). Thresholds are fractions in (0,1]. urgent_threshold is the urgent band. amber_threshold is optional and decouples this trigger’s marker line from the shared cutoff; omit it to use the resolved concern cutoff. Combinators are bounded two-trigger ANDs: both legs must be markers on the same session (“sodium short AND a cramp”). Exactly two triggers, with no OR, NOT, or nesting.

Presets

POST /v1/coach/config/preset applies a named starting point at a scope, which you can then tweak rule by rule.

AI-report & concern settings

GET / PUT /v1/coach/config/report-settings reads/upserts the report window/focus defaults and any overridden concern cutoffs at a scope. Unset fields fall through to the broader scope.
Athlete data is read-only. The Coach API never writes an athlete’s fueling data, debriefs, or prescriptions. A coach can configure only their own alerts, reports, groups, and thresholds.

Webhooks

A webhook is another delivery channel on the alerts you already configure ("channels": ["webhook"]). Register an endpoint, then select webhook as a channel on any rule.
Webhook delivery requires the Business or Enterprise tier. Registration succeeds at Pro Coach and Head Coach, but no events are delivered until the tier qualifies, and delivery stops within the same billing check if the tier later drops.
POST /v1/coach/webhooks registers an endpoint and returns the signing secret once.
secret appears in this response and never again. GET /v1/coach/webhooks omits it, and there is no endpoint that re-reads it. Store it when you register, or delete the endpoint and register a new one to get a fresh secret.
Event types: concern.detected and athlete.needs_attention, which an empty events list subscribes you to, plus coach.message.sent, which is opt-in and only fires for Enterprise coaches. The URL must be public https; internal, metadata, and loopback URLs are rejected at registration. Deliveries are signed with HMAC-SHA256 in the X-Saturday-Signature header using the secret returned at registration, retried with backoff, and auto-disabled after 15 consecutive failures. See Webhooks for verifying signatures.

Full identity

Coach API responses carry full athlete identity: names and data exactly as the coach sees them in the portal. The coach owns the coaching relationship, so the API has no de-identification layer (the export feature’s identity levels are a portal and export concern). Handling that PII downstream is your responsibility. See the data policy.

Rate limits

Coach principals get 50 requests per second sustained with a burst of 500, so a 500-athlete roster pull or digest completes in one turn without paging penalties. These limits are set for abuse protection rather than capacity management. See rate limiting.

SDKs

Both the TypeScript and Python SDKs expose the coach surface as a coach resource: