# Hyperhuman Content API

> One API for delivery, personalization, and insights across your entire product.

> Last updated: 2026-09-11. Spec version: see `info.version` in `/openapi.json`.

Hyperhuman is the fitness content infrastructure behind modern health, wellness, and fitness products. The Content API is the developer-facing surface for the publish + personalize + insights layer.

- Base URL: `https://content.api.hyperhuman.cc`
- Authentication: send `X-Api-Key: <your_key>` on every request
- All non-2xx responses use a single envelope: `{ "error": { "code": "<StableCode>", "message": "...", "target?": "...", "details?": [...] } }`
- Pagination: prefer **`links.next`** to advance (no `page`), except **chat** list/messages which use **`page`/`limit`**. Runtime cursors are `offset`/`limit`; OpenAPI may omit `offset` on shared list DTOs. **Max `50`** on most list endpoints; **`100`** for `GET /v1/orgs/{organizationId}/endusers` and chat. When `limit` is omitted: org **workouts/plans** default to **20**; **video-assets** and **groups** default to **10** — see [AGENTS.md](https://content.api.hyperhuman.cc/AGENTS.md) section 4. Values above the operation max return **400**. Org **plans list** excludes `end-user-ai-generated` (use `GET /v1/plans/{id}`). Full-workout **audio** export locale rules differ from **video** export — see AGENTS §9.

- Localization: pass `locale` (BCP-47, e.g. `en-US`, `fr-FR`, `nb-NO`) on read endpoints (query) or AI generate endpoints (body); 18 locales; falls back to English when unsupported on reads; generate returns `400` for unsupported locale (send `nb-NO`, not `no`)
- AI / heavy-video endpoints (recommend, generate, adapt, insights, chat **writes**, video generation, full video export) **and** `GET .../stock-exercises` count as **10x** rate-limit weight. Chat list is **1x**. Health-data batch push and nutrition log writes count as **1x** per request (batch capped at 50 entries). Trialing Stripe subscriptions use **1/100** of paid hourly/daily caps. Weight is consumed on admitted requests including most 400/403; a plan-gated stock-catalog 403 does **not** consume. On 429 honor `Retry-After` then `X-RateLimit-Reset`.
- **Strict parameters:** Do not send query or body keys that are not in the public OpenAPI operation; unknown keys often return **400** (`ValidationError`). **`/openapi.json` is the curated, productized surface** — not every in-process route may be listed. Partner clip browse is gated `GET .../stock-exercises`. See [AGENTS.md](https://content.api.hyperhuman.cc/AGENTS.md) sections 11–12.
- **Ids:** Opaque **24-character hex** strings (DB ids); do not document or depend on a specific storage engine name.

## Five capabilities

The API stacks five layers on the same library. Most teams ship steps 1-2 in week one and add the AI layers as their audience signals demand it.

1. **Publish Content** - list and play **workspace** workouts and plans (`GET /v1/orgs/{organizationId}/workouts`, `GET /v1/orgs/{organizationId}/plans`; empty **200** until items are added in Teams — no public stock-workout/program catalog), and browse workspace exercise videos (`GET /v1/orgs/{organizationId}/video-assets`).
2. **AI Recommend** - rank **existing** library content for the user (`POST /v1/orgs/{organizationId}/workouts/recommend`, `POST /v1/orgs/{organizationId}/plans/recommend`). Prefer this when pairing a quiz/profile to curated team workouts.
3. **AI Generate** - create new content on the fly (`POST /v1/orgs/{organizationId}/workouts/generate`, `POST /v1/orgs/{organizationId}/plans/generate`). Success is **HTTP 200** (not 201). Workout generate returns **ephemeral JSON** (no library workout id; play via JSON receiver — do not playlist). Plan generate returns a top-level plan `id` **only** when `endUserProfileDetails.endUserId` is set (store that id); without it there is no plan id. Free-text field is `userRequirements` (not `userGuidance`). Optional `methodologyProfileId` applies a **team** Programming DNA profile from `GET /v1/orgs/{organizationId}/methodologies` (opt-in only — nothing is applied if omitted). Response may include a **Generation Inspector** snapshot (`methodology` and `inspection`). HTTP client timeout ≥ **120s**. Optional body `locale` (BCP-47); invalid locale returns `400`.
4. **AI Adapt** - evolve existing content (`POST /v1/orgs/{organizationId}/workouts/{workoutId}/adapt`, `POST /v1/orgs/{organizationId}/plans/{planId}/adapt`). Workout adapt is ephemeral JSON. Plan adapt **requires** `endUserProfileId` and returns a persisted adapted program `id`. Free-text field is `userGuidance` (not `userRequirements`). Does **not** accept `methodologyProfileId`. HTTP client timeout ≥ **120s**.
5. **AI Insights** - daily digest + per-pillar drill-downs (`GET /v1/orgs/{organizationId}/endusers/{endUserId}/insights/digest`), plus org-scoped **writes** that feed them: batch health-data push (`POST .../endusers/{endUserId}/insights/health-data`) and direct nutrition logging (`POST .../endusers/{endUserId}/nutrition/log`).

In `.../endusers/{endUserId}/...` routes, the `{endUserId}` path segment is **resolved** by the server: it can be a **24-character hex** user id, an **external** id, or a **user email** (see [AGENTS.md](https://content.api.hyperhuman.cc/AGENTS.md) section 6). Responses still use opaque ids; prefer the hex id in new integrations. An identifier that does not resolve to an end user in the organization returns **`404`** (an unknown identifier never produces a `500`).

## Programming DNA & Generation Inspector

Coaches publish reusable team methodology in Hyperhuman Teams (**Set as team guideline** then **Make default**; **up to 10** team profiles). Personal (coach-only) Style is never listed and never applies to members.

- List: `GET /v1/orgs/{organizationId}/methodologies` — team profiles only (id, name, version, applicability, `isTeamDefault`, `ruleSummary`). **No** full guidance text. Empty `{ items: [] }` is **200**. **1x**. Listing never applies a profile.
- **This API:** generate is **opt-in**. Optional body `methodologyProfileId` (24-character hex from that list) on `POST .../workouts/generate` and `POST .../plans/generate`. Unknown, personal, or inactive ids → **400** (same message). Omit = none. `isTeamDefault` is informational — organization API keys never auto-apply it.
- **Hyperhuman Club and the Hyperhuman member web app:** when a team default exists, member Generate and Adapt follow that house Programming DNA. Members do not send `methodologyProfileId`. A personal Style is not enough.
- Not accepted on recommend, adapt, or chat on this API.
- Precedence for that request: platform bounds (pool, safety, duration, structure) and **hard** structured rules win; `userRequirements` wins over methodology **text** and preferred rules.
- Workout preflight: `POST .../workouts/generate/preflight` (**1x**, same body, no workout created) — feasibility + hard-rule clamps + `infeasible[]`. Not the Inspector snapshot. No plan generate preflight.
- Generate **Generation Inspector** snapshot: `methodology` (`{ id, name, version, reason }` or `null`) and `inspection` (`methodology`, `hardRulesApplied`, `violations`, `complianceStatus` of `compliant` | `partial` | `not_applicable`, `generationId`). Store it yourself — no `GET /v1/generations/{id}`; later `GET /v1/plans/{id}` does not replay it. Hard-rule results are deterministic; text guidelines are guidance, not a guarantee. Inspector fields are partner metadata, not playback fields. Inspector is generate-only on this API.

## Machine-readable specs

- [OpenAPI 3 (JSON)](https://content.api.hyperhuman.cc/openapi.json) - source of truth for SDK and tool/function-calling generation
- [LLM bundle](https://content.api.hyperhuman.cc/llms-full.txt) - auth, errors, pagination, locales, and a per-endpoint summary in one markdown file
- [Agent integration guide](https://content.api.hyperhuman.cc/AGENTS.md) - conventions a coding agent must follow when generating client code
- [Custom player guide](https://content.api.hyperhuman.cc/docs/guides/custom-player.md) - reference implementation for building your own interactive workout player
- [Developer cheat sheet](https://content.api.hyperhuman.cc/docs/guides/content-API-cheat-sheet.md) - copy-paste flows, curl/JS examples, localization, stock catalog
- [CloneMotion guide](https://content.api.hyperhuman.cc/docs/guides/clonemotion.md) - trainer guidance for creating better exercise clips from photos
- [Teams platform guide](https://team.hyperhuman.cc/docs/guides/teams-platform.md) - product walkthrough of the Teams workspace: create, library, automate, deliver, and billing
- [Swagger UI](https://content.api.hyperhuman.cc/docs/api-explorer) - try-it-out browser explorer

## Embedded library pages (member web)

Hosted **workout** and **program** catalog grids on the member web app (host `https://member.hyperhuman.cc`, production default `https://member.hyperhuman.cc`), not on `https://content.api.hyperhuman.cc`. Paths: `/workouts?orgId=...` and `/plans?orgId=...`. Optional query flags: `showSearch`, `showFilters`, `gridCols` (2-4, default 3), `playbackMode` (`overlay` default vs `dedicated-page` for new-tab playback), `showDuration`, `showDifficulty` — see the **Branded library embed** section in the API overview (Scalar / Swagger intro). Teams copy snippets from **Share → Embed page**. API parity: `GET /v1/orgs/{organizationId}/workouts` and `GET /v1/orgs/{organizationId}/plans`.

## Embedded workout player (Teams host)

Drop-in iframes on `https://team.hyperhuman.cc` — see **Embedded Player** in the API overview. Always frame them (not top-level).

- **Pre-built:** `/embed/workout/{workoutId}?organizationId=...&apiKey=...` → Content API `GET /workouts/{id}` + `/playlist` (+ `/music`); optional sessions/feedback via `sessionMode` / `endUserId`.
- **AI / JSON:** `/embed/workout-json-receiver?organizationId=...` → `HYPERHUMAN_EMBED_INIT` with generate/adapt JSON → media via `GET /v1/video-asset/{exerciseId}` only; **no** PulseMix, **no** sessions/feedback. Waits for a non-empty `apiKey` before those GETs.
- **Usage / quota:** the URL or INIT `apiKey` is sent as `X-Api-Key`. Pre-built GETs + sessions and each JSON `GET /video-asset/{id}` are **1x** on that key. Host `POST .../workouts/generate` is a separate **10x**. No separate embed-play unit.
- **Iframe host tips:** `allow="autoplay; fullscreen; accelerometer; gyroscope"` + `allowfullscreen`; full-viewport (`100dvh`) for mobile/portrait letterboxing; optional `HYPERHUMAN_HOST_IMMERSIVE` / `HYPERHUMAN_ORIENTATION_TOGGLE`; BGM music-off = pause (not volume) — see overview + custom-player guide.

## HyperCast connect & control (cast to TV)

Pair a phone/app **controller** with a TV **display** and drive workout playback remotely. The display is the org-branded member web page `https://member.hyperhuman.cc/cast?orgId=...` (QR + 6-digit code, **80-minute** session); your app is the remote. All routes under `/v1/cast/pairing` (OpenAPI tag **`workouts-cast`**, key-authenticated, **1x**). A **team-scoped key** may only join or create sessions for its own organization (`403` otherwise). Controllers **join** — do not call `POST .../session` (display-owned).

- Join: `POST /v1/cast/pairing/join-by-code` (6-digit code) or `POST .../join` (parse `sessionId` + `code` from the QR URL `https://member.hyperhuman.cc/cast?sessionId=...&code=...`). One controller per session — joining disconnects the previous device. Generate your own opaque `deviceId` and reuse it.
- Load + play: `POST .../{sessionId}/workout` (load on display; omit deprecated `workoutData`), then `POST .../{sessionId}/workout/start` — optional `endUserId` / `externalUserId` / `organizationId` (same names as sessions; omit = anonymous). Attribution is **API key + those fields only** (a member Bearer token does not attribute cast start). The end user must be an active org member (`404` otherwise — start does not play). Workout must be created/published (unknown id → `404`). `done` completes the session created at start.
- Control: `POST .../{sessionId}/workout/control` with `action`: `play` | `pause` | `next` | `prev` | `restart` | `done_exercise` | `done`. Show `done_exercise` only when status reports `currentExerciseIsRepBased: true`. Status polling: `GET .../{sessionId}/workout/status`. Unpair: `POST .../{sessionId}/device/disconnect` (playback keeps running; rejoin with the same code).
- REST commands broadcast to the display server-side — no realtime dependency required. For live status, connect Socket.IO using `socketIO.fullUrl` from the join response (namespace `/cast/pairing`, handshake `auth: { apiKey }`) and listen for `workout-cast`, `workout-session-started`, `workout-state-changed`, `workout-progress-changed`, `device-paired`, `device-disconnected`. `pairing-success` has no device field. Send `heartbeat` (`{ sessionId, deviceId }`) every 30 s. Poll `GET .../{sessionId}/workout/status` after a socket drop.

## Top endpoints (read-mostly)

- `GET /v1/workouts/metadata` - categories, difficulties, and related filter ids (not `GET /v1/workouts/categories` or `GET /v1/categories`)
- `GET /v1/orgs/{organizationId}/workouts` - paginated **workspace** workout library (empty 200 until added in Teams; not `GET .../library`; not the global stock template catalog)
- `GET /v1/workouts/{workoutId}` - full workout document
- `GET /v1/workouts/{workoutId}/playlist?locale=en-US` - segments with presigned progressive MP4 / M4A `url` (~7 days) plus public unsigned S3 JPEG `poster`/`thumbnail` on exercise/class/multi/promo/educational (intro/outro omit poster; do not use `video.url` as the image). Drives the **pre-built** / custom player. For `exercise`, `duration` is the planned work window (not MP4 length) — loop the clip (`main.video.loop: -1`) until it elapses. Class segments may include optional `class.chapters[]` for in-video exercise navigation when chapter analysis has been run
- `GET /v1/workouts/{workoutId}/music` - PulseMix tracks for a **persisted** workout id (not used by JSON-receiver embeds)
- `GET /v1/video-asset/{videoAssetId}` - exercise media by id (JSON-receiver embed path for each `exercise.id` in generate/adapt JSON; optional `locale` for instruction audio)
- `GET /v1/workouts/{workoutId}/sessions/recent` - most recent in-progress session for resume-first UX; needs an end-user identity (Bearer token or `endUserId`/`externalUserId` query fields), else `404`
- `GET /v1/workouts/feedback/options` - rating/difficulty scales (`{ data: { rating, difficulty } }`); submit option **hex ids** on feedback (not labels)
- `GET /v1/workouts/{workoutId}/export/video/stream_url?locale=en-US` - `text/plain` presigned URL to full video export (404 if locale not rendered)
- `GET /v1/workouts/{workoutId}/export/audio/stream_url?locale=en-US` - `text/plain` presigned URL to full narrative audio export
- `GET /v1/orgs/{organizationId}/plans` - paginated **workspace** plan library (same add-first rule for pre-made stock programs)
- `GET /v1/plans/{planId}` - full plan document
- `GET /v1/orgs/{organizationId}/methodologies` - team Programming DNA profiles for generate (`methodologyProfileId`); up to **10** team profiles; summary + `ruleSummary`, not full guidance text; empty `{ items: [] }` is **200**. **1x**. Listing never applies a profile.
- `GET /v1/orgs/{organizationId}/metadata` - org branding (logo, colors, watermark), module flags, and member-app AI visibility flags (`ai*Enabled` — not Content API gates); used by pre-built embed for default branding
- `GET /v1/orgs/{organizationId}/video-assets` - **workspace-only** exercise videos for catalogs/builders (no stock / pay-as-you-go; no `visibility` filter; `single-exercise` rows expose `audioInstructions[]` per locale, each with `assetUri` + optional `scriptText` (verbatim narrator transcript for captions / accessibility / search) - see OpenAPI). DB-level filters: `q` (locale-aware substring), `equipmentIds`, `muscleGroupIds`, `kinds`, `skillLevels`, `executionSides`, `coach`, `collectionNames`. Sort: `date` (alias of `createdAt`), `name`, `kind` with `+`/`-` prefix; default `-date`.
- `GET /v1/orgs/{organizationId}/groups` - exercise collections (circuits / sets)
- `GET /v1/orgs/{organizationId}/endusers/{endUserId}/insights/digest?date=<ISO 8601 date-time>` - daily AI insights digest (3-5 pillars, `todaysFocus`, `highlights[]` signals with additive `kind`s — render unknown kinds generically; optional `readiness`, `week`, `variant`, `shareText`, `dataProfile`; cached per user-local day)
- `GET /v1/orgs/{organizationId}/endusers/{endUserId}/insights/pillars/{pillarType}` - drill-down for a specific pillar
- `GET /v1/orgs/{organizationId}/endusers/{endUserId}/workouts` - this org's **published** workouts assigned to the member (not the org catalog; not another team's content)
- `GET /v1/orgs/{organizationId}/endusers/{endUserId}/plans` - this org's catalog programs (`public`/`private`) in the member library (not `GET .../plans/active`)

## Top endpoints (mutating)

- `POST /v1/orgs/{organizationId}/workouts/recommend` - rank existing workouts for a user (body: `endUserProfileDetails` + optional `locale` only)
- `POST /v1/orgs/{organizationId}/plans/recommend` - rank existing plans for a user
- `POST /v1/orgs/{organizationId}/workouts/generate` - ephemeral AI workout JSON (`single-exercise`|`rest` instances; optional `locale`; free-text `userRequirements` not `userGuidance`; optional `methodologyProfileId`); success **200** (not 201); may include Generation Inspector (`methodology` / `inspection`); HTTP client timeout ≥ 120s; play via JSON-receiver iframe + `GET /video-asset/{id}` — no `/playlist`/`/music`/sessions by id
- `POST /v1/orgs/{organizationId}/workouts/generate/preflight` - dry-run the same generate body (feasibility + hard rules). Does not create a workout. **1x**. Not a Generation Inspector snapshot — that arrives on generate.
- `POST /v1/orgs/{organizationId}/plans/generate` - AI multi-week plan; success **200**; top-level `id` only with `endUserProfileDetails.endUserId` (store it; `workoutCollectionSources`; optional `locale`; free-text `userRequirements` not `userGuidance`; optional `methodologyProfileId`); may include Generation Inspector (`methodology` / `inspection`); no plan generate preflight; HTTP client timeout ≥ 120s
- `POST /v1/orgs/{organizationId}/workouts/{workoutId}/adapt` - personalize an existing team workout (`{workoutId}` must belong to `{organizationId}`; otherwise `404`; free-text `userGuidance` not `userRequirements`)
- `POST /v1/orgs/{organizationId}/plans/{planId}/adapt` - personalize an existing team plan; **requires** `endUserProfileId` + `workoutSources` (`{planId}` must belong to `{organizationId}`; otherwise `404`; free-text `userGuidance` not `userRequirements`)
- `POST /v1/workouts/{workoutId}/sessions/start` - begin tracking a session
- `PATCH /v1/workouts/{workoutId}/sessions/{sessionId}` - update session progress
- `POST /v1/workouts/{workoutId}/sessions/end` - close out a session (abandon; keeps the last PATCHed progress)
- `POST /v1/workouts/{workoutId}/sessions/complete` - force 100% completion (natural finish / "mark as done")
- `POST /v1/workouts/{workoutId}/feedback` - submit session feedback (`{ workoutSessionId, ratingId?, difficultyId?, comment? }`)
- Sessions and feedback are **anonymous by default**; attribute them to an end user **without** a member token by sending `endUserId` / `externalUserId` (+ `organizationId` for cross-org keys) on each call — see [AGENTS.md](https://content.api.hyperhuman.cc/AGENTS.md) section 9. The **pre-built** embedded player's `sessionMode=attributed` uses the same identity; the **JSON-receiver** embed does not call session/feedback APIs (see **Embedded Player** in the API overview).
- `POST /v1/orgs/{organizationId}/content-autopilot` - create a scheduled content automation (generate/clone/publish); manage with `GET`/`PATCH`/`DELETE .../content-autopilot/{taskId}`, `.../activate`, `.../deactivate`, `.../run-now`, and `.../events`
- `POST /v1/chat/conversations` - start an AI coach conversation; continue with `GET`/`POST .../conversations/{conversationId}/messages`, confirm proposed actions with `.../actions/confirm`. `organizationId` and a user identity (`userEmail` **or** `userExternalUUID`) are **required** on every request (body for POST, query for GET/DELETE); not path params. These routes use **`page`-based** pagination
- `POST /v1/orgs/{organizationId}/endusers/{endUserId}/insights/health-data` - batch health-data push (max 50 entries: `activity`, `sleep`, `steps`, `body_metrics`, `heart_rate`, `hydration`, `oxygenation`, `scores`); **always 200** with per-entry `results` + `summary` (check `summary.failed`); idempotent on `(dataType, externalId)`; `externalId` required for `activity`, day-key default (`day-YYYY-MM-DD`) otherwise; backfill max 90 days; counts as **1x**
- `DELETE /v1/orgs/{organizationId}/endusers/{endUserId}/insights/health-data/{dataType}/{externalId}` - delete a pushed entry and recompute the day (wearable-synced activities are protected)
- `POST /v1/orgs/{organizationId}/endusers/{endUserId}/nutrition/log` - log meal macros (at least one of `caloriesKcal`/`proteinG`/`carbsG`/`fatG`); optional `externalId` makes re-POSTs an idempotent **replace** (omit = additive) and enables delete
- `DELETE /v1/orgs/{organizationId}/endusers/{endUserId}/nutrition/log/{externalId}` - remove a logged entry (subtracts from day totals, recomputes nutrition score)
- `POST /v1/orgs/{organizationId}/endusers/{endUserId}/workouts/assign` - add org workouts to the member library (max 10; empty 200; any ineligible id → 400 all-or-nothing; optional `sendAssignmentEmail` / `sendAssignmentPush`)
- `POST /v1/orgs/{organizationId}/endusers/{endUserId}/workouts/unassign` - remove this org's workout access (idempotent; missing / other-org ids ignored)
- `POST /v1/orgs/{organizationId}/endusers/{endUserId}/plans/assign` - add catalog programs to the member library (max 5; does **not** start the plan; start does not require assign)
- `POST /v1/orgs/{organizationId}/endusers/{endUserId}/plans/unassign` - remove this org's plan access (idempotent; deleting the active plan ends enrollment — stronger than `POST .../plans/{planId}/end`)
- `POST /v1/cast/pairing/join-by-code` - pair a controller with the member-web `https://member.hyperhuman.cc/cast` TV page (6-digit code; OpenAPI tag `workouts-cast`; one controller per session; team-scoped keys pinned to their org)
- `POST /v1/cast/pairing/{sessionId}/workout` · `.../workout/start` · `.../workout/control` - load, start (optional session attribution), and control playback (`play`|`pause`|`next`|`prev`|`restart`|`done_exercise`|`done`); REST broadcasts to the display

## Common pitfalls (do not invent)

- Workout **generate/adapt** = ephemeral JSON (no workout id). Pass the response as `HYPERHUMAN_EMBED_INIT.workout` to the JSON-receiver **iframe**; media = `GET /v1/video-asset/{exerciseId}`. Do not mint local hex ids and poll `/playlist`. Plan **generate** top-level `id` only with `endUserProfileDetails.endUserId`. Plan **adapt** requires `endUserProfileId`.
- Generate free-text is `userRequirements`; adapt is `userGuidance` (the other name → **400**). Keep the HTTP client open ≥ **120s**; a client abort can still finish 200 — do not immediately retry.
- **Programming DNA on this API is opt-in on generate.** Pass `methodologyProfileId` from `GET .../methodologies` (up to 10 published team profiles; **Set as team guideline** in Teams). Organization keys: omit = no team methodology (`isTeamDefault` is informational). Unknown / personal / inactive ids return **400**. Hyperhuman Club / member web apply the **team default as guidance** when one exists — members do not send this field; text is not a lock. Recommend, adapt, and chat on **this API** do not apply a team default. `userRequirements` wins over methodology text; hard rules and platform bounds (pool, safety, duration, structure) do not. The generate **Generation Inspector** is `methodology` plus `inspection` (`hardRulesApplied`, `violations`, `complianceStatus` of `compliant` | `partial` | `not_applicable`, `generationId`). Store that snapshot yourself — there is **no** `GET /v1/generations/{id}`, and later plan GET does not replay it. Text guidelines are guidance, not a guarantee; hard-rule results in `inspection` are deterministic. Workout preflight is **1x** and does not create a workout. Plan generate has no preflight.
- There is **no** `GET /v1/workouts` list, **no** `GET .../library`, **no** `GET /v1/categories` / `GET /v1/workouts/categories`. Catalog is `GET /v1/orgs/{organizationId}/workouts` (empty **200** is valid). Categories are `GET /v1/workouts/metadata`.
- Source fields: plan generate = `workoutCollectionSources`; plan adapt = `workoutSources`. Workout generate `exerciseSources` is the final catalog — team-only / premium-only / team+premium with 0 clips → **400** (not a free-stock or template workout). Omit/empty `exerciseSources` still defaults to free+premium. Optional `categoryIds` on workout recommend + plan generate hard-filters the public workout pool.
- **Plan = Program.** Visibility is `public` | `private` only. Workouts have **categories** (AI pools / quality) and optional **tags** (library filter/search only). Categories filter; Programs sequence (`GET /v1/plans/{id}/workouts`). Tags do not filter recommend/generate. Retire from AI pools with `private`; get-by-id still plays.
- Recommend responses are bare `{ workouts|plans, reasoning }` (not `{ data }`). Per-item `recommendation` is text, not a score.
- Org metadata `ai*Enabled` = member-app visibility, not Content API gates.
- JSON-receiver embeds have **no PulseMix** and **no** session/feedback (`sessionMode` on INIT is ignored for JSON). Always frame the player URLs. Do not resolve media until a non-empty `apiKey` is present. Hosted-embed GETs count as **1x** on that key.

- Feedback `ratingId` / `difficultyId` are **option ids** from `GET /v1/workouts/feedback/options` (24-character hex), not labels like `rating-5-stars` (those → `400`).
- **Assign vs start vs end vs unassign:** `.../plans/assign` puts a program in the library (does not set `isActive`). `.../plans/start` enrolls and does not require a prior assign. `GET .../endusers/{endUserId}/plans` is the org library, not `GET .../plans/active`. Assign: ineligible id → `400` all-or-nothing. Unassign: missing or other-org ids ignored (org-owned only; retired org content can still be removed). `.../plans/{planId}/end` clears `isActive` and keeps the row; `.../plans/unassign` deletes the row (and enrollment if that plan was active).
- **HyperCast:** controllers join (`join-by-code` / `join`); do not create or terminate sessions. Omit `workoutData`. Team-scoped keys cannot join another organization's session (`403`). Cast start attribution is fields + API key only (Bearer does not win); a non-member identifier → `404` and the TV does not play. Disconnect does not stop TV playback. Poll `GET .../workout/status` after a socket drop.
- Prefer [`AGENTS.md`](https://content.api.hyperhuman.cc/AGENTS.md) section 9 for the full pitfall list.

## Per-endpoint catalog

A complete catalog (with method, path, summary, tags, and stable `operationId`s) is appended live to `/llms-full.txt` from the current `/openapi.json`. Use that file as the single context drop for an LLM.

## Operations by tag (live)

### workouts

- `GET /v1/workouts/metadata` `WorkoutsApi_getWorkoutMetadata` — Get available workout categories and difficulties
- `GET /v1/workouts/equipment/metadata` `WorkoutsApi_getEquipmentMetadata` — Get available equipment categories and equipment items
- `GET /v1/workouts/{workoutId}` `WorkoutsApi_getWorkout` — Get workout details
- `GET /v1/workouts/{workoutId}/export/video/stream_url` `WorkoutsApi_getExportVideoStream` — Get complete workout video export URL
- `GET /v1/workouts/{workoutId}/export/audio/stream_url` `WorkoutsApi_getExportAudioStream` — Get audio-only workout export URL
- `GET /v1/workouts/exercises/metadata` `WorkoutsApi_getExercisesMetadata` — Get available muscle groups and exercise collections
- `GET /v1/orgs/{organizationId}/workouts` `OrganizationWorkoutsApi_getOrganizationWorkouts` — List organization-specific workouts
- `GET /v1/workouts/{workoutId}/playlist` `WorkoutContentApi_getWorkoutPlaylist` — Retrieve Detailed Workout Playlist Structure
- `GET /v1/workouts/{workoutId}/music` `WorkoutContentApi_getWorkoutMusic` — Get workout music playlist
- `POST /v1/workouts/{workoutId}/sessions/start` `WorkoutsSessionsApi_startWorkoutSession` — Start workout session
- `PATCH /v1/workouts/{workoutId}/sessions/{sessionId}` `WorkoutsSessionsApi_patchWorkoutSession` — Update a workout session
- `GET /v1/workouts/{workoutId}/sessions/{sessionId}` `WorkoutsSessionsApi_getWorkoutSession` — Get specific workout session details
- `POST /v1/workouts/{workoutId}/sessions/end` `WorkoutsSessionsApi_endWorkoutSession` — End a workout session
- `POST /v1/workouts/{workoutId}/sessions/complete` `WorkoutsSessionsApi_completeWorkoutSession` — Complete workout session with 100% progress
- `GET /v1/workouts/{workoutId}/sessions/recent` `WorkoutsSessionsApi_getRecentWorkoutSession` — Get most recent workout session
- `GET /v1/workouts/feedback/options` `WorkoutsFeedbackApi_getWorkoutFeedbackOptions` — Get workout feedback options
- `POST /v1/workouts/{workoutId}/feedback` `WorkoutsFeedbackApi_submitWorkoutFeedback` — Submit workout feedback

### workouts-cast

- `POST /v1/cast/pairing/session` `CastApi_createPairingSession` — Create a pairing session (display side)
- `GET /v1/cast/pairing/session/{sessionId}` `CastApi_getPairingSession` — Get pairing session snapshot
- `DELETE /v1/cast/pairing/session/{sessionId}` `CastApi_terminatePairingSession` — Terminate a pairing session (display side)
- `POST /v1/cast/pairing/join` `CastApi_joinPairingSession` — Join a pairing session (QR scan)
- `POST /v1/cast/pairing/join-by-code` `CastApi_joinPairingSessionByCode` — Join a pairing session (6-digit code)
- `POST /v1/cast/pairing/{sessionId}/workout` `CastApi_castWorkout` — Load a workout on the display
- `POST /v1/cast/pairing/{sessionId}/workout/start` `CastApi_startWorkoutSession` — Start cast workout playback
- `POST /v1/cast/pairing/{sessionId}/workout/control` `CastApi_controlWorkout` — Send a workout control command
- `GET /v1/cast/pairing/{sessionId}/workout/status` `CastApi_getWorkoutStatus` — Get cast workout status
- `POST /v1/cast/pairing/{sessionId}/device/disconnect` `CastApi_disconnectDevice` — Unpair the controller

### plans

- `GET /v1/plans/metadata` `PlansApi_getPlanMetadata` — Get available plan goals and difficulties
- `GET /v1/plans/{planId}` `PlansApi_getPlan` — Get training plan details
- `GET /v1/plans/{planId}/workouts` `PlansApi_getPlanWorkouts` — Get workouts in training plan
- `GET /v1/orgs/{organizationId}/plans` `OrganizationPlansApi_getOrganizationPlans` — List organization-specific training plans

### library-exercises

- `GET /v1/orgs/{organizationId}/video-assets` `OrganizationExercisesApi_getOrganizationExercises` — List workspace exercise videos for an organization

### library-stock-exercises

- `GET /v1/orgs/{organizationId}/stock-exercises` `OrganizationStockExercisesApi_getStockExercises` — List browsable stock exercise catalog

### exercise-groups

- `GET /v1/orgs/{organizationId}/groups` `OrganizationExerciseGroupsApi_getOrganizationExerciseGroups` — List organization micro workouts (exercise groups)
- `GET /v1/orgs/{organizationId}/groups/{groupId}` `OrganizationExerciseGroupsApi_getOrganizationExerciseGroupById` — Get micro workout (exercise group) by ID
- `GET /v1/orgs/{organizationId}/groups/{groupId}/exercises` `OrganizationExerciseGroupsApi_getOrganizationGroupExercises` — Get exercises in micro workout

### ai-generation

- `POST /v1/orgs/{organizationId}/workouts/generate` `OrganizationWorkoutGeneration_generateWorkout` — Generate personalized on-the-fly workouts based on user profile details, duration preferences, and exercise sources.
- `POST /v1/orgs/{organizationId}/workouts/generate/preflight` `OrganizationWorkoutGeneration_generateWorkoutPreflight` — Dry-run workout generate: resolve methodology and check hard Programming DNA rules
- `GET /v1/orgs/{organizationId}/methodologies` `OrganizationMethodologies_list` — List team Programming DNA profiles for this organization
- `POST /v1/orgs/{organizationId}/plans/generate` `OrganizationPlanGeneration_generatePlan` — Generate personalized training programs based on user profile details, duration preferences, and workout collection sources.

### ai-recommendation

- `POST /v1/orgs/{organizationId}/workouts/recommend` `OrganizationWorkoutRecommendation_getWorkoutRecommendations` — Get workout recommendations from existing library
- `POST /v1/orgs/{organizationId}/plans/recommend` `OrganizationPlanRecommendation_getPlanRecommendations` — Get training plan recommendations from existing library

### ai-adaptation

- `POST /v1/orgs/{organizationId}/workouts/{workoutId}/adapt` `OrganizationWorkoutAdaptation_adaptWorkout` — Adapt an existing team workout based on user profile and data
- `POST /v1/orgs/{organizationId}/plans/{planId}/adapt` `OrganizationPlanAdaptation_adaptPlan` — Adapt an existing program based on user profile and data

### organization

- `GET /v1/orgs/{organizationId}/metadata` `OrganizationMetadataApi_getOrganizationMetadata` — Get organization metadata
- `GET /v1/orgs/{organizationId}/branding/news` `OrganizationBrandingNewsApi_getOrganizationBrandingNews` — Get organization branding news feed
- `GET /v1/orgs/{organizationId}/join-options` `OrganizationJoinOptionsApi_getOrganizationJoinOptions` — Get organization join options
- `GET /v1/orgs/{organizationId}/stats` `OrganizationStatsApi_getOrganizationStats` — Get organization API usage statistics

### social

- `GET /v1/orgs/{organizationId}/drops` `OrganizationDropsApi_getOrganizationDrops` — List organization social drops
- `GET /v1/orgs/{organizationId}/drops/{dropId}` `OrganizationDropsApi_getOrganizationDropById` — Get drop by ID
- `POST /v1/orgs/{organizationId}/drops/{dropId}/export` `OrganizationDropsApi_exportDrop` — Export drop for channel bundle

### content-autopilot

- `GET /v1/orgs/{organizationId}/content-autopilot` `OrganizationContentAutopilotApi_listTasks` — List content autopilot tasks
- `POST /v1/orgs/{organizationId}/content-autopilot` `OrganizationContentAutopilotApi_createTask` — Create content autopilot task
- `GET /v1/orgs/{organizationId}/content-autopilot/events` `OrganizationContentAutopilotApi_listTeamEvents` — List execution events across all tasks
- `GET /v1/orgs/{organizationId}/content-autopilot/{taskId}` `OrganizationContentAutopilotApi_getTask` — Get autopilot task by ID
- `PATCH /v1/orgs/{organizationId}/content-autopilot/{taskId}` `OrganizationContentAutopilotApi_updateTask` — Update autopilot task configuration
- `DELETE /v1/orgs/{organizationId}/content-autopilot/{taskId}` `OrganizationContentAutopilotApi_deleteTask` — Delete autopilot task (soft delete)
- `POST /v1/orgs/{organizationId}/content-autopilot/{taskId}/activate` `OrganizationContentAutopilotApi_activateTask` — Activate autopilot task
- `POST /v1/orgs/{organizationId}/content-autopilot/{taskId}/deactivate` `OrganizationContentAutopilotApi_deactivateTask` — Deactivate autopilot task
- `POST /v1/orgs/{organizationId}/content-autopilot/{taskId}/run-now` `OrganizationContentAutopilotApi_runNow` — Manually trigger task execution
- `GET /v1/orgs/{organizationId}/content-autopilot/{taskId}/events` `OrganizationContentAutopilotApi_listTaskEvents` — List execution events for a task

### social-publish

- `GET /v1/orgs/{organizationId}/social-publish/connected-platforms` `OrganizationSocialPublishApi_getConnectedPlatforms` — List connected social platforms
- `GET /v1/orgs/{organizationId}/social-publish` `OrganizationSocialPublishApi_listTasks` — List scheduled social publish automations
- `POST /v1/orgs/{organizationId}/social-publish` `OrganizationSocialPublishApi_createTask` — Create a social publish automation (draft)
- `GET /v1/orgs/{organizationId}/social-publish/{taskId}` `OrganizationSocialPublishApi_getTask` — Get one social publish automation
- `PATCH /v1/orgs/{organizationId}/social-publish/{taskId}` `OrganizationSocialPublishApi_patchTask` — Update a social publish automation
- `DELETE /v1/orgs/{organizationId}/social-publish/{taskId}` `OrganizationSocialPublishApi_deleteTask` — Delete a social publish automation (soft delete)

### endusers-ai-insights

- `GET /v1/orgs/{organizationId}/endusers/{endUserId}/insights/digest` `UserInsightsApi_getUserDailyDigest` — Get daily AI-powered user insights digest
- `GET /v1/orgs/{organizationId}/endusers/{endUserId}/insights/pillars/{pillarType}` `UserInsightsApi_getPillarDetail` — Get detailed view of a specific insight pillar
- `POST /v1/orgs/{organizationId}/endusers/{endUserId}/insights/health-data` `OrganizationEndUserHealthDataApi_logHealthData` — Push health data for an end user (batch)
- `DELETE /v1/orgs/{organizationId}/endusers/{endUserId}/insights/health-data/{dataType}/{externalId}` `OrganizationEndUserHealthDataApi_deleteHealthDataEntry` — Delete a pushed health data entry

### endusers-ai-chat

- `POST /v1/chat/conversations` `ChatApi_startConversation` — Start a new chat conversation
- `GET /v1/chat/conversations` `ChatApi_getConversations` — Get user conversations
- `GET /v1/chat/conversations/{conversationId}/messages` `ChatApi_getConversationMessages` — Get conversation messages
- `POST /v1/chat/conversations/{conversationId}/messages` `ChatApi_sendMessage` — Send message to conversation
- `POST /v1/chat/conversations/{conversationId}/actions/confirm` `ChatApi_confirmAction` — Confirm a pending AI action
- `DELETE /v1/chat/conversations/{conversationId}` `ChatApi_archiveConversation` — Archive conversation

### endusers-nutrition

- `POST /v1/orgs/{organizationId}/endusers/{endUserId}/nutrition/photo-log` `OrganizationEndUserNutritionApi_analyzeNutritionPhoto` — Analyze a meal photo for nutrition logging
- `POST /v1/orgs/{organizationId}/endusers/{endUserId}/nutrition/photo-log/{pendingLogId}/confirm` `OrganizationEndUserNutritionApi_confirmNutritionPhotoLog` — Confirm a pending photo nutrition log
- `POST /v1/orgs/{organizationId}/endusers/{endUserId}/nutrition/log` `OrganizationEndUserNutritionApi_logNutrition` — Log nutrition for an end user
- `DELETE /v1/orgs/{organizationId}/endusers/{endUserId}/nutrition/log/{externalId}` `OrganizationEndUserNutritionApi_deleteNutritionLog` — Delete a logged nutrition entry

### endusers-content

- `GET /v1/orgs/{organizationId}/endusers/{endUserId}/workouts` `OrganizationEndUserContentApi_listWorkouts` — List workouts in an end user library
- `POST /v1/orgs/{organizationId}/endusers/{endUserId}/workouts/assign` `OrganizationEndUserContentApi_assignWorkouts` — Assign workouts to an end user library
- `POST /v1/orgs/{organizationId}/endusers/{endUserId}/workouts/unassign` `OrganizationEndUserContentApi_unassignWorkouts` — Remove workouts from an end user library
- `GET /v1/orgs/{organizationId}/endusers/{endUserId}/plans` `OrganizationEndUserContentApi_listPlans` — List programs in an end user library
- `POST /v1/orgs/{organizationId}/endusers/{endUserId}/plans/assign` `OrganizationEndUserContentApi_assignPlans` — Assign programs to an end user library
- `POST /v1/orgs/{organizationId}/endusers/{endUserId}/plans/unassign` `OrganizationEndUserContentApi_unassignPlans` — Remove programs from an end user library

### endusers-plans

- `GET /v1/orgs/{organizationId}/endusers/{endUserId}/plans/active/progress` `OrganizationEndUserPlansApi_getActivePlanProgress` — Get plan progress for an end user
- `GET /v1/orgs/{organizationId}/endusers/{endUserId}/plans/active` `OrganizationEndUserPlansApi_getActivePlan` — Get active plan for an end user
- `POST /v1/orgs/{organizationId}/endusers/{endUserId}/plans/start` `OrganizationEndUserPlansApi_startPlan` — Start a plan for an end user
- `PATCH /v1/orgs/{organizationId}/endusers/{endUserId}/plans/active/settings` `OrganizationEndUserPlansApi_updateActivePlanSettings` — Update active plan settings for an end user
- `POST /v1/orgs/{organizationId}/endusers/{endUserId}/plans/{planId}/end` `OrganizationEndUserPlansApi_endPlan` — End an active plan for an end user

### endusers-management

- `GET /v1/orgs/{organizationId}/endusers` `OrganizationEndUsersApi_getOrganizationEndUsers` — Get organization end users
- `DELETE /v1/orgs/{organizationId}/endusers` `OrganizationEndUsersApi_removeEndUser` — Remove end user from organization
- `POST /v1/orgs/{organizationId}/endusers/invite` `OrganizationEndUsersApi_inviteEndUsers` — Invite end users to organization
- `PUT /v1/orgs/{organizationId}/endusers/suspend` `OrganizationEndUsersApi_suspendEndUser` — Suspend end user in organization
- `PUT /v1/orgs/{organizationId}/endusers/reactivate` `OrganizationEndUsersApi_reactivateEndUser` — Reactivate end user in organization
- `GET /v1/orgs/{organizationId}/endusers/find` `OrganizationEndUsersApi_getEndUserById` — Find end user by email or external UUID
