# Hyperhuman Content API

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

> Last updated: 2026-04-30. 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: `offset` and `limit` (no `page`). **Max `50`** on most list endpoints; **`100`** for `GET /v1/orgs/{organizationId}/endusers`. Organization **workout and plan** library lists and **`GET /v1/orgs/{organizationId}/video-assets`** use **`limit` 10 in the service** when `limit` is **omitted** (pass it explicitly for 20/50) — see [AGENTS.md](https://content.api.hyperhuman.cc/AGENTS.md) section 4.
- Localization: pass `locale` (BCP-47, e.g. `en-US`, `fr-FR`); falls back to English when unsupported
- AI / heavy-video endpoints (recommend, generate, adapt, insights, chat, video generation, full video export) count as **10x** rate-limit weight
- **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. 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 workouts and plans (`GET /v1/orgs/{organizationId}/workouts`, `GET /v1/orgs/{organizationId}/plans`), and browse workspace exercise videos (`GET /v1/orgs/{organizationId}/video-assets`).
2. **AI Recommend** - rank library content for the user (`POST /v1/orgs/{organizationId}/workouts/recommend`, `POST /v1/orgs/{organizationId}/plans/recommend`).
3. **AI Generate** - create new content on the fly (`POST /v1/orgs/{organizationId}/workouts/generate`, `POST /v1/orgs/{organizationId}/plans/generate`).
4. **AI Adapt** - evolve existing content as the user progresses (`POST /v1/orgs/{organizationId}/workouts/{workoutId}/adapt`, `POST /v1/orgs/{organizationId}/plans/{planId}/adapt`).
5. **AI Insights** - daily digest + per-pillar drill-downs (`GET /v1/orgs/{organizationId}/endusers/{endUserId}/insights/digest`).

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.

## 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
- [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 (production base `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`.

## Top endpoints (read-mostly)

- `GET /v1/workouts/metadata` - categories, equipment, muscle groups, supported locales
- `GET /v1/orgs/{organizationId}/workouts` - paginated workout library for an org
- `GET /v1/workouts/{workoutId}` - full workout document
- `GET /v1/workouts/{workoutId}/playlist?locale=en-US` - segments with HLS streams (drives the player)
- `GET /v1/workouts/{workoutId}/music` - pre-signed background music tracks
- `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 training plan library
- `GET /v1/plans/{planId}` - full plan document
- `GET /v1/orgs/{organizationId}/metadata` - org branding (logo, colors, watermark) and module flags
- `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=YYYY-MM-DD` - daily AI insights digest
- `GET /v1/orgs/{organizationId}/endusers/{endUserId}/insights/pillars/{pillarType}` - drill-down for a specific pillar

## Top endpoints (mutating)

- `POST /v1/orgs/{organizationId}/workouts/recommend` - rank existing workouts for a user
- `POST /v1/orgs/{organizationId}/plans/recommend` - rank existing plans for a user
- `POST /v1/orgs/{organizationId}/workouts/generate` - create an AI-generated workout
- `POST /v1/orgs/{organizationId}/plans/generate` - create an AI-generated multi-week plan
- `POST /v1/orgs/{organizationId}/workouts/{workoutId}/adapt` - personalize an existing team workout
- `POST /v1/orgs/{organizationId}/plans/{planId}/adapt` - personalize an existing team plan
- `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
- `POST /v1/workouts/{workoutId}/feedback` - submit session feedback

## 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.

---

## Full operation reference

Each block below mirrors a single operation in the OpenAPI document. The `operationId` is stable and suitable for use as a tool / function name.

## Tag: workouts

### GET /v1/workouts/metadata

- operationId: `WorkoutsApi_getWorkoutMetadata`
- summary: Get available workout categories and difficulties

Retrieves all unique workout categories (e.g., HIIT, Strength, Mobility, Cardio) and difficulty levels available across workouts.

**Use this endpoint to populate:**
- `categoryId` parameter in `POST /v1/orgs/{orgId}/workouts/generate`
- Workout category filters in your UI

**Response includes:**
- `categories`: Array of {id, name} objects for each workout category
- `difficulties`: Array of difficulty strings (beginner, intermediate, advanced)

**Example Categories:**
- HIIT (615aa3f0dbe7140012c59e94)
- Strength (615aa3dadbe7140012c59e90)
- Mobility (615aa3e5dbe7140012c59e92)
- Cardio (615aa3ebdbe7140012c59e93)
- Yoga (649edb81733e2e007a30ab8d)
- Pilates (615aa3f5dbe7140012c59e95)

**Related Metadata Endpoints:**
- `GET /v1/workouts/metadata` - Categories & difficulties (for workout generation/filtering)
- `GET /v1/workouts/equipment/metadata` - Equipment items & categories
- `GET /v1/workouts/exercises/metadata` - Muscle groups & collections
- `GET /v1/plans/metadata` - Goals & difficulties (for plan generation)

**For Workout Generation:** Call this endpoint + equipment/metadata + exercises/metadata (advanced)

---

### 🌍 Localization

Content localized based on `locale` query parameter:
- **Supported**: 13 languages (en-US, fr-FR, de-DE, es-ES, etc.)
- **Fallback**: English if locale unsupported
- **Affects**: Category names

---

**Related Endpoints:**
- Generate: `POST /v1/orgs/{orgId}/workouts/generate`
- List: `GET /v1/orgs/{orgId}/workouts`
- Recommend: `POST /v1/orgs/{orgId}/workouts/recommend`

**Parameters:**
- `locale` (query) — Preferred language locale (e.g., fr-FR)

**Responses:**
- 200 — Successfully retrieved category and difficulty lists.
- 401 — Authentication failed - API key is missing or invalid.

### GET /v1/workouts/equipment/metadata

- operationId: `WorkoutsApi_getEquipmentMetadata`
- summary: Get available equipment categories and equipment items

Retrieves all equipment items (e.g., Dumbbells, Kettlebells, Resistance Bands) and their categories (e.g., Free Weights, Bodyweight, Gym Equipment).

**Use this endpoint to populate:**
- `preferredEquipmentCategoryIds` in workout generation requests
- Equipment filters in your application UI

**Response includes:**
- `categories`: Equipment categories with {id, name, category} 
- `equipment`: Individual equipment items with {id, name, equipmentCategoryId}

**Example Equipment Categories:**
- Bodyweight (67deb36a6a5e49797c122f56)
- Free Weights (67deb3696a5e49797c122f55)
- Gym Equipment (67deb36a6a5e49797c122f57)

**Example Equipment Items:**
- Dumbbell (60812dea606fc90011eb4405) → Free Weights
- Kettlebell (60812dd5606fc90011eb4404) → Free Weights
- Mat (60812e08606fc90011eb4408) → Bodyweight
- Resistance Band (64f9800b3b07122623b91c7d) → Free Weights

---

### 🌍 Localization

Content localized based on `locale` query parameter:
- **Supported**: 13 languages (en-US, fr-FR, de-DE, es-ES, etc.)
- **Fallback**: English if locale unsupported
- **Affects**: Equipment names, equipment category names

---

**Related Metadata Endpoints:**
- `GET /v1/workouts/equipment/metadata` - Equipment items & categories
- `GET /v1/workouts/metadata` - Categories & difficulties
- `GET /v1/workouts/exercises/metadata` - Muscle groups & collections

**Related Endpoints:**
- Generate: `POST /v1/orgs/{orgId}/workouts/generate`
- List: `GET /v1/orgs/{orgId}/workouts`

**Parameters:**
- `locale` (query) — Preferred language locale (e.g., fr-FR)

**Responses:**
- 200 — Successfully retrieved equipment and category lists.
- 401 — Authentication failed - API key is missing or invalid.

### GET /v1/workouts/{workoutId}

- operationId: `WorkoutsApi_getWorkout`
- summary: Get workout details

## Complete Workout Details

Retrieves comprehensive workout information including metadata, equipment requirements, muscle group distribution, and media assets.

---

### 📋 What's Included

| Field | Description | Example |
|-------|-------------|---------|
| **Core Info** | Name, description, duration, difficulty | "Full Body HIIT", 1800 seconds |
| **Categories** | Workout type classifications | ["HIIT", "Cardio"] |
| **Equipment** | Required/recommended gear | ["Dumbbells", "Mat"] |
| **Muscle Groups** | Percentage distribution (sorted) | Chest: 35%, Arms: 25% |
| **Media Assets** | Preview videos, images, audio | HLS streams, thumbnails |
| **Trainer Info** | Coach details and organization | Name, bio, team |

---

### 🎯 Use Cases

✅ **Workout Preview** - Show details before starting  
✅ **Equipment Check** - Verify user has required gear  
✅ **Difficulty Assessment** - Match to user fitness level  
✅ **Content Discovery** - Browse workout library

---

### 🔄 Integration Flow

1. **Browse Workouts**: `GET /v1/workouts` → Get workout list
2. **Get Details**: `GET /v1/workouts/{id}` → **This endpoint** (full details)
3. **Check Equipment**: Review equipment array for user compatibility
4. **Start Session**: `POST /v1/workouts/{id}/sessions/start`
5. **Get Playlist**: `GET /v1/workouts/{id}/playlist` → Detailed segments

---

### 🌍 Localization

Content localized based on `locale` query parameter:
- **Supported**: 13 languages (en-US, fr-FR, de-DE, es-ES, etc.)
- **Fallback**: English if locale unsupported
- **Affects**: Name, description, equipment names, muscle group names

---

### 🔗 Related Endpoints

- Workout List: `GET /v1/workouts` - Browse all workouts
- Workout Playlist: `GET /v1/workouts/{id}/playlist` - Get detailed segments
- Start Session: `POST /v1/workouts/{id}/sessions/start` - Begin tracking
- Submit Feedback: `POST /v1/workouts/{id}/feedback` - Rate workout

**Parameters:**
- `workoutId` (path) (required) — The unique identifier (UUID) of the workout to retrieve.
- `locale` (query) — Preferred language locale (e.g., fr-FR)

**Responses:**
- 200 — Workout details retrieved successfully.
- 401 — Authentication failed - API key is missing, invalid, or Bearer token is missing/invalid.
- 404 — The specified workout ID does not exist.

### GET /v1/workouts/{workoutId}/export/video/stream_url

- operationId: `WorkoutsApi_getExportVideoStream`
- summary: Get complete workout video export URL

## Full Workout Video Export

Returns a pre-signed S3 URL to a **complete, fully-rendered workout video** for the requested locale. This is typically a single file containing the entire workout from start to finish, including all exercises, rest periods, audio narration, graphics, and timing overlays.

**Response shape:** HTTP **200** with **Content-Type: text/plain**. The body is a **single** URL string (not JSON). The URL is presigned and valid for **7 days**. The object at that URL matches whatever the export pipeline stored (commonly **MP4**; **HLS** or other packaging is possible in some cases—use the file extension and response headers when fetching the URL).

---

### 📋 Video Details

**Format:** MP4 (H.264)  
**Resolution:** 1080p (1920x1080)  
**Frame Rate:** 30 fps  
**Codec:** H.264 (High Profile)  
**Audio:** AAC stereo, embedded in video  
**File Size:** Typically 100-500 MB depending on workout length  
**Expiration:** Pre-signed URL valid for 7 days  
**Protocol:** HTTPS  
**Content Type:** video/mp4

---

### 🎯 Use Cases

This endpoint is ideal for:
- **Simple video playback** - Direct download and play the complete workout
- **Offline viewing** - Download the full video for offline use
- **Social sharing** - Share the complete workout video
- **Video archival** - Store the full workout as a single file
- **Third-party players** - Use any standard MP4-compatible video player

---

### 🎬 Integration Examples

**Basic HTML5 Video Player:**
```javascript
// Fetch the video URL
const videoUrl = await fetch(
  `/v1/workouts/${workoutId}/export/video/stream_url?locale=en-US`,
  { headers: { 'X-Api-Key': apiKey } }
).then(r => r.text());

// Use standard HTML5 video player
const video = document.createElement('video');
video.src = videoUrl;
video.controls = true;
video.width = 1920;
video.height = 1080;
document.body.appendChild(video);
```

**iOS/Swift AVPlayer:**
```swift
let videoURL = URL(string: videoUrlString)!
let player = AVPlayer(url: videoURL)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
present(playerViewController, animated: true) {
    player.play()
}
```

**Android/Kotlin ExoPlayer:**
```kotlin
val player = ExoPlayer.Builder(context).build()
playerView.player = player
val mediaItem = MediaItem.fromUri(videoUrl)
player.setMediaItem(mediaItem)
player.prepare()
player.play()
```

**Download for Offline Use:**
```javascript
async function downloadWorkoutVideo(workoutId, filename) {
  const videoUrl = await fetch(
    `/v1/workouts/${workoutId}/export/video/stream_url`,
    { headers: { 'X-Api-Key': apiKey } }
  ).then(r => r.text());
  
  const response = await fetch(videoUrl);
  const blob = await response.blob();
  const url = window.URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename || 'workout.mp4';
  a.click();
}
```

---

### 🌍 Localization Support

The video export includes localized content based on the `locale` parameter:
- **Audio narration** - Voice instructions in the requested language
- **Text overlays** - Exercise names, timers, and instructions in local language
- **Exercise names** - Localized exercise titles

**Supported locales:**
en-US, en-GB, en-AU, fr-FR, de-DE, es-ES, it-IT, pt-PT, he-IL, ro-RO, cs-CZ, fi-FI, nl-NL

**Locale fallback behavior:**
1. If requested locale exists → return that version
2. If requested locale not available but user's preferred locale exists → return preferred version
3. Otherwise → return English (en-US) version
4. If no export available → return 404

---

### ⚡ Video Generation Status

Workout videos are **pre-generated asynchronously** after workout creation or modification. 

**Possible scenarios:**
- ✅ **Video ready** - Returns URL immediately (typical case)
- ⏳ **Video processing** - Returns 404 if generation still in progress (usually 2-5 minutes)
- ❌ **Generation failed** - Returns 404 if export job failed

**Best practice:** Check `GET /v1/workouts/{id}` response for video availability before requesting stream URL.

---

### 💡 Best Practices

✅ **Cache the URL** - Save in memory/storage for up to 6 days  
✅ **Refresh before expiration** - Fetch new URL when approaching 7-day limit  
✅ **Handle 404 gracefully** - Video may still be processing  
✅ **Support offline download** - Allow users to download for offline viewing  
✅ **Show file size** - Inform users about download size (typically 100-500 MB)  
✅ **Implement retry logic** - Retry with exponential backoff if initial request fails  
✅ **Consider bandwidth** - These are large files; provide quality/size warnings on mobile

---

### 🔄 Workflow Comparison

**Option A: Complete Video (This Endpoint)**
```
GET /v1/workouts/{id}/export/video/stream_url
→ Single presigned URL to the full-workout video export
→ Simple playback, no segment control
→ Ideal for: Downloads, simple players, sharing
```

**Option B: Segmented Playlist**
```
GET /v1/workouts/{id}/playlist
→ Individual exercise videos + timing
→ Full control over each segment
→ Ideal for: Custom players, skip exercises, progress tracking
```

---

### Rate Limit

This endpoint counts as 10x API calls toward your hourly and daily limits.

---

### 🔗 Related Endpoints

- **Workout Details:** `GET /v1/workouts/{id}` - Get workout metadata
- **Workout Playlist:** `GET /v1/workouts/{id}/playlist` - Get segment-by-segment structure
- **Audio Only:** `GET /v1/workouts/{id}/export/audio/stream_url` - Get audio-only version
- **Start Session:** `POST /v1/workouts/{id}/sessions/start` - Track workout session

**Parameters:**
- `workoutId` (path) (required) — The unique identifier of the workout.
- `locale` (query) — Preferred language locale (e.g., de-DE, fr-FR, en-US). If specified and no export exists for this locale, returns 404. Falls back to user's preferred locale or en-US.

**Responses:**
- 200 — text/plain body: a single presigned HTTPS URL to the full video export (valid 7 days). Format of the linked asset matches the stored render (commonly MP4; see operation description).
- 401 — Authentication failed - API key is missing, invalid, or Bearer token is missing/invalid.
- 404 — Workout not found, video export not available for the requested locale, or video is still being generated (processing typically takes 2-5 minutes after workout creation).

### GET /v1/workouts/{workoutId}/export/audio/stream_url

- operationId: `WorkoutsApi_getExportAudioStream`
- summary: Get audio-only workout export URL

## Audio-Only Workout Export

Returns a pre-signed S3 URL to the **complete audio narrative** for the workout. This is ideal for audio-guided workout experiences where users prefer voice-only instruction without video.

**Response shape:** HTTP **200** with **Content-Type: text/plain**. The body is a **single** URL string (not JSON), presigned and valid for **7 days**, pointing at the generated narrative audio object (commonly **M4A**).

---

### 📋 Audio Details

**Format:** M4A (AAC)  
**Quality:** 128 kbps stereo  
**File Size:** Typically 5-20 MB depending on workout length  
**Content:** Voice narration, exercise instructions, timing cues, background audio  
**Expiration:** Pre-signed URL valid for 7 days  
**Protocol:** HTTPS  
**Content Type:** audio/mp4

---

### 🎧 Use Cases

Perfect for:
- **Audio-only workouts** - Voice-guided training without video overhead
- **Background play** - Users can multitask while working out
- **Low bandwidth** - Only 5-20 MB vs 100-500 MB for video
- **Accessibility** - Screen-free workout experience for visually impaired users
- **Outdoor activities** - Listen while running, cycling, or hiking
- **Podcast-style workouts** - Portable fitness content
- **Mobile data savings** - Significantly reduced data consumption

---

### 🎬 Integration Examples

**Basic HTML5 Audio:**
```javascript
// Fetch audio URL
const audioUrl = await fetch(
  `/v1/workouts/${workoutId}/export/audio/stream_url?locale=en-US`,
  { headers: { 'X-Api-Key': apiKey } }
).then(r => r.text());

// Simple audio player
const audio = new Audio(audioUrl);
audio.play();

// Or with controls
const player = document.createElement('audio');
player.src = audioUrl;
player.controls = true;
document.body.appendChild(player);
```

**Custom Audio Player with Progress Tracking:**
```javascript
const audio = new Audio(audioUrl);

// Track progress
audio.addEventListener('timeupdate', () => {
  const progress = (audio.currentTime / audio.duration) * 100;
  console.log(`Workout progress: ${progress.toFixed(1)}%`);
});

// Handle completion
audio.addEventListener('ended', () => {
  console.log('Workout completed!');
  // Submit workout session completion
});

// Play with controls
audio.play();
```

**iOS/Swift AVAudioPlayer:**
```swift
let audioURL = URL(string: audioUrlString)!
do {
    let audioData = try Data(contentsOf: audioURL)
    audioPlayer = try AVAudioPlayer(data: audioData)
    audioPlayer?.prepareToPlay()
    audioPlayer?.play()
} catch {
    print("Error loading audio: \(error)")
}
```

**Android/Kotlin MediaPlayer:**
```kotlin
val mediaPlayer = MediaPlayer().apply {
    setDataSource(audioUrl)
    prepareAsync()
    setOnPreparedListener { start() }
}
```

**Background Audio with Web Audio API:**
```javascript
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const audioElement = new Audio(audioUrl);
const track = audioContext.createMediaElementSource(audioElement);
track.connect(audioContext.destination);

// Enable background playback
if ('mediaSession' in navigator) {
  navigator.mediaSession.metadata = new MediaMetadata({
    title: 'HIIT Workout',
    artist: 'Hyperhuman',
    album: 'Workout Audio',
  });
}

audioElement.play();
```

---

### 🌍 Localization Support

Audio includes localized voice narration in the requested language:
- **Voice instructions** - Exercise cues in native language
- **Counting** - Rep counts and timers in local language
- **Motivational cues** - Encouragement in native language

**Supported locales:**
en-US, en-GB, en-AU, fr-FR, de-DE, es-ES, it-IT, pt-PT, he-IL, ro-RO, cs-CZ, fi-FI, nl-NL

**Locale fallback behavior:**
1. If requested locale exists → return that version
2. If requested locale not available but user's preferred locale exists → return preferred version
3. Otherwise → return English (en-US) version
4. If no audio export available → return 404

---

### ⚡ Audio Generation Status

Audio files are **pre-generated asynchronously** after workout creation.

**Possible scenarios:**
- ✅ **Audio ready** - Returns URL immediately (typical case)
- ⏳ **Audio processing** - Returns 404 if generation in progress (usually 1-3 minutes)
- ❌ **Generation failed** - Returns 404 if export job failed

---

### 💡 Best Practices

✅ **Cache the URL** - Save in memory/storage for up to 6 days  
✅ **Refresh before expiration** - Fetch new URL after 6 days  
✅ **Enable background playback** - Allow app to continue playing when screen is off  
✅ **Handle network errors** - Implement retry logic with exponential backoff  
✅ **Show progress** - Display current time and duration  
✅ **Support playback controls** - Play, pause, seek, speed adjustment  
✅ **Lock screen controls** - Integrate with OS media controls  
✅ **Download option** - Allow offline download for gym use with no connectivity

---

### 🔄 Content Comparison

**Audio Export (This Endpoint):**
- Complete audio narrative in one file
- Voice instructions + timing cues + background audio
- 5-20 MB file size
- Simple streaming/download

**Playlist Audio:**
- Individual audio clips per segment
- Requires assembly and synchronization
- More control but more complexity

---

### 🔗 Related Endpoints

- **Workout Details:** `GET /v1/workouts/{id}` - Get workout metadata
- **Workout Playlist:** `GET /v1/workouts/{id}/playlist` - Get segment-by-segment structure
- **Video Export:** `GET /v1/workouts/{id}/export/video/stream_url` - Get full video version
- **Start Session:** `POST /v1/workouts/{id}/sessions/start` - Track workout session

**Parameters:**
- `workoutId` (path) (required) — The unique identifier of the workout.
- `locale` (query) — Preferred language locale for audio narration (e.g., de-DE, fr-FR, en-US). If specified and no export exists for this locale, returns 404. Falls back to user's preferred locale or en-US.

**Responses:**
- 200 — text/plain body: a single presigned HTTPS URL to the full narrative audio export (valid 7 days; commonly M4A/AAC).
- 401 — Authentication failed - API key is missing, invalid, or Bearer token is missing/invalid.
- 404 — Workout not found, audio export not available for the requested locale, or audio is still being generated (processing typically takes 1-3 minutes after workout creation).

### GET /v1/workouts/exercises/metadata

- operationId: `WorkoutsApi_getExercisesMetadata`
- summary: Get available muscle groups and exercise collections

Retrieves all muscle groups and exercise collections with counts.

**Use this endpoint to populate:**
- `muscleGroupIds` for targeted muscle group focus in workout generation
- `excludeExerciseCollections` to avoid specific trainer styles or backgrounds

**Response includes:**
- `muscleGroups`: Array of {id, name} for each muscle group (Arms, Chest, Legs, Core, etc.)
- `collections`: Array of {name, count} for exercise collections (Free/Premium categories)

**Example Muscle Groups:**
- Arms (604b0129beaba770c284ff40)
- Chest (604b0169076fe2727354d740)
- Back (604b016c076fe2727354d740)
- Core (604b0170076fe2727354d741)
- Legs (604b0177076fe2727354d743)
- Shoulders (604b0165076fe2727354d73e)
- Glutes (604b0173076fe2727354d742)

**Example Collections:**
- "Premium - Neutral" (808 exercises) - Consistent neutral background
- "Premium - Industrial" (200 exercises) - Industrial gym setting
- "Premium - White" (151 exercises) - Clean white background
- "Free - Eclectic Home" (239 exercises) - Varied home settings
- "Premium - Advanced Gym" (120 exercises) - Professional gym equipment

---

### 🌍 Localization

Content localized based on `locale` query parameter:
- **Supported**: 13 languages (en-US, fr-FR, de-DE, es-ES, etc.)
- **Fallback**: English if locale unsupported
- **Affects**: Muscle group names (collections not localized)

---

**Related Metadata Endpoints:**
- `GET /v1/workouts/exercises/metadata` - Muscle groups & collections
- `GET /v1/workouts/metadata` - Categories & difficulties
- `GET /v1/workouts/equipment/metadata` - Equipment items & categories

**Related Endpoints:**
- Generate: `POST /v1/orgs/{orgId}/workouts/generate`
- List: `GET /v1/orgs/{orgId}/workouts`

**Parameters:**
- `locale` (query) — Preferred language locale (e.g., fr-FR)

**Responses:**
- 200 — Successfully retrieved list of muscle groups and exercise collections.
- 401 — Authentication failed - API key is missing or invalid.

### GET /v1/orgs/{organizationId}/workouts

- operationId: `OrganizationWorkoutsApi_getOrganizationWorkouts`
- summary: List organization-specific workouts

## Organization Workout Library

Retrieves paginated list of workouts specific to your organization. Includes filtering, sorting, and search capabilities.

---

### 🎯 Organization Context

| Aspect | Organization Workouts | Public Workouts |
|--------|----------------------|-----------------|
| **Content** | Your branded workouts | Global library |
| **Customization** | Team-specific exercises | Generic fitness content |
| **Branding** | Your trainers/style | Mixed sources |
| **Access Control** | Organization members | Public access |

---

### 📋 Use Cases

✅ **White-label Apps** - Show only your organization's content  
✅ **Corporate Wellness** - Company-specific fitness programs  
✅ **Trainer Platforms** - Personal trainer's workout library  
✅ **Gym Management** - Facility-specific workouts

---

### 🔍 Filtering & Pagination

**Visibility Filtering:**
- `private` - Organization-only content (requires ownership)
- `public` - Publicly accessible workouts
- `all` - Both private and public (default)

**Additional Filters:**
- `difficulty` - Filter by beginner, intermediate, or advanced
- `categoryIds` - Filter by category IDs (comma-separated)
- `duration` - Filter by duration range in format "min-max" (e.g., "15-45")
- `q` - Search by workout name or description

**Pagination:**
- `offset` - Number of items to skip (default: 0)
- `limit` - Items per page (default: 20, max: 50)
- Prefer following `links.next` from the response over computing offsets manually.

---

### 💡 Best Practices

✅ **Cache responses** - Workouts change infrequently  
✅ **Filter by visibility** - Use `public` for end-user apps, `all` for management  
✅ **Implement search** - Use `q` parameter for name/description filtering  
✅ **Handle empty results** - Graceful fallback to public workouts

---

### 🔗 Related Endpoints

- Generate Workout: `POST /v1/orgs/{orgId}/workouts/generate` - Create new workout
- Recommend Workout: `POST /v1/orgs/{orgId}/workouts/recommend` - Get suggestions
- Workout Details: `GET /v1/workouts/{id}` - Get individual workout
- Workout Metadata: `GET /v1/workouts/metadata` - Get categories and difficulties

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `limit` (query) — Maximum number of items to return per page.
- `q` (query) — Search text
- `sort` (query) — Fields to sort. Prefix them with - (descending) or + (ascending): +someField,-otherField
- `locale` (query) — BCP-47 locale (e.g. en-US, fr-FR). Localizes text and locale-aware media when this operation supports it. Falls back to English when unsupported.
- `visibility` (query) — Filter workouts by their visibility status.
- `difficulty` (query) — Filter workouts by difficulty level.
- `categoryIds` (query) — Filter workouts by one or more workout category IDs (comma-separated 24-character hex DB ids). Optional tokens prefixed with `custom:` are filtered in memory (e.g. duration-based buckets).
- `duration` (query) — Filter workouts by duration range in minutes. Use format "min-max" (e.g., "10-30").
- `allowAssessment` (query) — Filter workouts by assessment support

**Responses:**
- 200 — Successfully retrieved paginated list of workouts for the organization.
- 400 — Invalid pagination or filter parameters.
- 401 — Authentication failed - API key is missing or invalid.
- 404 — The specified organization ID does not exist.

### GET /v1/workouts/{workoutId}/playlist

- operationId: `WorkoutContentApi_getWorkoutPlaylist`
- summary: Retrieve Detailed Workout Playlist Structure

## Workout Playlist with Media Assets

Essential endpoint for building workout video players. Returns the complete, ordered sequence of workout segments with all media assets.

---

### 📋 Typical Integration Flow

1. **Get Workout Metadata**: `GET /v1/workouts/{id}` → Basic info (name, duration, difficulty)
2. **Get Playlist**: `GET /v1/workouts/{id}/playlist` → **This endpoint** (complete playback structure)
3. **Build Player**: Use segments array to create video player experience
4. **Track Session**: `POST /v1/workouts/{id}/sessions/start`
5. **Submit Feedback**: `POST /v1/workouts/{id}/feedback`

---

### 🎬 Segment Types

| Type | Description | Contains |
|------|-------------|----------|
| `intro` | Workout introduction | Video/audio intro, workout overview |
| `exercise` | Exercise performance | Name, reps/duration, **rpe**, **amrap**, video (intro+main), audio cues, equipment, muscle groups |
| `break` | Rest period | Duration, optional video/audio |
| `outro` | Workout conclusion | Cooldown, summary |

---

### 📦 What's Included

- **Video URLs**: Pre-signed S3 URLs for HLS master playlists
- **Audio URLs**: Instruction audio, background music, voice cues
- **Timing**: Precise duration for each segment in seconds
- **Exercise Metadata**: Muscle groups, equipment, difficulty
- **Localization**: All content in requested locale (13 languages)

---

### 🌍 Localization

Supports 13 locales: en-US, en-GB, en-AU, fr-FR, de-DE, es-ES, it-IT, pt-PT, he-IL, ro-RO, cs-CZ, fi-FI, nl-NL

Falls back to English if locale unsupported.

---

### 🔗 Related Endpoints

- Get Workout: `GET /v1/workouts/{id}`
- Workout Metadata: `GET /v1/workouts/metadata`
- Start Session: `POST /v1/workouts/{id}/sessions/start`
- Full audio export: `GET /v1/workouts/{id}/export/audio/stream_url`

**Parameters:**
- `workoutId` (path) (required) — The unique workout id (24-character hex DB id) whose playlist is required.
- `locale` (query) — Language locale for localized content (BCP-47 format). Affects exercise names, equipment names, muscle group names, and audio selection. Supported: en-US, en-GB, en-AU, fr-FR, de-DE, es-ES, it-IT, pt-PT, he-IL, ro-RO, cs-CZ, fi-FI, nl-NL. Falls back to English if unsupported.

**Responses:**
- 200 — Successfully retrieved the detailed workout playlist structure. `data` is an ordered array; each item is an object with a single key indicating the segment kind (one of `intro`, `narrative`, `exercise`, `multiExercise`, `class`, `promo`, `educational`, `break`, `outro`). The value under that key is the segment payload (see schema for the per-kind shape).
- 401 — Authentication failed. API key or Bearer token is missing, invalid, or expired.
- 404 — The specified workout ID does not exist or a playlist could not be generated for it.

### GET /v1/workouts/{workoutId}/music

- operationId: `WorkoutContentApi_getWorkoutMusic`
- summary: Get workout music playlist

## Workout Music Playlist

Returns a shuffled playlist of music tracks selected to fill the workout duration. 
Uses the trainer's music preferences and workout categories to select appropriate tracks.

### Music Selection
- **User Preferences**: Respects trainer's music settings and category mappings
- **Category Filtering**: Selects tracks matching workout categories (HIIT, Strength, etc.)
- **Duration Filling**: Shuffled, deduplicated track list whose total duration generously exceeds the workout duration so the player always has headroom for runtime track selection (do not rely on a specific buffer ratio)
- **Fallback**: Uses stock music if no user preferences set

### 📱 Mobile Ready
- Pre-signed S3 URLs (7-day expiration)
- Simple JSON response
- Track metadata (name, duration, filename)
- Total playlist duration for player setup

### 🔗 Related Endpoints
- Workout Details: `GET /v1/workouts/{id}`
- Workout Playlist: `GET /v1/workouts/{id}/playlist`

**Parameters:**
- `workoutId` (path) (required) — The unique identifier of the workout

**Responses:**
- 200 — Successfully retrieved workout music playlist
- 401 — Authentication failed. API key or Bearer token is missing, invalid, or expired.
- 404 — The specified workout ID does not exist.

### POST /v1/workouts/{workoutId}/sessions/start

- operationId: `WorkoutsSessionsApi_startWorkoutSession`
- summary: Start workout session

## Begin Workout Tracking

Initiates a new workout session for progress tracking, analytics, and completion monitoring. Handles both authenticated and anonymous users.

---

### 🔄 Session Lifecycle

1. **Start Session**: `POST /v1/workouts/{id}/sessions/start` → **This endpoint**
2. **Track Progress**: `PATCH /v1/workouts/{id}/sessions/{sessionId}` → Update completion
3. **End Session**: `POST /v1/workouts/{id}/sessions/end` → Mark complete
4. **Submit Feedback**: `POST /v1/workouts/{id}/feedback` → Rate experience

---

### 👤 Authentication Scenarios

**Authenticated Users:**
- Closes any previous incomplete sessions automatically
- Links session to user profile for history tracking
- Enables progress sync across devices

**Anonymous Users:**
- Creates temporary session without user linkage
- Limited to single-device tracking
- No historical data retention

---

### 📊 What Gets Tracked

| Data Point | Description | Use Case |
|------------|-------------|----------|
| **Start Time** | Session initiation timestamp | Duration calculation |
| **User ID** | Authenticated user (if logged in) | Progress history |
| **Workout ID** | Which workout being performed | Analytics |
| **Session ID** | Unique session identifier | Progress updates |

---

### 💡 Best Practices

✅ **Start immediately** - Call when user begins workout  
✅ **Handle failures** - Graceful degradation if session fails  
✅ **Update progress** - Use PATCH endpoint during workout  
✅ **Complete session** - Always call end endpoint

---

### 🔗 Related Endpoints

- Get Workout: `GET /v1/workouts/{id}` - Workout details before starting
- Update Session: `PATCH /v1/workouts/{id}/sessions/{sessionId}` - Track progress
- End Session: `POST /v1/workouts/{id}/sessions/end` - Mark complete
- Submit Feedback: `POST /v1/workouts/{id}/feedback` - Rate workout

**Parameters:**
- `workoutId` (path) (required) — The unique identifier (UUID) of the workout to start a session for.

**Request body:** application/json

**Responses:**
- 201 — Workout session successfully started. Any previous incomplete sessions for this workout are automatically closed.
- 401 — Authentication failed (API key invalid or Bearer token invalid).
- 404 — The specified workout ID does not exist.

### PATCH /v1/workouts/{workoutId}/sessions/{sessionId}

- operationId: `WorkoutsSessionsApi_patchWorkoutSession`
- summary: Update a workout session

Updates an active workout session to track playback progress.

**Primary Use:**
- Send `progressSeconds` with actual playback time during workout
- Update `workoutSegmentId` to track current segment

**Duration Tracking:**
- `progressSeconds` should represent total actual playback time (excluding pauses)
- Duration is capped at 2x the expected workout duration to prevent outliers
- On end session, the last known duration from PATCH is preserved

**Best Practices:**
- PATCH every 10-30 seconds during active playback
- PATCH on segment transitions
- Send final PATCH before calling end session

**Related Endpoints:**
- Start Session: `POST /v1/workouts/{id}/sessions/start`
- End Session: `POST /v1/workouts/{id}/sessions/end`
- Complete Session: `POST /v1/workouts/{id}/sessions/complete`

**Parameters:**
- `workoutId` (path) (required) — The unique identifier (UUID) of the workout associated with the session.
- `sessionId` (path) (required) — The unique identifier (UUID) of the workout session to update.

**Request body (required):** application/json

**Responses:**
- 200 — Workout session updated successfully.
- 400 — Invalid data provided in the request body.
- 401 — Authentication failed (API key invalid or Bearer token invalid).
- 404 — The specified workout ID or session ID does not exist.
- 409 — Session is already completed and cannot be updated.

### GET /v1/workouts/{workoutId}/sessions/{sessionId}

- operationId: `WorkoutsSessionsApi_getWorkoutSession`
- summary: Get specific workout session details

## Get Specific Session Details

Retrieves details of a specific workout session by its unique ID. Includes status, duration, progress, and timestamps.

---

### 🎯 Use Cases

**Session Inspection:**
- View detailed session information
- Check current status and progress
- Verify session duration

**Progress Tracking:**
- Display session statistics
- Track completion percentage
- Show duration in seconds

---

### 📊 Response Fields

| Field | Type | Description |
|-------|------|-------------|
| id | string | Session unique identifier |
| createdAt | date | When session was started |
| updatedAt | date | Last update timestamp |
| workoutId | string | Associated workout ID |
| workoutSegmentId | string | Current segment ID |
| status | string | 'started' or 'completed' |
| duration | number | Playback time in seconds |
| progress | number | Completion fraction (0-1) |

---

### 💡 Best Practices

✅ **Handle 404 gracefully** - Session may not exist or be accessible  
✅ **Check status field** - Verify session is 'started' before updates  
✅ **Use duration** - Duration is in seconds (actual playback time)

---

### 🔗 Related Endpoints

- Recent Session: `GET /v1/workouts/{id}/sessions/recent` - Most recent active session
- Update Session: `PATCH /v1/workouts/{id}/sessions/{sessionId}` - Track progress
- End Session: `POST /v1/workouts/{id}/sessions/end` - Complete session
- Start Session: `POST /v1/workouts/{id}/sessions/start` - Begin new session

**Parameters:**
- `workoutId` (path) (required) — The unique identifier (UUID) of the workout associated with the session.
- `sessionId` (path) (required) — The unique identifier (UUID) of the workout session to retrieve.

**Responses:**
- 200 — Workout session details retrieved successfully.
- 401 — Authentication failed (API key invalid or Bearer token invalid).
- 403 — The authenticated user is not authorized to view this session.
- 404 — The specified workout ID or session ID does not exist.

### POST /v1/workouts/{workoutId}/sessions/end

- operationId: `WorkoutsSessionsApi_endWorkoutSession`
- summary: End a workout session

Marks an active workout session as completed. The duration is preserved from the last PATCH call.

**Duration Behavior:**
- Uses the duration from the most recent PATCH (progressSeconds-based)
- Does NOT recalculate duration from wall-clock time
- If no PATCH was called, duration will be 0

**Typical Flow:**
1. Start session: `POST /v1/workouts/{id}/sessions/start`
2. Update progress: `PATCH /v1/workouts/{id}/sessions/{sessionId}` with progressSeconds
3. **End session**: `POST /v1/workouts/{id}/sessions/end` (this endpoint)
4. Submit feedback: `POST /v1/workouts/{id}/feedback`

**Best Practice:** Send a final PATCH with current progressSeconds before calling end.

**Related Endpoints:**
- Start Session: `POST /v1/workouts/{id}/sessions/start`
- Update Session: `PATCH /v1/workouts/{id}/sessions/{sessionId}`
- Complete Session: `POST /v1/workouts/{id}/sessions/complete` (force 100%)
- Submit Feedback: `POST /v1/workouts/{id}/feedback`

**Parameters:**
- `workoutId` (path) (required) — The unique identifier (UUID) of the workout associated with the session.

**Request body (required):** application/json

**Responses:**
- 200 — Workout session successfully marked as completed.
- 400 — Invalid request data (e.g., missing sessionId).
- 401 — Authentication failed (API key invalid or Bearer token invalid).
- 404 — The specified workout ID or session ID does not exist.
- 409 — Session is already completed.

### POST /v1/workouts/{workoutId}/sessions/complete

- operationId: `WorkoutsSessionsApi_completeWorkoutSession`
- summary: Complete workout session with 100% progress

## Force Complete Session

Marks a session as fully completed (100% progress) regardless of actual elapsed time. Use this when you need to guarantee a workout shows as complete in user history.

---

### 🎯 When to Use

**Use `/sessions/complete` when:**
- ✅ User manually marks workout as done
- ✅ Workout completed outside the app (e.g., offline)
- ✅ Need to force 100% completion for user's history
- ✅ Retroactive completion (user forgot to track)

**Use `/sessions/end` instead when:**
- ✅ Normal workout completion flow
- ✅ Want to preserve actual progress percentage
- ✅ User stopped mid-workout
- ✅ Tracking actual time and progress

---

### 📊 Endpoint Comparison

| Aspect | complete | end |
|--------|----------|-----|
| **Progress** | Always 100% | Actual progress |
| **Duration** | Full workout duration | Actual elapsed time |
| **Use Case** | Manual/forced completion | Natural completion |
| **User Experience** | "Mark as done" button | Workout timer ends |

---

### 🔄 Integration Flow

**Normal Flow (Use /end):**
```
1. POST /sessions/start → Start tracking
2. PATCH /sessions/{id} → Update progress
3. POST /sessions/end → Natural completion
4. POST /feedback → Collect feedback
```

**Force Complete Flow (Use /complete):**
```
1. POST /sessions/start → May have happened earlier
2. POST /sessions/complete → Force to 100%
3. (Optional) POST /feedback → Collect feedback
```

---

### 💡 Best Practices

✅ **Use sparingly** - Prefer /end for actual completions  
✅ **Document reason** - Log why forced completion was used  
✅ **User transparency** - Show "marked complete" vs "completed"  
✅ **Analytics distinction** - Track forced vs natural completions separately

---

### 🔗 Related Endpoints

- Start Session: `POST /v1/workouts/{id}/sessions/start` - Begin workout
- End Session (natural): `POST /v1/workouts/{id}/sessions/end` - Normal completion
- Update Session: `PATCH /v1/workouts/{id}/sessions/{sessionId}` - Track progress
- Get Session: `GET /v1/workouts/{id}/sessions/{sessionId}` - Check status

**Parameters:**
- `workoutId` (path) (required) — The unique identifier (UUID) of the workout associated with the session.

**Request body (required):** application/json

**Responses:**
- 200 — Workout session successfully marked as 100% completed.
- 400 — Invalid request data (e.g., missing sessionId) or session is already completed.
- 401 — Authentication failed (API key invalid or Bearer token invalid).
- 403 — The authenticated user is not authorized to complete this session.
- 404 — The specified workout ID or session ID does not exist.

### GET /v1/workouts/{workoutId}/sessions/recent

- operationId: `WorkoutsSessionsApi_getRecentWorkoutSession`
- summary: Get most recent workout session

## Get Most Recent Session

Retrieves the most recent incomplete (status='started') workout session for a specific workout. Use this to check if there's an active session to resume.

---

### 🎯 Use Cases

**Resume Workout:**
- Check if user has an active session to resume
- Display current progress and duration
- Continue where they left off

**Progress Tracking:**
- Show current completion status
- Display workout progress
- Check session state

---

### 📊 Session States

**Started Sessions:**
- Session is in progress
- Can be updated via PATCH to track progress
- Will be auto-completed if user starts a new session for the same workout

**Completed Sessions:**
- Session has ended (via /end, /complete, or auto-complete)
- Duration and progress are finalized
- Historical record for analytics

---

### 💡 Best Practices

✅ **Check before starting** - Use this to offer "Resume" vs "Start New"  
✅ **Handle 404 gracefully** - No active session is normal for new workouts  
✅ **Use for resume flow** - Perfect for "Continue Workout" buttons  
✅ **Check status field** - Verify session is 'started' before resuming

---

### 🔗 Related Endpoints

- Start Session: `POST /v1/workouts/{id}/sessions/start` - Begin new session
- Get Session by ID: `GET /v1/workouts/{id}/sessions/{sessionId}` - Specific session
- Update Session: `PATCH /v1/workouts/{id}/sessions/{sessionId}` - Track progress
- End Session: `POST /v1/workouts/{id}/sessions/end` - Complete session

**Parameters:**
- `workoutId` (path) (required) — The unique identifier (UUID) of the workout for which to find the recent session.

**Responses:**
- 200 — Most recent workout session details retrieved successfully.
- 401 — Authentication failed (API key invalid or Bearer token invalid).
- 404 — No workout session (active or completed) found for this workout ID and authenticated user.

### GET /v1/workouts/feedback/options

- operationId: `WorkoutsFeedbackApi_getWorkoutFeedbackOptions`
- summary: Get workout feedback options

## Available Feedback Options

Retrieves the rating scales and options available for workout feedback. Use this to populate your feedback UI dynamically.

---

### 📋 Response Structure

**Rating Options:**
- 1 Star - Poor
- 2 Stars - Fair  
- 3 Stars - Good
- 4 Stars - Very Good
- 5 Stars - Excellent

**Difficulty Options:**
- 1 - Too Easy
- 2 - Easy
- 3 - Just Right
- 4 - Challenging
- 5 - Too Hard

---

### 🎯 Use Cases

✅ **Build Feedback UI** - Get all available options dynamically  
✅ **Validate Submissions** - Ensure feedback IDs are valid before submitting  
✅ **Localization** - Get translated option labels based on locale  
✅ **Consistency** - Use platform-standard feedback scales

---

### 💡 Best Practices

✅ **Cache options** - They rarely change, cache for 24 hours  
✅ **Use option IDs** - Submit feedback using IDs not labels  
✅ **Handle missing options** - Graceful degradation if endpoint fails  
✅ **Refresh periodically** - Check for new options monthly

---

### 🔗 Related Endpoints

- Submit Feedback: `POST /v1/workouts/{id}/feedback` - Submit user feedback
- End Session: `POST /v1/workouts/{id}/sessions/end` - Complete before feedback
- Start Session: `POST /v1/workouts/{id}/sessions/start` - Begin workout tracking

**Responses:**
- 200 — Successfully retrieved feedback options with all available rating scales.
- 401 — Authentication failed - API key is missing or invalid.

### POST /v1/workouts/{workoutId}/feedback

- operationId: `WorkoutsFeedbackApi_submitWorkoutFeedback`
- summary: Submit workout feedback

## Workout Rating & Comments

Collect user feedback after workout completion including ratings, difficulty assessment, exercise performance data (weights and reps), and optional comments for continuous improvement.

---

### 📋 Feedback Components

| Component | Required | Description | Options |
|-----------|----------|-------------|---------|
| **Session ID** | ✅ Yes | Links feedback to completed session | From session start |
| **Overall Rating** | ❌ Optional | General workout satisfaction | 1-5 stars |
| **Difficulty Rating** | ❌ Optional | Perceived workout difficulty | Too easy → Too hard |
| **Weight Unit** | ❌ Optional | Unit system for weights | metric (kg) or imperial (lbs) |
| **Exercise Data** | ❌ Optional | Per-exercise weights and reps | Array of exercise feedback |
| **Comments** | ❌ Optional | Free-text feedback | Max 1000 characters |

---

### 🏋️ Exercise Performance Tracking

**Exercise Data Structure:**
- Each entry corresponds to an exercise from the workout playlist (excluding rest segments)
- Maintain the same order as the workout playlist
- Filter out rest segments and irrelevant exercises client-side before submission

**Weight Tracking:**
- Required for weight-based exercises
- Specify `weightUnit` ('metric' for kg, 'imperial' for lbs) when providing weights
- Weight range: 0-1000 (supports both kg and lbs)
- Example: `{ instanceId: "...", weight: 25.5 }` with `weightUnit: "metric"`

**Reps Tracking:**
- Required for AMRAP/time-based exercises (capture actual reps completed)
- Required for rep-based exercises (capture actual reps completed)
- Reps range: 1-100
- Example: `{ instanceId: "...", reps: 15 }`

**Combined:**
- Some exercises may track both weight and reps
- Example: `{ instanceId: "...", weight: 20.0, reps: 12 }`

---

### 🎯 When to Collect Feedback

✅ **After completion** - User finished entire workout  
✅ **After early exit** - User stopped mid-workout  
✅ **Optional prompts** - Don't force feedback  
✅ **Quick ratings** - Minimize friction

---

### 🔄 Integration Flow

1. **Complete Workout**: User finishes or stops workout
2. **End Session**: `POST /v1/workouts/{id}/sessions/end` → Get session ID
3. **Get Playlist**: `GET /v1/workouts/{id}/playlist` → Get exercise order and instance IDs
4. **Show Feedback UI**: Present rating interface + exercise performance tracking
5. **Submit Feedback**: `POST /v1/workouts/{id}/feedback` → **This endpoint**
6. **Thank User**: Acknowledge feedback submission

---

### 📊 Feedback Options

**Get Available Options:**
`GET /v1/workouts/feedback/options` → Returns current rating scales

**Rating Scales:**
- Overall: 1 (Poor) → 5 (Excellent)
- Difficulty: 1 (Too Easy) → 5 (Too Hard)

---

### 💡 Best Practices

✅ **Make optional** - Don't require feedback to proceed  
✅ **Quick UI** - Star ratings over complex forms  
✅ **Contextual timing** - Collect when experience is fresh  
✅ **Anonymous option** - Allow feedback without login  
✅ **Order matters** - Maintain playlist order when submitting exerciseData  
✅ **Filter client-side** - Remove rest segments before submission  
✅ **Validate instanceIds** - Ensure instanceIds match workout playlist  
✅ **Unit consistency** - Use same weightUnit for all exercises in a submission

---

### 🔗 Related Endpoints

- Feedback Options: `GET /v1/workouts/feedback/options` - Get rating scales
- End Session: `POST /v1/workouts/{id}/sessions/end` - Complete before feedback
- Start Session: `POST /v1/workouts/{id}/sessions/start` - Begin tracking
- Get Workout: `GET /v1/workouts/{id}` - Workout details
- Get Playlist: `GET /v1/workouts/{id}/playlist` - Get exercise order and instance IDs

**Parameters:**
- `workoutId` (path) (required) — The unique identifier (UUID) of the workout receiving feedback.

**Request body (required):** application/json

**Responses:**
- 201 — Feedback submitted successfully.
- 400 — Invalid feedback data provided (e.g., missing required rating, invalid rating value, missing weightUnit when weights provided, invalid instanceIds). Check feedback options endpoint.
- 401 — Authentication failed (API key invalid or Bearer token invalid if provided).
- 404 — The specified workout ID does not exist.

## Tag: plans

### GET /v1/plans/metadata

- operationId: `PlansApi_getPlanMetadata`
- summary: Get available plan goals and difficulties

Retrieves all fitness goals and difficulty levels available across training plans.

**Use this endpoint to populate:**
- `goalIds` parameter in `POST /v1/orgs/{orgId}/plans/generate`
- Plan goal filters in your application UI

**Response includes:**
- `goals`: Array of {id, name} for each fitness goal
- `difficulties`: Array of difficulty strings (beginner, intermediate, advanced)

**Example Goals:**
- Lose weight (63c92926f920a9005c4e619c)
- Build strength (63c92926f920a9005c4e619d)
- Improve endurance (63c92926f920a9005c4e619e)
- Increase mobility (63c92926f920a9005c4e619f)
- Get toned (63c92926f920a9005c4e61a0)
- Physiotherapy (63c92926f920a9005c4e61a1)

**Related Metadata Endpoints:**
- `GET /v1/plans/metadata` - Goals & difficulties (for plan generation/filtering)
- `GET /v1/workouts/metadata` - Categories & difficulties (for workout generation)
- `GET /v1/workouts/equipment/metadata` - Equipment items & categories
- `GET /v1/workouts/exercises/metadata` - Muscle groups & collections

**For Plan Generation:** Call this endpoint + equipment/metadata
**For Workout Generation:** Call workouts/metadata + equipment/metadata + exercises/metadata (advanced)

---

### 🌍 Localization

Content localized based on `locale` query parameter:
- **Supported**: 13 languages (en-US, fr-FR, de-DE, es-ES, etc.)
- **Fallback**: English if locale unsupported
- **Affects**: Goal names

---

**Related Endpoints:**
- Generate: `POST /v1/orgs/{orgId}/plans/generate`
- List: `GET /v1/orgs/{orgId}/plans`
- Recommend: `POST /v1/orgs/{orgId}/plans/recommend`

**Parameters:**
- `locale` (query) — Preferred language locale (e.g., fr-FR)

**Responses:**
- 200 — Successfully retrieved plan goals and difficulty lists.
- 401 — Authentication failed - API key is missing or invalid.

### GET /v1/plans/{planId}

- operationId: `PlansApi_getPlan`
- summary: Get training plan details

## Complete Training Plan Details

Retrieves comprehensive information about a specific multi-week training program including metadata, goals, and trainer information.

---

### 📋 What's Included

| Field | Description | Example |
|-------|-------------|---------|
| **Core Info** | Name, description, difficulty, visibility | "4-Week Strength Builder", intermediate, public |
| **Program Duration** | Duration in weeks and workouts per week | 8 weeks, 3 workouts/week |
| **Goals** | Primary fitness objectives | ["Build Strength", "Increase Endurance"] |
| **Trainer** | Coach name and ID | "Sarah Johnson", trainer123 |
| **Preview** | Video and thumbnail links | HLS stream URL, poster image |
| **Share Link** | Shareable URL for the plan | https://app.hyperhuman.cc/plans/... |
| **Organization** | Team/brand information (if applicable) | Logo URL, organization name |

**Note:** To get the workouts in this plan, call `GET /v1/plans/{id}/workouts`

---

### 🎯 Use Cases

✅ **Plan Preview** - Show details before starting program  
✅ **Goal Alignment** - Match plan goals to user objectives  
✅ **Trainer Information** - Display coach name and credentials  
✅ **Program Overview** - Understand plan structure and goals  
✅ **Duration Planning** - Display program length and weekly commitment  
✅ **Program Comparison** - Compare different program durations and intensities

---

### ⏱️ Program Duration Fields

The response includes two key duration fields:

- **`durationInWeeks`**: Total program duration (1-24 weeks)
- **`workoutsPerWeek`**: Weekly workout frequency (1-7 workouts)

**Example Response:**
```json
{
  "data": {
    "id": "64f1a2b3c4d5e6f7a8b9c0d2",
    "name": "8-Week Strength Program",
    "description": "A comprehensive strength building program",
    "difficulty": "intermediate",
    "visibility": "public",
    "durationInWeeks": 8,
    "workoutsPerWeek": 3,
    "goals": [...],
    "trainer": {...},
    "preview": {...}
  }
}
```

---

### 🔄 Integration Flow

1. **Browse Plans**: `GET /v1/orgs/{orgId}/plans` → Get plan list (with duration filters)
2. **Get Plan Details**: `GET /v1/plans/{id}` → **This endpoint** (full details including duration)
3. **Get Workouts**: `GET /v1/plans/{id}/workouts` → Associated workouts list
4. **Start Program**: Begin first workout in sequence
5. **Track Progress**: Monitor completion across weeks

---

### 🌍 Localization

Content localized based on `locale` query parameter:
- **Supported**: 13 languages (en-US, fr-FR, de-DE, es-ES, etc.)
- **Fallback**: English if locale unsupported
- **Affects**: Plan name, description, goal names

---

### 🔗 Related Endpoints

- Plan List: `GET /v1/orgs/{orgId}/plans` - Browse organization plans (with duration filtering)
- Plan Workouts: `GET /v1/plans/{id}/workouts` - Get associated workouts
- Plan Metadata: `GET /v1/plans/metadata` - Get goals and difficulties
- Generate Plan: `POST /v1/orgs/{orgId}/plans/generate` - Create new plan with duration
- Filter Plans: `GET /v1/orgs/{orgId}/plans?durationInWeeks=8&workoutsPerWeek=3` - Find specific duration programs

**Parameters:**
- `planId` (path) (required) — The unique identifier (UUID) of the workout plan to retrieve.
- `locale` (query) — Preferred language locale for localized content (e.g., en-US, fr-FR, de-DE). Affects plan name, description, and goal names.

**Responses:**
- 200 — Workout plan details retrieved successfully with duration information.
- 401 — Authentication failed - API key is missing or invalid.
- 404 — The specified plan ID does not exist.

### GET /v1/plans/{planId}/workouts

- operationId: `PlansApi_getPlanWorkouts`
- summary: Get workouts in training plan

## Plan Workout Collection

Retrieves the complete list of workouts that comprise a specific training plan, ordered as they appear in the program sequence.

---

### 🎯 Plan Context

| Aspect | Plan Workouts | General Workouts |
|--------|---------------|------------------|
| **Ordering** | Sequential program flow | Alphabetical/date |
| **Progression** | Structured difficulty curve | Independent difficulty |
| **Coherence** | Designed to work together | Standalone sessions |
| **Collection** | Returns all workouts in plan | Filtered/paginated results |

---

### 📋 Use Cases

✅ **Program Navigation** - Browse workouts in sequence  
✅ **Progress Tracking** - Show completed vs remaining workouts  
✅ **Workout Preview** - Display individual workout details  
✅ **Plan Management** - Admin oversight of program content

---

### 🔍 Response Structure

**Returns:**
- Complete list of ALL workouts in the plan (no pagination)
- Workouts ordered as designed in the plan sequence
- Each workout includes: name, duration, difficulty, categories, trainer, preview

**Note:** This endpoint returns all workouts at once. For plans with many workouts, consider implementing client-side pagination for display purposes.

---

### 💡 Best Practices

✅ **Respect workout order** - Maintain the sequence returned by the API  
✅ **Show progress** - Track which workouts user has completed  
✅ **Fetch workout details** - Call `GET /v1/workouts/{id}` for full details  
✅ **Cache workout list** - Workout order rarely changes within a plan  
✅ **Client-side pagination** - Implement display pagination if needed

---

### 🔗 Related Endpoints

- Plan Details: `GET /v1/plans/{id}` - Get plan information
- Workout Details: `GET /v1/workouts/{id}` - Get individual workout
- Start Session: `POST /v1/workouts/{id}/sessions/start` - Begin workout
- Plan List: `GET /v1/orgs/{orgId}/plans` - Browse all plans

**Parameters:**
- `planId` (path) (required) — The unique identifier (UUID) of the workout plan.
- `locale` (query) — Preferred language locale (e.g., fr-FR)

**Responses:**
- 200 — Successfully retrieved complete list of workouts for the plan.
- 401 — Authentication failed - API key is missing or invalid.
- 404 — The specified plan ID does not exist.

### GET /v1/orgs/{organizationId}/plans

- operationId: `OrganizationPlansApi_getOrganizationPlans`
- summary: List organization-specific training plans

## Organization Training Plans

Retrieves paginated list of multi-week training programs specific to your organization.

**Note:** DTOs reference `WorkoutCollection` types - this is the same as "Plan" (historical naming).

---

### 🔄 Choose Your Endpoint

| Need | Use | Speed | Result |
|------|-----|-------|--------|
| Browse library | `GET /orgs/{id}/plans` | <50ms | Filtered list |
| Smart match | `POST /plans/recommend` | <100ms | Top 12 matches |
| Custom program | `POST /plans/generate` | 5-8s | Unique plan |

**Note:** Generated plans are NOT included in this list - use `GET /v1/plans/{id}` with stored ID.

---

### 🎯 Organization Context

| Aspect | Organization Plans | Public Plans |
|--------|-------------------|--------------|
| **Content** | Your branded programs | Global library |
| **Customization** | Team-specific goals | Generic fitness |
| **Branding** | Your trainers/style | Mixed sources |
| **Access Control** | Organization members | Public access |

---

### 📋 Use Cases

✅ **White-label Apps** - Show only your organization's content  
✅ **Corporate Wellness** - Company-specific fitness programs  
✅ **Trainer Platforms** - Personal trainer's program library  
✅ **Gym Management** - Facility-specific training plans

---

### 🔍 Filtering & Pagination

**Visibility Filtering:**
- `private` - Organization-only content (requires ownership)
- `public` - Publicly accessible plans
- `all` - Both private and public (default)

**Additional Filters:**
- `difficulties` - Filter by difficulty levels (comma-separated: beginner,intermediate,advanced)
- `goalIds` - Filter by goal IDs (comma-separated)
- `durationInWeeks` - Filter by program duration (e.g., ?durationInWeeks=8)
- `workoutsPerWeek` - Filter by weekly frequency (e.g., ?workoutsPerWeek=3)
- `q` - Search by plan name or description

**Pagination:**
- `offset` - Number of items to skip (default: 0)
- `limit` - Items per page (default: 20, max: 50)
- `sort` - Sort by: createdAt, updatedAt, name, difficulty, durationInWeeks, workoutsPerWeek
- Prefer following `links.next` from the response over computing offsets manually.

---

### 💡 Best Practices

✅ **Cache responses** - Plans change infrequently  
✅ **Filter by visibility** - Use `public` for end-user apps, `all` for management  
✅ **Implement search** - Use `q` parameter for name/description filtering  
✅ **Handle empty results** - Graceful fallback to public plans

---

### 🔗 Related Endpoints

- Generate Plan: `POST /v1/orgs/{orgId}/plans/generate` - Create new plan
- Plan Details: `GET /v1/plans/{id}` - Get individual plan
- Plan Metadata: `GET /v1/plans/metadata` - Get goals and difficulties
- Recommend Plans: `POST /v1/orgs/{orgId}/plans/recommend` - Smart suggestions

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `limit` (query) — Maximum number of items to return per page.
- `q` (query) — Search text
- `sort` (query) — Fields to sort. Prefix them with - (descending) or + (ascending): +someField,-otherField
- `locale` (query) — BCP-47 locale (e.g. en-US, fr-FR). Localizes text and locale-aware media when this operation supports it. Falls back to English when unsupported.
- `visibility` (query) — Filter plans by their visibility status.
- `difficulty` (query) [deprecated] — Filter plans by difficulty level (single value). DEPRECATED: Use 'difficulties' for multiple values.
- `difficulties` (query) — Filter plans by one or more difficulty levels (comma-separated). Preferred over 'difficulty'.
- `goalIds` (query) — Filter plans by one or more goal IDs (provide comma-separated values if multiple).
- `durationInWeeks` (query) — Filter plans by duration in weeks (1-24)
- `workoutsPerWeek` (query) — Filter plans by workouts per week (1-7)

**Responses:**
- 200 — Successfully retrieved paginated list of plans for the organization.
- 400 — Invalid pagination or filter parameters.
- 401 — Authentication failed - API key is missing or invalid.
- 404 — The specified organization ID does not exist.

## Tag: library-exercises

### GET /v1/orgs/{organizationId}/video-assets

- operationId: `OrganizationExercisesApi_getOrganizationExercises`
- summary: List workspace exercise videos for an organization

## Workspace Exercise Library

Paginated **published** workspace exercise videos for `organizationId` - previews, metadata, and creator references for catalogs, workout builders, and players. Sorted newest first.

---

### What you get

Only the videos this workspace uploaded itself. Use it to power your in-product catalog, workout builder, or video player with content you own.

- `trainer` - the workspace member who uploaded the asset
- `organization` - the workspace you queried, repeated on every row

---

### Video type guide

| Video type | API `kind` | Audio behavior | Playback notes |
|---|---|---|---|
| **EXERCISE** - Smart video for flexible workout building | `single-exercise` | **Original audio removed; multilingual instructions in `audioInstructions[]`** | Configurable reps or duration in workout builder; auto-loops to match your configured time; shows exercise labels and overlays in player |
| **ORIGINAL** - Video plays as uploaded | `multi-exercise` (no separate `original` enum) | Original audio preserved on the video | Plays once from start to finish; added to workouts as-is |
| **PROMO** - Clean playback for branding | `promo` | Original audio preserved on the video | No exercise overlays or labels; great for intros, outros, or splash screens |
| **TUTORIAL** - Educational content | `educational` | Original audio with instructor explanations | Perfect for explaining proper form and technique; great for beginners or complex movements |
| **CLASS** - Long-format instructor-led workout | `class` | Original audio with instructor guidance | Complete workout session format; ideal for yoga, pilates, dance, or instructor-led classes |

---

### Pagination

- `offset` - number of items to skip (default: `0`)
- `limit` - items per page (service default: `10`, max: `50`)
- Prefer following `links.next` from the response over computing offsets manually

---

### Filtering

All filters AND-combine; multi-value filters are OR-internally (`$in`). Every filter is applied at the DB level so `links.total` stays exact.

| Param | Type | Behavior |
|---|---|---|
| `q` | string | Case-insensitive substring on `name`. With `?locale=`, also matches the locale-specific name. |
| `equipmentIds` | CSV of 24-hex ids | Returns assets whose `equipment[]` includes ANY id. |
| `muscleGroupIds` | CSV of 24-hex ids | Returns assets whose `muscleGroups[]` includes ANY id. |
| `kinds` | CSV of `VideoAssetKind` | See **Video type guide** above for the product-name mapping. |
| `skillLevels` | CSV of `SkillLevel` | `beginner` / `intermediate` / `advanced`. |
| `executionSides` | CSV of `ExecutionSide` | `both` / `left` / `right` / `alternating`. |
| `coach` | `male` / `female` | Single value. |
| `collectionNames` | CSV of strings | Exact match (case-sensitive); rows with empty `collectionName` are excluded when set. |

Bad values (non-hex ids, unknown enum members, empty CSV) return `400 ValidationError` with the offending field in `error.target`.

---

### Sorting

Use `?sort=` with `+` / `-` prefix and comma-separated multi-keys.

| Public name | DB field | Notes |
|---|---|---|
| `date` (alias of `createdAt`) | `createdAt` | Default: `-date` (newest first). |
| `name` | `name` | Sorts by canonical `name`. Locale-aware sort is not yet supported. |
| `kind` | `kind` | Useful for grouping; pair with `+name` for stable secondary order. |

Examples: `?sort=-date`, `?sort=+name`, `?sort=-kind,+name`. Unknown fields return `400 ValidationError`.

---

### Localization

Optional `locale` (BCP-47, e.g. `en-US`) localizes:

- `name`
- `muscleGroups[].name`
- `equipment[].name`
- `audioInstructions[]` (see below)

When omitted, raw values fall back to English. Full supported locale list: see `AGENTS.md` section 5.

---

### Audio instructions (`audioInstructions[]`)

Populated **only** for `kind: single-exercise` rows - the smart-clip product type that strips original audio in favor of multilingual instructions.

- **With `?locale=`** - items narrow to matching locales via the standard locale chain (English fallback)
- **Without `?locale=`** - returns **all** available locales so catalog UIs can render a "narrated in N languages" indicator
- **Other kinds** (`multi-exercise`, `educational`, `promo`, `class`) keep their original audio baked into the video and return `[]`

Each entry carries:

- `id` - audio resource id (24-character hex)
- `createdAt` - ISO 8601 timestamp
- `assetUri` - **short-lived presigned URL** to the encoded MP4A clip (do not cache long-term; refresh by re-listing)
- `type` - audio role; for instruction tracks this is `exerciseInstruction`
- `locale` - BCP-47 tag of this specific track (e.g. `en-US`, `fr-FR`)
- `scriptText` *(optional)* - **verbatim transcript** the narrator reads in `assetUri`. Use it for closed captions, accessibility overlays, search indexing, and AI summarization. Omitted on single exercise clips with no intructions at all, no text based instructions or having recorded or audio file instructions - **always null-check** before rendering.

Full per-asset audio (cues + instructions, including `scriptText` per track) is exposed on the per-asset detail endpoint when listed in `/openapi.json`.

---

### Related Endpoints

- Micro Workouts: `GET /v1/orgs/{organizationId}/groups` - circuit / sets-based collections built on top of these exercises
- Workout Metadata: `GET /v1/workouts/metadata` - categories and difficulties shared across the catalog

**Parameters:**
- `organizationId` (path) (required) — Unique organization (workspace) resource id for Hyperhuman Teams - opaque 24-character hex string.
- `limit` (query) — Maximum number of items to return per page.
- `q` (query) — Search text
- `sort` (query) — Sortable fields (use `+` / `-` prefix, comma-separated for multi-key). Allowed: `date` (alias of `createdAt`), `name`, `kind`. Default: `-date` (newest first). Examples: `-date`, `+name`, `-kind,+name`.
- `locale` (query) — BCP-47 locale (e.g. en-US). Localizes text and locale-aware media when supported; falls back to English.
- `equipmentIds` (query) — Comma-separated equipment ids (24-character hex). Returns assets whose `equipment[]` includes ANY of these.
- `muscleGroupIds` (query) — Comma-separated muscle-group ids (24-character hex). Returns assets whose `muscleGroups[]` includes ANY of these.
- `kinds` (query) — Comma-separated video kinds. Allowed: `single-exercise`, `multi-exercise`, `class`, `promo`, `educational`. See the **Video type guide** in the endpoint description for product-name-to-`kind` mapping.
- `skillLevels` (query) — Comma-separated skill levels. Allowed: `beginner`, `intermediate`, `advanced`.
- `executionSides` (query) — Comma-separated execution sides. Allowed: `both`, `left`, `right`, `alternating`.
- `coach` (query) — Coach gender as recorded on the asset.
- `collectionNames` (query) — Comma-separated collection names (exact match, case-sensitive). Rows with empty `collectionName` are excluded when this filter is set.

**Responses:**
- 200 — Paginated workspace exercise videos with previews and metadata.
- 400 — Request validation failed. `error.code` is `ValidationError`; `error.target` is the DTO class name; `error.details[]` lists the offending fields.
- 401 — Missing, malformed, or unknown `X-Api-Key` header.
- 403 — API key is valid but is not authorized for this `organizationId` (the key belongs to a different workspace, or no API key was sent on a guarded route).
- 404 — No workspace exists for the supplied `organizationId`.

## Tag: exercise-groups

### GET /v1/orgs/{organizationId}/groups

- operationId: `OrganizationExerciseGroupsApi_getOrganizationExerciseGroups`
- summary: List organization micro workouts (exercise groups)

## Micro Workouts (Exercise Groups)

**Micro workouts** are short, focused exercise collections designed for quick sessions. Use these endpoints to surface them in your app.

Retrieves paginated list of exercise collections (micro workouts) for your organization. Each collection has simple execution details: circuit or sets format, rounds, rest periods, and per-exercise config (time-based or reps-based).

---

### Use Cases

- **White-label Apps** - Show organization micro workouts
- **Quick Workout Browsing** - Circuit and sets-based collections
- **Integration with Workouts** - Collections can be used in workout generation (excludeExerciseCollections)

---

### Execution Details (per collection)

- **kind**: circuit | sets
- **rounds**: Number of rounds
- **roundBreakDuration**: Rest between rounds (ms)
- **exerciseBreakDuration**: Rest between exercises (ms)
- **exerciseConfig**: timeBasedConfig (duration, pace, rpe, amrap) or repsBasedConfig (reps, weightLevel, pace, rpe)

---

### Filtering and Pagination

**Visibility:**
- `public` - Publicly accessible collections (default)
- `private` - Organization-only (requires API key for same org)
- `all` - Both (requires API key for same org)

**Pagination:**
- `offset` - Pagination offset (default: 0)
- `limit` - Items per page (default: 10, max: 50)

---

### Related Endpoints

- Get Group Detail: `GET /v1/orgs/{orgId}/groups/{groupId}` - Full detail with execution config
- Get Group Exercises: `GET /v1/orgs/{orgId}/groups/{groupId}/exercises` - Exercises in collection
- List Workouts: `GET /v1/orgs/{orgId}/workouts` - Organization workouts

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `limit` (query) — Items per page. Defaults to 10, max 50.
- `locale` (query) — BCP-47 locale (e.g. en-US, fr-FR). Localizes text and locale-aware media when this operation supports it. Falls back to English when unsupported.
- `visibility` (query) — Filter groups by their visibility status.
- `offset` (query) — Pagination offset. Defaults to 0.

**Responses:**
- 200 — Successfully retrieved paginated list of micro workouts (exercise collections).
- 401 — Unauthorized
- 403 — API key not authorized for this organization

### GET /v1/orgs/{organizationId}/groups/{groupId}

- operationId: `OrganizationExerciseGroupsApi_getOrganizationExerciseGroupById`
- summary: Get micro workout (exercise group) by ID

## Micro Workout Detail

Returns full detail for a micro workout (exercise collection) belonging to your organization. Includes execution config (circuit/sets, rounds, rest, time-based or reps-based per exercise).

**Response structure:**
- `kind` - circuit | sets
- `rounds`, `roundBreakDuration`, `exerciseBreakDuration`
- `exerciseConfig` - timeBasedConfig (duration, pace, rpe, amrap) or repsBasedConfig (reps, weightLevel, pace, rpe)
- `preview` - Video preview URLs
- `trainer`, `organization`

**Access:** Only collections created by organization trainers. Private collections require API key for the same organization.

---

### Related Endpoints

- List Collections: `GET /v1/orgs/{orgId}/groups` - Browse organization collections
- Get Exercises: `GET /v1/orgs/{orgId}/groups/{groupId}/exercises` - Exercises in this collection

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `groupId` (path) (required) — The unique identifier of the exercise collection (group).
- `locale` (query) — BCP-47 locale (e.g. en-US, fr-FR). Localizes text and locale-aware media when this operation supports it. Falls back to English when unsupported.

**Responses:**
- 200 — Successfully retrieved micro workout (exercise collection) detail.
- 401 — Unauthorized
- 403 — API key not authorized for this organization
- 404 — Group not found or not accessible

### GET /v1/orgs/{organizationId}/groups/{groupId}/exercises

- operationId: `OrganizationExerciseGroupsApi_getOrganizationGroupExercises`
- summary: Get exercises in micro workout

## Get Micro Workout Exercises

Returns the list of exercises in a micro workout (exercise collection). Each exercise includes video preview, metadata (muscle groups, equipment), and trainer info.

**Use this endpoint to:**
- Display the exercise sequence for a micro workout
- Build a preview or playback UI for the collection
- Get exercise details before starting a session

---

### Related Endpoints

- Get Collection: `GET /v1/orgs/{orgId}/groups/{groupId}` - Collection detail with execution config
- List Collections: `GET /v1/orgs/{orgId}/groups` - Browse organization collections

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `groupId` (path) (required) — The unique identifier of the exercise collection (group) whose exercises are requested.
- `locale` (query) — BCP-47 locale (e.g. en-US, fr-FR). Localizes text and locale-aware media when this operation supports it. Falls back to English when unsupported.

**Responses:**
- 200 — Successfully retrieved list of exercises in the micro workout with instance config (reps, duration, pace, rpe).
- 401 — Unauthorized
- 403 — API key not authorized for this organization
- 404 — Group not found or not accessible

## Tag: ai-generation

### POST /v1/orgs/{organizationId}/workouts/generate

- operationId: `OrganizationWorkoutGeneration_generateWorkout`
- summary: Generate personalized on-the-fly workouts based on user profile details, duration preferences, and exercise sources.

## Generate Personalized Workouts

Create AI-powered workout plans tailored to user profiles, equipment, fitness levels, and duration preferences.

---

### ⚠️ Storage Note

Generated workouts are returned directly (not saved to organization library).  
If you need to reference the workout later, store the returned workout structure in your database.

---

### 🎯 What's Generated

- **Duration**: 10-90 minute workout with automatic warm-up/cooldown
- **Personalized**: Matched to category, difficulty, age, equipment, muscle groups
- **Complete**: Ready-to-use workout structure with exercises, sets, reps, timing
- **Safe**: Every workout includes 3-5 warm-up and cooldown exercises

**User Profile:**
- Age → Appropriate exercise intensity (16-24 Young, 25-34 Young Adult, etc.)
- Fitness level → Uses first level if multiple provided
- Equipment → Guides exercise selection (bodyweight always included)
- Muscle groups → Prioritizes specified groups (optional guidance)

**Duration Logic:**
- Both min/max provided → AI selects optimal in range
- Only one value → Uses as target
- Smart adjustment to meet constraints

---

### 📋 Quick Start

1. **Fetch Metadata**: 
   - `GET /v1/workouts/metadata` → Category IDs (required)
   - `GET /v1/workouts/equipment/metadata` → Equipment category IDs (optional)
   - `GET /v1/workouts/exercises/metadata` → Muscle group IDs (advanced)

2. **Build Request** with metadata IDs

3. **POST to this endpoint**

---

### 📦 Exercise Sources

| Source | Description | Use Case |
|--------|-------------|----------|
| `premium_stock` | High-quality professional content with consistent trainers/backgrounds | **Production (Recommended)** |
| `free_stock` | Varied quality with mixed trainers/backgrounds | Development/testing only |
| `team_exercises` | Your organization's custom content | Maximum brand consistency |

**Default**: If omitted, uses both free and premium stock (not recommended for production)

---

### 💡 Best Practices

✅ Use `premium_stock` for consistent quality  
✅ Use `team_exercises` for brand consistency  
✅ Specify both min and max duration for predictable results  
❌ Avoid mixing `free_stock` with `premium_stock` (visual inconsistency)

**Advanced Options:**

- `muscleGroupIds`: Prioritize specific muscle groups (guidance, not strict filter)
- `excludeExerciseCollections`: Exclude trainer styles (e.g., ["Free - Outdoor"])
- `userRequirements`: High-level context and guidelines for customer-specific training definitions (e.g., "intermediate osteoporosis strength training", "CRITICAL: USE up to 3 unique exercises"). Overrides default AI behavior. Max 2000 characters.

---

### Rate Limit

This endpoint counts as 10x API calls toward your hourly and daily limits.

---

### 🔗 Related Endpoints

- **Metadata**: `GET /v1/workouts/metadata` - Get category IDs
- **Metadata**: `GET /v1/workouts/equipment/metadata` - Get equipment IDs
- **Metadata**: `GET /v1/workouts/exercises/metadata` - Get muscle group IDs
- **List**: `GET /v1/orgs/{orgId}/workouts` - Browse organization workouts
- **Recommend**: `POST /v1/orgs/{orgId}/workouts/recommend` - Smart suggestions

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.

**Request body (required):** application/json

**Responses:**
- 200 — Successfully generated workout plan with automatic warm-up and cooldown sections. AI may adjust final difficulty based on exercise availability and safety considerations.
- 400 — Bad Request - Invalid input parameters.
- 401 — Unauthorized - Invalid API key.
- 403 — Forbidden - API key required or insufficient permissions.
- 404 — Not Found - Organization or other resources not found.
- 500 — Internal Server Error - Failed to generate workout.

### POST /v1/orgs/{organizationId}/plans/generate

- operationId: `OrganizationPlanGeneration_generatePlan`
- summary: Generate personalized training programs based on user profile details, duration preferences, and workout collection sources.

## Generate Multi-Week Training Programs

Create AI-powered, personalized training plans with progressive workout structures.

---

### ⚠️ CRITICAL: Store the Returned Plan ID

Generated plans are **NOT** included in `GET /v1/orgs/{orgId}/plans` responses.

**You MUST store the returned `id` in your database:**
```javascript
const response = await fetch('/v1/orgs/{orgId}/plans/generate', {...});
const planId = response.id;  // Store this in your database
await db.userPlans.create({ userId, planId });

// Later retrieve: GET /v1/plans/{planId}
```

**Retrieval Pattern:**
- Generated plans: `GET /v1/plans/{id}` with stored ID
- Organization library: `GET /v1/orgs/{orgId}/plans`

---

### 🔄 Choose Your Endpoint

| Need | Use | Speed | Result |
|------|-----|-------|--------|
| Browse library | `GET /orgs/{id}/plans` | <50ms | Filtered list |
| Smart match | `POST /plans/recommend` | <100ms | Top 12 matches |
| Custom program | `POST /plans/generate` | 5-8s | Unique plan |

---

### 📋 Quick Start

1. **Fetch Metadata**: `GET /v1/plans/metadata` → Get goal IDs and difficulties
2. **Generate Program**: `POST /v1/orgs/{orgId}/plans/generate` → **This endpoint**
3. **Store Plan ID**: Save the returned `id` to your database/system
4. **Retrieve Plan**: `GET /v1/plans/{id}` → Use stored ID to access full details
5. **Get Workouts**: `GET /v1/plans/{id}/workouts` → Get workouts in program

---

### 🎯 Program Structure

- **Duration**: 4-52 weeks of structured training
- **Frequency**: 1-7 workouts per week
- **Progressive**: Automatic intensity and volume progression
- **Balanced**: Muscle group distribution across training cycle
- **Complete**: Full workout details with exercises, sets, reps

---

### 📦 Workout Collection Sources

| Source | Description | Use Case |
|--------|-------------|----------|
| `template` | Curated platform workout collections | **Recommended for variety** |
| `team` | Your organization's custom workout collections | Brand consistency |

**Default**: Uses both if omitted

---

### 💡 AI Personalization

- **Goal Alignment**: Structures program based on fitness goals
- **Difficulty Matching**: Adjusts complexity to user fitness level
- **Age/Gender Adaptation**: Considers user demographics
- **Duration Optimization**: Selects ideal program length within your range

**Advanced Options:**
- `userRequirements`: High-level context and guidelines for customer-specific training methodologies and program structure (e.g., "Critical: select and use only workouts that are named and contain the word 'minimal'", "Progressive overload with 3-week cycles"). Overrides default AI selection and structure behavior. Max 2000 characters.

---

### 🎯 What's Generated

- **Duration**: 4-52 weeks of structured training
- **Frequency**: 1-7 workouts per week
- **Personalized**: Matched to age, fitness level, goals, equipment
- **Progressive**: Automatic intensity progression across weeks
- **Complete**: Full workout details with exercises

**Duration Logic:**
- Both min/max provided → AI selects optimal in range
- Only one value → Uses as target
- None → Defaults to 8 weeks

**User Profile:**
- Age → Appropriate intensity (16-24 Young, 25-34 Young Adult, 35-44 Early Middle Age, 45-54 Mid Life, 55-64 Late Middle Age, 65-74 Early Senior, 75+ Senior)
- Fitness level → Uses first level if multiple provided
- Goals → Structures program around objectives
- Training days/week → Determines frequency and volume

---

### Rate Limit

This endpoint counts as 10x API calls toward your hourly and daily limits.

---

### 🔗 Related Endpoints

- **Metadata**: `GET /v1/plans/metadata` - Get goal IDs and difficulties
- **Metadata**: `GET /v1/workouts/equipment/metadata` - Get equipment category IDs
- **Retrieve**: `GET /v1/plans/{id}` - Get generated plan details
- **List**: `GET /v1/orgs/{orgId}/plans` - Browse organization library
- **Recommend**: `POST /v1/orgs/{orgId}/plans/recommend` - Smart suggestions (faster)

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.

**Request body (required):** application/json

**Responses:**
- 200 — Successfully generated training program with progressive workout structure.
- 400 — Bad Request - Invalid input parameters.
- 401 — Unauthorized - Invalid API key.
- 403 — Forbidden - API key required or insufficient permissions.
- 404 — Not Found - Organization or other resources not found.
- 500 — Internal Server Error - Failed to generate program.

## Tag: ai-recommendation

### POST /v1/orgs/{organizationId}/workouts/recommend

- operationId: `OrganizationWorkoutRecommendation_getWorkoutRecommendations`
- summary: Get workout recommendations from existing library

## Smart Matching to Existing Content

Returns ranked workout suggestions from your organization's library based on user profile analysis.

---

### 🎯 Recommendations vs. Generation

| Aspect | **Recommendations** (This Endpoint) | **Generation** (`/generate`) |
|--------|-------------------------------------|-------------------------------|
| **Speed** | Instant (<100ms) | 2-5 seconds |
| **Content** | Existing library workouts | New, unique workouts |
| **Customization** | Matches to closest fit | Exact specifications |
| **Use Case** | Browse & discover | On-demand custom |
| **Requires** | Pre-built workout library | Just metadata IDs |

---

### 📋 When to Use Recommendations

✅ **Quick browsing experience** - Show options immediately  
✅ **Large workout library** - You have 20+ workouts  
✅ **Consistency** - Same workout for multiple users  
✅ **Analytics tracking** - See which workouts are popular

---

### 📋 When to Use Generation

✅ **Unique per user** - Every workout is personalized  
✅ **Specific requirements** - Exact duration, muscle groups  
✅ **No library** - You don't have pre-built workouts  
✅ **Dynamic content** - New workouts every session

---

### 🔍 How Matching Works

1. **Analyzes** user profile (age, fitness level, goals, equipment)
2. **Scores** each workout in your library (0.0 - 1.0)
3. **Ranks** by match score
4. **Returns** top 12 with match reasons

**Match Factors:**
- Fitness level alignment (35% weight)
- Equipment compatibility (25% weight)
- Goal alignment (20% weight)
- Age appropriateness (10% weight)
- Duration preference (10% weight)

---

### Rate Limit

This endpoint counts as 10x API calls toward your hourly and daily limits.

---

### 🔗 Related Endpoints

- Generate Workout: `POST /v1/orgs/{orgId}/workouts/generate`
- List Workouts: `GET /v1/orgs/{orgId}/workouts`
- Workout Metadata: `GET /v1/workouts/metadata`

**Parameters:**
- `organizationId` (path) (required) — Organization/Team unique identifier

**Request body (required):** application/json

**Responses:**
- 200 — Ranked list of recommended workouts with match scores and reasons
- 401 — Unauthorized - Invalid API key
- 404 — Organization/Team not found

### POST /v1/orgs/{organizationId}/plans/recommend

- operationId: `OrganizationPlanRecommendation_getPlanRecommendations`
- summary: Get training plan recommendations from existing library

## Smart Matching to Existing Training Programs

Returns ranked multi-week plan suggestions from your organization's library based on user profile analysis.

---

### 🔄 Choose Your Endpoint

| Need | Use | Speed | Result |
|------|-----|-------|--------|
| Browse library | `GET /orgs/{id}/plans` | <50ms | Filtered list |
| Smart match | `POST /plans/recommend` | <100ms | Top 12 matches |
| Custom program | `POST /plans/generate` | 5-8s | Unique plan |

---

### 🎯 Recommendations vs. Generation

| Aspect | **Recommendations** (This Endpoint) | **Generation** (`/generate`) |
|--------|-------------------------------------|-------------------------------|
| **Speed** | Instant (<100ms) | 3-8 seconds |
| **Content** | Existing plan library | New, unique programs |
| **Customization** | Matches to closest fit | Exact specifications |
| **Use Case** | Browse existing programs | Build custom programs |
| **Requires** | Pre-built plan library | Just metadata IDs |

---

### 📋 When to Use Recommendations

✅ **Quick discovery** - Show plan options immediately  
✅ **Curated programs** - You have expertly designed plans  
✅ **Consistency** - Multiple users follow same plan  
✅ **Track analytics** - See which plans are popular

---

### 📋 When to Use Generation

✅ **Unique programs** - Every plan is personalized  
✅ **Specific goals** - Exact duration, frequency, progression  
✅ **No library** - You don't have pre-built plans  
✅ **Dynamic content** - New plans every request

---

### 🔍 How Matching Works

1. **Analyzes** user profile (age, fitness level, goals, training frequency)
2. **Scores** each plan in your library (0.0 - 1.0)
3. **Ranks** by match score
4. **Returns** top 12 with match reasons

**Match Factors:**
- Goal alignment (40% weight)
- Fitness level match (30% weight)
- Training frequency compatibility (20% weight)
- Age appropriateness (10% weight)

---

### Rate Limit

This endpoint counts as 10x API calls toward your hourly and daily limits.

---

### 🔗 Related Endpoints

- Generate Plan: `POST /v1/orgs/{orgId}/plans/generate`
- List Plans: `GET /v1/orgs/{orgId}/plans`
- Plan Metadata: `GET /v1/plans/metadata`

**Parameters:**
- `organizationId` (path) (required) — Organization/Team unique identifier

**Request body (required):** application/json

**Responses:**
- 200 — Ranked list of recommended training plans with match scores and reasons
- 400 — Bad Request - Invalid input data
- 401 — Unauthorized - Invalid API key
- 404 — Organization/Team not found

## Tag: ai-adaptation

### POST /v1/orgs/{organizationId}/workouts/{workoutId}/adapt

- operationId: `OrganizationWorkoutAdaptation_adaptWorkout`
- summary: Adapt an existing team workout based on user profile and data

## Workout Adaptation

Personalizes an existing team workout by adjusting intensity, duration, and exercises based on:
- User profile (age, fitness level, goals, equipment)
- Last 60 days of wearable health data (body/sleep/physical scores)
- Workout feedback and session history
- User-provided guidance and constraints

---

### Key Differences from Generation

| Aspect | Adaptation | Generation |
|--------|------------|------------|
| Source | Existing workout | New from scratch |
| Goals | Preserved from original | User-specified |
| Structure | Modified original | AI-created |
| Use case | Personalize team content | On-demand custom |

---

### What Gets Adapted

- **Intensity**: Reps, duration, rest periods, pace, weight level, RPE
- **Duration**: Total workout time adjusted for user
- **Exercises**: Substitutions only when needed (safety, equipment)
- **NOT Changed**: Workout goals, overall structure intent

---

### Adaptation Factors

Based on user data, the AI considers:
- **Low sleep scores**: Reduces intensity, adds longer rest
- **Declining physical scores**: Lowers reps/duration
- **High difficulty feedback**: Decreases difficulty progressively
- **Low completion rate**: Shortens workout, adds rest
- **User injury mention**: Substitutes risky exercises
- **Equipment constraints**: Replaces with available alternatives

---

### Response Format

Returns same structure as AI workout generation for seamless integration.

---

### Rate Limit

This endpoint counts as 10x API calls toward your hourly and daily limits.

---

### Related Endpoints

- Generate Workout: `POST /v1/orgs/{orgId}/workouts/generate`
- Recommend Workout: `POST /v1/orgs/{orgId}/workouts/recommend`
- List Workouts: `GET /v1/orgs/{orgId}/workouts`

**Parameters:**
- `organizationId` (path) (required) — Organization/Team ID
- `workoutId` (path) (required) — Workout ID to adapt

**Request body (required):** application/json

**Responses:**
- 200 — Adapted workout with personalized adjustments
- 400 — Invalid request or workout not eligible for adaptation (only team workouts can be adapted)
- 401 — Unauthorized - Invalid API key
- 404 — Workout or organization not found

### POST /v1/orgs/{organizationId}/plans/{planId}/adapt

- operationId: `OrganizationPlanAdaptation_adaptPlan`
- summary: Adapt an existing program based on user profile and data

## Program Adaptation

Personalizes an existing training program by modifying the workout schedule based on:
- User profile (age, fitness level, goals, equipment)
- Last 60 days of wearable health data (body/sleep/physical scores)
- Workout feedback and session history
- User-provided guidance and constraints

---

### Key Behaviors

| Scenario | Behavior |
|----------|----------|
| **Team-owned program** | Creates a personal copy, original unchanged |
| **User-generated program** | Updates in place |
| **Completed workouts** | Never modified, only future workouts adapted |

---

### What Gets Adapted

- **Add workouts**: Insert new workouts from library
- **Remove workouts**: Shorten program or accommodate constraints
- **Replace workouts**: Substitute with better alternatives
- **Reshuffle**: Reorder for better progression/recovery

---

### Adaptation Triggers

| Data Source | Impact on Adaptation |
|-------------|---------------------|
| **Low sleep scores** | Remove intense workouts, add recovery |
| **Declining physical scores** | Shorten program, easier alternatives |
| **"Too hard" feedback** | Replace with lower difficulty |
| **Low completion rate** | Remove workouts to reduce overwhelm |
| **Injury in userGuidance** | Remove workouts targeting injured area |
| **Beginner fitness level** | Easier workouts throughout |

---

### Response Details

- `wasDuplicated`: true if program was copied from team program
- `originalProgramId`: ID of original program if duplicated
- `workouts[].action`: 'kept', 'replaced', or 'added'

---

### Rate Limit

This endpoint counts as 10x API calls toward your hourly and daily limits.

---

### Related Endpoints

- Generate Program: `POST /v1/orgs/{orgId}/plans/generate`
- Recommend Program: `POST /v1/orgs/{orgId}/plans/recommend`
- Get Program: `GET /v1/plans/{id}`
- List Programs: `GET /v1/orgs/{orgId}/plans`

**Parameters:**
- `organizationId` (path) (required) — Organization/Team ID
- `planId` (path) (required) — Plan ID to adapt

**Request body (required):** application/json

**Responses:**
- 200 — Adapted program with personalized workout modifications
- 400 — Invalid request or program not found
- 401 — Unauthorized - Invalid API key
- 403 — Forbidden - Cannot adapt programs from other organizations
- 404 — Program or organization not found

## Tag: organization

### GET /v1/orgs/{organizationId}/metadata

- operationId: `OrganizationMetadataApi_getOrganizationMetadata`
- summary: Get organization metadata

## Organization Branding & Configuration

Retrieves your organization's public metadata including branding, configuration, and client joining settings.

---

### What's Included

**Branding:**
- `name`, `handle`, `description`, `website`
- `logoUrl` — single pre-derived 320x320 PNG variant served from CDN. There are no other size variants exposed today; resize client-side if you need smaller.
- `watermarkUrl` — single pre-derived 320x320 PNG variant. Square, transparent background. Used in the embedded player and in rendered video exports.
- `brandMainColor`, `brandSecondaryColor`, `brandTextColor` (hex strings). Any of these may be `null` if the org has not configured them.

**Configuration:**
- `clientsJoinSetting` — `OPEN` | `INVITE_ONLY` | `SUBSCRIPTION_REQUIRED`. For full join-flow context (e.g. paid join, available subscription products), call `GET /v1/orgs/{orgId}/join-options` instead.

**Module Status (booleans, always present):**
- `hasAssenseiModuleActive` — Asensei Assessment add-on (real-time AI form analysis on supported exercises). When `false`, AI assessment endpoints will not produce data for this org's content.
- `hasWearableModuleActive` — ROOK Wearables add-on (Apple Health / Garmin / Fitbit / Oura sync). When `false`, end-user insights pillars that depend on wearables fall back to workout-derived data.
- `hasWhiteLabelAppActive` — Org has a paid white-label consumer app build. Use this to gate "open in app" deeplinks and white-label-only UI.

**Identifiers:**
- `id` — pass to every `/v1/orgs/{organizationId}/...` endpoint.
- `handle` — URL-friendly slug, may be `null`.

---

### Common Use Cases

- **White-label mobile apps** - Fetch branding on app launch and on org switch.
- **Custom web portals** - Apply organization colors/logo at the document root.
- **Multi-tenant platforms** - Resolve organization context once per session.
- **Onboarding flows** - Combine with `/join-options` to render the right CTA.

---

### Best Practices

- **Cache metadata** - Treat as essentially-static. Refresh daily or on app restart; a 5-minute in-memory cache is also fine.
- **Fallback colors** - Always have hard-coded fallbacks; any of the brand color fields may be `null`.
- **Logo size** - Logo and watermark are served at a fixed 320x320 resolution; downscale in the client if you need smaller.
- **Module gates** - Drive UI off the `has*ModuleActive` flags rather than trying to infer from feature responses.

---

### 🔗 Related Endpoints

- Join Options: `GET /v1/orgs/{orgId}/join-options`
- Generate Workout: `POST /v1/orgs/{orgId}/workouts/generate`
- List Workouts: `GET /v1/orgs/{orgId}/workouts`
- List Plans: `GET /v1/orgs/{orgId}/plans`

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.

**Responses:**
- 200 — Successfully retrieved organization metadata with complete branding information.
- 401 — Authentication failed - API key is missing or invalid.
- 404 — The specified organization ID does not exist.

### GET /v1/orgs/{organizationId}/join-options

- operationId: `OrganizationJoinOptionsApi_getOrganizationJoinOptions`
- summary: Get organization join options

## User Onboarding Configuration

Retrieves information about how users can join your organization, including join settings, subscription requirements, and available plans.

---

### 📋 What's Included

**Join Settings:**
- `clientsJoinSetting`: OPEN, INVITE_ONLY, or SUBSCRIPTION_REQUIRED
- `canJoinDirectly`: Boolean flag for self-signup
- `inviteOnlyMode`: Requires invitation to join
- `requiresSubscription`: Payment required to access

**Free Plans:**
- `hasFreePlanWithAutoJoin`: Free access available
- `freeJoinOptions`: List of free plans with auto-enrollment
- `totalProductsAvailable`: Count of subscription products

---

### 🎯 Join Settings Explained

| Setting | Description | User Flow |
|---------|-------------|-----------|
| **OPEN** | Anyone can join | Self-signup enabled |
| **INVITE_ONLY** | Requires invitation | Admin must invite |
| **SUBSCRIPTION_REQUIRED** | Payment required to join | User subscribes for access |

---

### 💡 Integration Patterns

**Self-Signup Flow (OPEN):**
```
1. GET /v1/orgs/{orgId}/join-options → Check if open
2. POST /v1/orgs/{orgId}/endusers/invite → Create user
3. User receives email/access
```

**Invitation Flow (INVITE_ONLY):**
```
1. Admin invites via dashboard or API
2. User receives invitation
3. User accepts and creates account
```

**Subscription Required:**
```
1. GET /v1/orgs/{orgId}/join-options → Check products
2. Display subscription options to user
3. User subscribes → Access granted
```

---

### 🔗 Related Endpoints

- Organization Metadata: `GET /v1/orgs/{orgId}/metadata`
- Invite Users: `POST /v1/orgs/{orgId}/endusers/invite`
- Paywall Products: `GET /v1/orgs/{orgId}/paywall/products`

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.

**Responses:**
- 200 — Successfully retrieved organization join options and configuration.
- 401 — Authentication failed - API key is missing or invalid.
- 404 — The specified organization ID does not exist.

### GET /v1/orgs/{organizationId}/stats

- operationId: `OrganizationStatsApi_getOrganizationStats`
- summary: Get organization API usage statistics

## Organization API Usage Statistics

Retrieves real-time API usage statistics for your organization including current hour requests, daily totals, and rate limits.

---

### 📋 What's Included

**Current Usage:**
- Current hour timestamp and request count
- Total requests made today
- Current date for reference

**Rate Limits:**
- Hourly rate limit from your subscription
- Daily rate limit from your subscription

**Organization Info:**
- Organization ID and name
- Response timestamp

---

### 🎯 Use Cases

✅ **API Monitoring** - Track real-time API usage  
✅ **Rate Limit Management** - Monitor against your limits  
✅ **Usage Analytics** - Understand API consumption patterns  
✅ **Alerting Systems** - Set up usage-based notifications

---

### 💡 Best Practices

✅ **Cache responses** - Stats update every 5 minutes  
✅ **Monitor trends** - Track usage patterns over time  
✅ **Set up alerts** - Monitor approaching rate limits  
✅ **Plan capacity** - Use data for subscription planning

---

### 🔗 Related Endpoints

- Organization Metadata: `GET /v1/orgs/{orgId}/metadata`
- Organization Workouts: `GET /v1/orgs/{orgId}/workouts`
- Organization Plans: `GET /v1/orgs/{orgId}/plans`

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.

**Responses:**
- 200 — Successfully retrieved organization API usage statistics.
- 401 — Unauthorized
- 403 — Forbidden
- 404 — Organization not found

## Tag: social

### GET /v1/orgs/{organizationId}/drops

- operationId: `OrganizationDropsApi_getOrganizationDrops`
- summary: List organization social drops

## Organization Social Drops

Retrieves paginated list of ready social drops for your organization. A drop is a publish-ready package (video variants, covers, copy packs) linked to an Exercise, Collection (Micro Workout), Workout, or Program.

**Note:** Only drops with status `ready` are returned. Draft, processing, and failed drops are excluded.

---

### Access and Auth

- **Auth:** API key (X-Api-Key header)
- **Scope:** Organization (organizationId)
- **Capabilities:** List ready drops, get drop detail, export channel bundle (video, cover, copy pack)

---

### Use Cases

- **Social scheduling tools** - List drops for content calendar
- **Content management dashboards** - Browse and export ready assets
- **Bulk export workflows** - Get video + cover + copy for channel publishing
- **Channel-specific discovery** - Filter by aspect ratio and duration

---

### Filtering and Pagination

**Filters:**
- `sourceType` - exercise, exerciseCollection, workout, workoutCollection
- `sourceId` - Filter by source entity ID (use with sourceType for entity-context views)
- `aspectRatio` - 9:16, 1:1, 16:9 (drops that have this variant)
- `duration` - 15, 30, 60 (seconds; drops that have this variant)
- `q` - Search by drop title (min 3 characters)

**Sort:** Use `sort` with +field (asc) or -field (desc), e.g. `-updatedAt`, `+createdAt`

**Pagination:**
- `limit` - Items per page (default: 20, max: 50)
- `offset` - Pagination offset (default: 0)

---

### Best Practices

- **Cache responses** - Drops change when new content is published
- **Use sourceId** - Filter by source when showing drops in entity-context modals
- **Handle empty results** - Graceful fallback when no ready drops exist

---

### Related Endpoints

- Get Drop: `GET /v1/orgs/{orgId}/drops/{dropId}` - Full detail with variants and copy packs
- Export Drop: `POST /v1/orgs/{orgId}/drops/{dropId}/export` - Channel bundle (video, cover, copy)

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `limit` (query) — Items per page. Defaults to 20, max 50.
- `q` (query) — Search by drop title (min 3 characters)
- `sort` (query) — Sort: +updatedAt, -createdAt
- `sourceType` (query) — Filter by source type
- `sourceId` (query) — Filter by source entity ID
- `aspectRatio` (query) — Filter by aspect ratio
- `duration` (query) — Filter by duration in seconds
- `offset` (query) — Pagination offset. Defaults to 0.

**Responses:**
- 200 — Successfully retrieved paginated list of ready drops for the organization.
- 400 — Invalid filter or pagination parameters.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.
- 404 — The specified organization ID does not exist.

### GET /v1/orgs/{organizationId}/drops/{dropId}

- operationId: `OrganizationDropsApi_getOrganizationDropById`
- summary: Get drop by ID

## Drop Detail

Returns full drop detail including video variants with presigned download and poster URLs, and copy packs per channel.

**Response structure:**
- `copyPack` - Copy pack for the default channel (channelPreset)
- `copyPacks` - Copy packs per channel; use `copyPacks[viewChannel] ?? copyPack` for channel switching
- `variants` - Video variants with aspectRatio, duration, downloadLink, posterLink, posterLinksByChannel, size

**URL expiration:** Presigned URLs are valid for 7 days.

---

### Related Endpoints

- List Drops: `GET /v1/orgs/{orgId}/drops` - Browse organization drops
- Export Drop: `POST /v1/orgs/{orgId}/drops/{dropId}/export` - Get channel bundle

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `dropId` (path) (required) — The unique identifier of the drop.

**Responses:**
- 200 — Successfully retrieved drop detail.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.
- 404 — Drop not found or not ready.

### POST /v1/orgs/{organizationId}/drops/{dropId}/export

- operationId: `OrganizationDropsApi_exportDrop`
- summary: Export drop for channel bundle

## Export for Channel Bundle

Returns presigned video download URL, cover image URL, and copy pack for the specified channel. Use for one-click export to Reels, Shorts, TikTok, etc.

**Response contents:**
- `videoDownloadLink` - Presigned S3 URL for MP4 video (valid 7 days)
- `coverLink` - Presigned S3 URL for cover/poster image
- `copyPack` - Hook, caption, hashtags, CTA, poster tagline
- `ctaUrl` - Optional CTA URL from team settings (when configured)

**Channel presets:** ig_reels, ig_stories, ig_carousel, tiktok, yt_shorts, fb_reels, yt_longform. Each has default aspect ratio and duration; override via body.

---

### Integration Example

```javascript
const res = await fetch(
  `/v1/orgs/${orgId}/drops/${dropId}/export`,
  {
    method: 'POST',
    headers: {
      'X-Api-Key': apiKey,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ channelPreset: 'ig_reels' }),
  }
);
const { videoDownloadLink, coverLink, copyPack } = await res.json();
// Download video, copy caption to clipboard
```

---

### Best Practices

- **Refresh URLs before expiry** - Presigned URLs valid for 7 days
- **Use channel preset** - Platform-specific defaults (aspect ratio, duration)
- **Handle 400** - Requested variant may not be available for this drop

---

### Related Endpoints

- Get Drop: `GET /v1/orgs/{orgId}/drops/{dropId}` - Full detail before export
- List Drops: `GET /v1/orgs/{orgId}/drops` - Browse organization drops

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `dropId` (path) (required) — The unique identifier of the drop.

**Request body:** application/json

**Responses:**
- 200 — Channel bundle with presigned URLs and copy pack.
- 400 — Requested variant not available for this drop.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.
- 404 — Drop not found or not ready.

## Tag: content-autopilot

### GET /v1/orgs/{organizationId}/content-autopilot

- operationId: `OrganizationContentAutopilotApi_listTasks`
- summary: List content autopilot tasks

## Content Autopilot Tasks

Retrieves a paginated list of content autopilot tasks for your organization. Autopilot tasks automate the creation, adaptation, and publishing of workouts and programs on a configurable schedule.

---

### Access and Auth

- **Auth:** API key (X-Api-Key header)
- **Scope:** Organization (organizationId)

---

### Use Cases

- **Dashboard views** - List all scheduled content automation tasks
- **Status monitoring** - Filter by active/draft to see running vs paused tasks
- **Content type filtering** - Show only workout or program automation tasks
- **Audit and review** - Browse task configurations for the organization

---

### Filtering and Pagination

**Filters:**
- `status` - active, draft
- `scope` - ai_generate, create_similar, publish_existing
- `contentType` - workout, program

**Sort:** `sortBy` (updatedAt, createdAt) + `sortOrder` (ascend, descend)

**Pagination:**
- `limit` - Items per page (default: 20, max: 50)
- `offset` - Pagination offset (default: 0)

---

### Related Endpoints

- Get Task: `GET /v1/orgs/{orgId}/content-autopilot/{taskId}`
- Create Task: `POST /v1/orgs/{orgId}/content-autopilot`
- Task Events: `GET /v1/orgs/{orgId}/content-autopilot/{taskId}/events`

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `limit` (query) — Items per page. Defaults to 20, max 50.
- `status` (query) — Filter by task status
- `scope` (query) — Filter by task scope
- `contentType` (query) — Filter by content type
- `sortBy` (query) — Sort by field
- `sortOrder` (query) — Sort order
- `offset` (query) — Pagination offset. Defaults to 0.

**Responses:**
- 200 — Successfully retrieved paginated list of autopilot tasks.
- 400 — Invalid filter or pagination parameters.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.

### POST /v1/orgs/{organizationId}/content-autopilot

- operationId: `OrganizationContentAutopilotApi_createTask`
- summary: Create content autopilot task

## Create Autopilot Task

Creates a new content autopilot task in `draft` status. Use the activate endpoint to start the scheduling.

---

### Access and Auth

- **Auth:** API key (X-Api-Key header)
- **Scope:** Organization (organizationId)

---

### Task Scopes

| Scope | Description | Required Fields |
|-------|-------------|-----------------|
| `ai_generate` | Generate new content using AI | categoryIds/goalIds, difficultyOptions |
| `create_similar` | Adapt an existing workout/program | `sourceContentId` |
| `publish_existing` | Flip visibility to public on schedule | `existingContentId` |

---

### Prompt Guidance Modes

| Mode | Description |
|------|-------------|
| `none` | No additional prompt context |
| `custom` | User-written free text (`customPromptGuidance`) |
| `ai_tuned` | 30-day analytics context appended to prompt |
| `ai_driven` | Fully autonomous - AI decides category, difficulty, duration |

---

### Validation

- `minutesLimit` entitlement must have remaining capacity
- `premiumStock` entitlement required if `includePremiumStock` is true
- `sourceContentId` / `existingContentId` must belong to the organization

---

### Related Endpoints

- Activate: `POST /v1/orgs/{orgId}/content-autopilot/{taskId}/activate`
- Update: `PATCH /v1/orgs/{orgId}/content-autopilot/{taskId}`
- List: `GET /v1/orgs/{orgId}/content-autopilot`

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.

**Request body (required):** application/json

**Responses:**
- 201 — Task created in draft status.
- 400 — Invalid input or missing required fields for scope.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — Entitlement limit exceeded or API key not authorized.

### GET /v1/orgs/{organizationId}/content-autopilot/events

- operationId: `OrganizationContentAutopilotApi_listTeamEvents`
- summary: List execution events across all tasks

## Organization Autopilot Events

Retrieves execution history across all content autopilot tasks for the organization. Each event records a task execution outcome: success, failure, or skip.

---

### Access and Auth

- **Auth:** API key (X-Api-Key header)
- **Scope:** Organization (organizationId)

---

### Use Cases

- **Execution dashboard** - View recent automation activity across all tasks
- **Failure monitoring** - Filter by `failed` to identify issues
- **Audit trail** - Review content creation history with content IDs

---

### Filtering

- `taskId` - Filter events to a specific task
- `eventType` - success, failed, skipped_limit, skipped_premium_lost

---

### Related Endpoints

- List Tasks: `GET /v1/orgs/{orgId}/content-autopilot`
- Task Events: `GET /v1/orgs/{orgId}/content-autopilot/{taskId}/events`

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `limit` (query) — Items per page. Defaults to 20, max 50.
- `eventType` (query) — Filter by event type
- `taskId` (query) — Filter events to a specific task
- `offset` (query) — Pagination offset. Defaults to 0.

**Responses:**
- 200 — Successfully retrieved execution events.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.

### GET /v1/orgs/{organizationId}/content-autopilot/{taskId}

- operationId: `OrganizationContentAutopilotApi_getTask`
- summary: Get autopilot task by ID

## Autopilot Task Detail

Returns full configuration of a content autopilot task including schedule, content parameters, and prompt guidance settings.

---

### Access and Auth

- **Auth:** API key (X-Api-Key header)
- **Scope:** Organization (organizationId)

---

### Related Endpoints

- List Tasks: `GET /v1/orgs/{orgId}/content-autopilot`
- Update Task: `PATCH /v1/orgs/{orgId}/content-autopilot/{taskId}`
- Task Events: `GET /v1/orgs/{orgId}/content-autopilot/{taskId}/events`

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `taskId` (path) (required) — The unique identifier of the autopilot task.

**Responses:**
- 200 — Successfully retrieved autopilot task detail.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.
- 404 — Task not found or deleted.

### PATCH /v1/orgs/{organizationId}/content-autopilot/{taskId}

- operationId: `OrganizationContentAutopilotApi_updateTask`
- summary: Update autopilot task configuration

## Update Autopilot Task

Partially updates an autopilot task's configuration. All fields are optional. Fields not included in the request body remain unchanged.

Notable: update `nextRunAt` to reschedule an active task to a different day without affecting its repeat configuration.

---

### Access and Auth

- **Auth:** API key (X-Api-Key header)
- **Scope:** Organization (organizationId)

---

### Related Endpoints

- Get Task: `GET /v1/orgs/{orgId}/content-autopilot/{taskId}`
- Activate: `POST /v1/orgs/{orgId}/content-autopilot/{taskId}/activate`

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `taskId` (path) (required) — The unique identifier of the autopilot task.

**Request body (required):** application/json

**Responses:**
- 200 — Task updated.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.
- 404 — Task not found or deleted.

### DELETE /v1/orgs/{organizationId}/content-autopilot/{taskId}

- operationId: `OrganizationContentAutopilotApi_deleteTask`
- summary: Delete autopilot task (soft delete)

## Delete Autopilot Task

Soft-deletes a content autopilot task. The task will no longer execute and will not appear in list results.

---

### Access and Auth

- **Auth:** API key (X-Api-Key header)
- **Scope:** Organization (organizationId)

---

### Related Endpoints

- List Tasks: `GET /v1/orgs/{orgId}/content-autopilot`

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `taskId` (path) (required) — The unique identifier of the autopilot task.

**Responses:**
- 204 — Task deleted.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.
- 404 — Task not found or already deleted.

### POST /v1/orgs/{organizationId}/content-autopilot/{taskId}/activate

- operationId: `OrganizationContentAutopilotApi_activateTask`
- summary: Activate autopilot task

## Activate Task

Sets the task status to `active` and determines the next scheduled execution time.

**Schedule resolution:**
1. If the task already has a `nextRunAt` in the future, it is preserved.
2. Otherwise, `nextRunAt` is computed from the task's `repeatMode`.
3. For `once` mode, defaults to now.

---

### Access and Auth

- **Auth:** API key (X-Api-Key header)
- **Scope:** Organization (organizationId)

---

### Related Endpoints

- Deactivate: `POST /v1/orgs/{orgId}/content-autopilot/{taskId}/deactivate`
- Run Now: `POST /v1/orgs/{orgId}/content-autopilot/{taskId}/run-now`

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `taskId` (path) (required) — The unique identifier of the autopilot task.

**Responses:**
- 200 — Task activated.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.
- 404 — Task not found or deleted.

### POST /v1/orgs/{organizationId}/content-autopilot/{taskId}/deactivate

- operationId: `OrganizationContentAutopilotApi_deactivateTask`
- summary: Deactivate autopilot task

## Deactivate Task

Sets the task status to `draft` and clears the `nextRunAt` schedule. The task will stop executing until activated again.

---

### Access and Auth

- **Auth:** API key (X-Api-Key header)
- **Scope:** Organization (organizationId)

---

### Related Endpoints

- Activate: `POST /v1/orgs/{orgId}/content-autopilot/{taskId}/activate`

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `taskId` (path) (required) — The unique identifier of the autopilot task.

**Responses:**
- 200 — Task deactivated.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.
- 404 — Task not found or deleted.

### POST /v1/orgs/{organizationId}/content-autopilot/{taskId}/run-now

- operationId: `OrganizationContentAutopilotApi_runNow`
- summary: Manually trigger task execution

## Run Now

Executes a content autopilot task immediately, bypassing the cron schedule. Respects all entitlement limits and daily execution caps (max 20 per user per day).

The task can be in any status (active or draft) -- this allows testing a task configuration before activating it on a schedule.

---

### Access and Auth

- **Auth:** API key (X-Api-Key header)
- **Scope:** Organization (organizationId)

---

### Rate Limit

This endpoint triggers AI content generation and counts as 10x API calls toward your hourly and daily limits.

---

### Related Endpoints

- Activate: `POST /v1/orgs/{orgId}/content-autopilot/{taskId}/activate`
- Task Events: `GET /v1/orgs/{orgId}/content-autopilot/{taskId}/events`

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `taskId` (path) (required) — The unique identifier of the autopilot task.

**Responses:**
- 200 — Task executed successfully.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — Entitlement limit exceeded or API key not authorized.
- 404 — Task not found or deleted.

### GET /v1/orgs/{organizationId}/content-autopilot/{taskId}/events

- operationId: `OrganizationContentAutopilotApi_listTaskEvents`
- summary: List execution events for a task

## Task Execution History

Retrieves the execution event log for a specific autopilot task. Each event records a task run outcome.

**Event types:**
- `success` - Content created and published successfully. `resultContentId` contains the new content ID.
- `failed` - Execution failed. `errorMessage` contains the reason.
- `skipped_limit` - Daily execution limit (20/day) reached. Task retries next day.
- `skipped_premium_lost` - Premium stock entitlement lost. Schedule advanced.

---

### Access and Auth

- **Auth:** API key (X-Api-Key header)
- **Scope:** Organization (organizationId)

---

### Related Endpoints

- Get Task: `GET /v1/orgs/{orgId}/content-autopilot/{taskId}`
- Team Events: `GET /v1/orgs/{orgId}/content-autopilot/events`

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `taskId` (path) (required) — The unique identifier of the autopilot task.
- `limit` (query) — Items per page. Defaults to 20, max 50.
- `eventType` (query) — Filter by event type
- `offset` (query) — Pagination offset. Defaults to 0.

**Responses:**
- 200 — Successfully retrieved task execution events.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.
- 404 — Task not found or deleted.

## Tag: social-publish

### GET /v1/orgs/{organizationId}/social-publish/connected-platforms

- operationId: `OrganizationSocialPublishApi_getConnectedPlatforms`
- summary: List connected social platforms

Returns which platforms (instagram, tiktok, youtube, facebook) have a stored account id for this organization. Account connection is managed in the Hyperhuman team app, not via this API.

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.

**Responses:**
- 200
- 403

### GET /v1/orgs/{organizationId}/social-publish

- operationId: `OrganizationSocialPublishApi_listTasks`
- summary: List scheduled social publish automations

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `limit` (query) — Maximum number of items to return per page.
- `status` (query) — Filter by task status
- `sourceMode` (query) — Filter by source mode
- `sortBy` (query) — Sort by field
- `sortOrder` (query) — Sort order
- `search` (query) — Case-insensitive substring match on automation name (min 1 character after trim)

**Responses:**
- 200
- 403

### POST /v1/orgs/{organizationId}/social-publish

- operationId: `OrganizationSocialPublishApi_createTask`
- summary: Create a social publish automation (draft)

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.

**Request body (required):** application/json

**Responses:**
- 201
- 403

### GET /v1/orgs/{organizationId}/social-publish/{taskId}

- operationId: `OrganizationSocialPublishApi_getTask`
- summary: Get one social publish automation

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `taskId` (path) (required) — The unique identifier of the scheduled social publish automation.

**Responses:**
- 200
- 404

### PATCH /v1/orgs/{organizationId}/social-publish/{taskId}

- operationId: `OrganizationSocialPublishApi_patchTask`
- summary: Update a social publish automation

Use `status: active` to schedule (requires connected accounts for all target platforms). Use `status: draft` to pause. Other fields follow the same rules as the team internal API.

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `taskId` (path) (required) — The unique identifier of the scheduled social publish automation.

**Request body (required):** application/json

**Responses:**
- 200
- 422

### DELETE /v1/orgs/{organizationId}/social-publish/{taskId}

- operationId: `OrganizationSocialPublishApi_deleteTask`
- summary: Delete a social publish automation (soft delete)

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `taskId` (path) (required) — The unique identifier of the scheduled social publish automation.

**Responses:**
- 204

## Tag: endusers-ai-insights

### GET /v1/orgs/{organizationId}/endusers/{endUserId}/insights/digest

- operationId: `UserInsightsApi_getUserDailyDigest`
- summary: Get daily AI-powered user insights digest

## Daily Health & Fitness Digest

Returns a personalized daily digest of insight pillars selected from: `health_pulse`, `training`, `recovery`, `movement`, `nutrition`, `body`. The exact set returned depends on which data sources the user has connected and on the current data completeness — typically 3-5 pillars per day.

Each pillar in the digest is intentionally **summary-only**. Coaching signals such as readiness band, recommended intensity, risk triggers, form cue, why-explanations, and alternative actions are surfaced inside the `headline` and `action` strings, but **are not exposed as separate fields on `InsightPillarDto`**. To get the full coaching context for a single pillar, drill into `GET /v1/orgs/{organizationId}/endusers/{endUserId}/insights/pillars/{pillarType}`.

---

### Pillars Overview

| Pillar | Description | Primary Data Source |
|--------|-------------|---------------------|
| **Health Pulse** | Overall health status and main driver | Rook wearables / workout adherence |
| **Training** | Workout progress and next session | Workout sessions, AI assessments |
| **Recovery** | Sleep and recovery readiness | Rook sleep score / workout intensity |
| **Movement** | Daily activity and cardio | Rook activity / workout frequency |
| **Nutrition** | Nutrition quality (when available) | Photo logs (coming soon) |
| **Body** | Body metrics and momentum | Rook body score / workout streak |

---

### Status Indicators

- **Green** (75-100): On track
- **Yellow** (55-74): Watch it
- **Red** (0-54): Priority action needed

---

### Data Completeness

The `dataCompleteness` field (0-1) indicates how much user data is available:
- **0.8+**: Full wearable + workout data
- **0.5-0.8**: Partial data (some wearables or workout history)
- **<0.5**: Limited data (fallback insights provided)

---

### Fallback Behavior

When data is missing:
- Health Pulse calculates a "Hyperhuman Pulse" from workout adherence
- Recovery uses workout intensity to estimate readiness
- Nutrition shows a CTA to start tracking

---

### Enhanced Coaching Signals

The digest includes intelligent coaching signals embedded in pillar headlines:

| Signal | Description | Pillars |
|--------|-------------|---------|
| **Readiness** | Push/Maintain/Recover recommendation based on recovery data | Health Pulse, Recovery |
| **Risk Detection** | Fatigue warning when multiple triggers present | Training |
| **Form Focus** | Primary technique cue from AI assessments | Training |
| **Program Pacing** | On track/behind status for weekly goals | Training |

These signals adjust the `action` field to provide intensity-appropriate recommendations based on current readiness state.

---

### Use Cases

- Daily digest screen in mobile app
- Coach dashboard for client overview
- Notification content generation
- Personalized workout recommendations

---

### Rate Limit

This endpoint counts as 10x API calls toward your hourly and daily limits.

---

### Related Endpoints

- Workout Sessions: `GET /v1/me/workouts/sessions`
- Workout Feedback: `POST /v1/workouts/{id}/feedback`
- AI Assessments: `GET /v1/me/workouts/measured-metrics`

**Parameters:**
- `organizationId` (path) (required) — The unique identifier (UUID) of the organization.
- `endUserId` (path) (required) — User identifier: email, externalUUID, or internal user ID.
- `date` (query) — Date for the digest (ISO 8601, YYYY-MM-DD). Past dates within last 7 days and today supported; future or older dates return 400. Defaults to today.
- `forceRefresh` (query) — Force refresh, bypassing cache.

**Responses:**
- 200 — Successfully retrieved daily digest.
- 400 — Invalid date (e.g., future date, more than 7 days in past, or invalid format).
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.
- 404 — User or organization not found.

### GET /v1/orgs/{organizationId}/endusers/{endUserId}/insights/pillars/{pillarType}

- operationId: `UserInsightsApi_getPillarDetail`
- summary: Get detailed view of a specific insight pillar

## Pillar Detail

Returns expanded information for a specific insight pillar, including all available metrics and recommendations.

---

### Available Pillar Types

| Type | Details Included |
|------|------------------|
| `health_pulse` | Score, delta, driver, readiness band, recommended intensity |
| `training` | Last workout, next session, form score, form cue, pacing status, risk level, why explanations, alternative actions |
| `recovery` | Sleep score, main issue, intensity, readiness band, HRV band, why explanations |
| `movement` | Activity level, suggested minutes, cardio trend |
| `nutrition` | Nutrition score (when available), wins/watches |
| `body` | Body score, weekly trend, streak, biggest lever |

---

### Enhanced Coaching Details

When drilling into pillar details, you get actionable coaching context:

**Readiness (Health Pulse and Recovery pillars):**
- `readinessBand`: Training capacity recommendation (`push` | `maintain` | `recover`)
- `readinessMessage`: Explanation of the recommendation
- `recommendedIntensity`: Target workout intensity (`light` | `moderate` | `high`)
- `hrvBand`: HRV relative to personal baseline (`low` | `normal` | `high`)

**Risk Detection (Training pillar):**
- `riskLevel`: Fatigue risk severity (`low` | `moderate` | `high`)
- `riskTriggers`: Array of up to 3 contributing factors
- `alternativeActions`: Safe workout alternatives when risk detected

**Form Coaching (Training pillar):**
- `formCue`: Primary technique focus for today
- `formSuggestion`: Adjustment if form is declining
- `repAccuracy`: Rep accuracy percentage from AI assessment

**Program Pacing (Training pillar):**
- `pacingStatus`: Weekly pacing status (`on_track` | `behind` | `ahead`)
- `pacingMessage`: Weekly progress summary
- `workoutsPlannedThisWeek` / `workoutsCompletedThisWeek`

**Explanations:**
- `why`: Array of up to 3 reasons explaining the recommendation

---

### Use Cases

- Drill-down from digest view
- Detailed pillar cards in UI
- Coach insights for specific areas

---

### Rate Limit

This endpoint counts as 10x API calls toward your hourly and daily limits.

**Parameters:**
- `organizationId` (path) (required) — The unique identifier (UUID) of the organization.
- `endUserId` (path) (required) — User identifier: email, externalUUID, or internal user ID.
- `pillarType` (path) (required) — Type of pillar to get details for.

**Responses:**
- 200 — Successfully retrieved pillar details.
- 400 — Invalid pillar type.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.
- 404 — User, organization, or pillar not found.

## Tag: endusers-ai-chat

### POST /v1/chat/conversations

- operationId: `ChatApi_startConversation`
- summary: Start a new chat conversation

## Start Conversation

Creates a new conversation with the AI fitness coach and returns the assistant's response to the initial message.

---

### User Identification

Provide at least one of `userEmail` or `userExternalUUID` in the request body to identify the end user.

---

### Organization ID

- Use `"default"` to automatically resolve the organization from the API key
- Or provide a valid 24-character hex organization id

---

### Response

Returns the created conversation metadata and the AI assistant's first response message. The assistant message may include `metadata.contentPreview` (workouts, programs, exercises) or `metadata.pendingAction` when the AI proposes a state-changing action.

---

### Pending Actions

When the AI proposes a state-changing action (e.g. log nutrition, add workouts, generate workout), the assistant message will include `metadata.pendingAction` with an `id`, `type`, `summary`, and `payload`. Use the Confirm Action endpoint to execute it.

---

### Rate Limit

This endpoint counts as 10x API calls toward your hourly and daily limits.

---

### Related Endpoints

- `POST /v1/chat/conversations/{conversationId}/messages` - Send follow-up messages
- `GET /v1/chat/conversations` - List user conversations
- `POST /v1/chat/conversations/{conversationId}/actions/confirm` - Confirm a pending action

**Request body (required):** application/json

**Responses:**
- 201
- 400
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.

### GET /v1/chat/conversations

- operationId: `ChatApi_getConversations`
- summary: Get user conversations

## List Conversations

Returns a paginated list of the end user's chat conversations, ordered by most recent activity.

---

### User Identification

Provide at least one of `userEmail` or `userExternalUUID` as query parameters to identify the end user.

---

### Pagination

| Parameter | Default | Max |
|-----------|---------|-----|
| `page` | 1 | - |
| `limit` | 20 | 100 |

---

### Related Endpoints

- `POST /v1/chat/conversations` - Start a new conversation
- `GET /v1/chat/conversations/{conversationId}/messages` - Get messages in a conversation
- `DELETE /v1/chat/conversations/{conversationId}` - Archive a conversation

**Parameters:**
- `organizationId` (query) (required) — Organization ID - use "default" to use the user's default organization
- `page` (query) — Page number
- `limit` (query) — Items per page
- `userEmail` (query) — User email address to identify the end user
- `userExternalUUID` (query) — External unique identifier for the end user

**Responses:**
- 200
- 400
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.

### GET /v1/chat/conversations/{conversationId}/messages

- operationId: `ChatApi_getConversationMessages`
- summary: Get conversation messages

## Get Messages

Returns a paginated list of messages in a conversation, ordered chronologically.

---

### User Identification

Provide at least one of `userEmail` or `userExternalUUID` as query parameters to identify the end user. The user must be the owner of the conversation.

---

### Message Structure

Each message includes:
- `role`: `"user"` or `"assistant"`
- `content`: The message text
- `status`: `"pending"`, `"delivered"`, or `"failed"`
- `metadata`: Optional object containing `reasoning`, `topics`, `contentPreview`, `pendingAction`, or `confirmedResult`

---

### Pagination

| Parameter | Default | Max |
|-----------|---------|-----|
| `page` | 1 | - |
| `limit` | 50 | 100 |

---

### Related Endpoints

- `POST /v1/chat/conversations/{conversationId}/messages` - Send a new message
- `GET /v1/chat/conversations` - List conversations

**Parameters:**
- `conversationId` (path) (required)
- `organizationId` (query) (required) — Organization ID - use "default" to use the user's default organization
- `page` (query) — Page number
- `limit` (query) — Items per page
- `userEmail` (query) — User email address to identify the end user
- `userExternalUUID` (query) — External unique identifier for the end user

**Responses:**
- 200
- 400
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.

### POST /v1/chat/conversations/{conversationId}/messages

- operationId: `ChatApi_sendMessage`
- summary: Send message to conversation

## Send Message

Sends a user message to an existing conversation and returns the AI coach's response.

---

### User Identification

Provide at least one of `userEmail` or `userExternalUUID` in the request body to identify the end user. The user must be the owner of the conversation.

---

### Response

Returns three objects:
- `userMessage` - The saved user message
- `assistantMessage` - The AI coach response
- `conversation` - Updated conversation metadata (messageCount, lastMessageAt)

---

### AI Capabilities

The AI coach can:
- List, search, and recommend workouts, programs, and exercises (returned in `metadata.contentPreview`)
- Propose state-changing actions like logging nutrition, adding/assigning workouts and plans, adapting or generating workouts and programs (returned in `metadata.pendingAction`)
- Provide personalized advice based on user profile, training history, and goals

---

### Rate Limit

This endpoint counts as 10x API calls toward your hourly and daily limits.

---

### Related Endpoints

- `POST /v1/chat/conversations` - Start a new conversation
- `POST /v1/chat/conversations/{conversationId}/actions/confirm` - Confirm a pending action
- `GET /v1/chat/conversations/{conversationId}/messages` - Get message history

**Parameters:**
- `conversationId` (path) (required)

**Request body (required):** application/json

**Responses:**
- 201
- 400
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.
- 404 — Conversation not found or does not belong to the user.

### POST /v1/chat/conversations/{conversationId}/actions/confirm

- operationId: `ChatApi_confirmAction`
- summary: Confirm a pending AI action

## Confirm Action

Executes a pending action proposed by the AI coach after user confirmation.

---

### How It Works

1. The AI proposes an action via `assistantMessage.metadata.pendingAction`
2. Your client shows a confirmation UI with the action `summary`
3. User confirms, and you call this endpoint with the `pendingActionId`
4. The action is executed and the result is returned

---

### Supported Action Types

| Type | Description | Response Field |
|------|-------------|----------------|
| `log_nutrition` | Log a meal or nutrition entry | `nutritionOverview` |
| `add_workouts` | Add workouts to user's library | `message` |
| `add_plans` | Add plans to user's library | `message` |
| `assign_workouts` | Assign workouts to the user | `message` |
| `assign_plans` | Assign plans to the user | `message` |
| `adapt_workout` | Adapt a workout on the fly | `adaptedWorkout` |
| `adapt_program` | Adapt a program on the fly | `adaptedProgram` |
| `generate_workout` | Generate a new workout | `generatedWorkout` |
| `generate_program` | Generate a new program | `generatedProgram` |

---

### Adapted/Generated Content

`adaptedWorkout` and `generatedWorkout` use the same response format as `POST /v1/orgs/{orgId}/workouts/{id}/adapt` and `POST /v1/orgs/{orgId}/workouts/generate` respectively. Same applies to program equivalents. Clients can reuse the same types and rendering logic.

---

### Related Endpoints

- `POST /v1/chat/conversations/{conversationId}/messages` - Send a message (may return pendingAction)
- `POST /v1/chat/conversations` - Start a conversation (may return pendingAction)

**Parameters:**
- `conversationId` (path) (required)

**Request body (required):** application/json

**Responses:**
- 200 — Action executed successfully.
- 400 — Action expired or invalid.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.
- 404 — Pending action not found or already used.

### DELETE /v1/chat/conversations/{conversationId}

- operationId: `ChatApi_archiveConversation`
- summary: Archive conversation

## Archive Conversation

Soft-deletes a conversation by setting its status to `archived`. The conversation and its messages are retained in the database but will no longer appear in the conversations list.

---

### User Identification

Provide at least one of `userEmail` or `userExternalUUID` as query parameters to identify the end user. The user must be the owner of the conversation.

---

### Related Endpoints

- `GET /v1/chat/conversations` - List active conversations
- `GET /v1/chat/conversations/{conversationId}/messages` - Get messages before archiving

**Parameters:**
- `conversationId` (path) (required)
- `organizationId` (query) (required) — Organization ID - use "default" to use the user's default organization
- `userEmail` (query) — User email address to identify the end user
- `userExternalUUID` (query) — External unique identifier for the end user

**Responses:**
- 200 — Conversation archived successfully.
- 400
- 401 — Authentication failed - API key is missing or invalid.
- 403 — API key is not authorized for this organization.

## Tag: endusers-plans

### GET /v1/orgs/{organizationId}/endusers/{endUserId}/plans/active/progress

- operationId: `OrganizationEndUserPlansApi_getActivePlanProgress`
- summary: Get plan progress for an end user

## Plan progress

Returns per-workout progress for the end user's active plan. Keys are position indices (as strings); values include completed, latestCompletionDate, workoutName, difficulty, duration, categories, workoutId, positionInPlan.

When the end user has no active plan, the endpoint responds with `200 OK` and `{ "data": null }` (rather than 404), so callers can render a "no active plan" state without treating it as an error.

---

### Related endpoints

- `GET .../plans/active` - Get active plan details
- `POST .../plans/start` - Start a plan
- `POST .../plans/{planId}/end` - End active plan

**Parameters:**
- `organizationId` (path) (required) — The organization id (24-character hex string).
- `endUserId` (path) (required) — The user identifier. Can be email, externalUUID, or internal user ID.

**Responses:**
- 200 — Plan progress for the end user, or `data: null` when there is no active plan.
- 401
- 403

### GET /v1/orgs/{organizationId}/endusers/{endUserId}/plans/active

- operationId: `OrganizationEndUserPlansApi_getActivePlan`
- summary: Get active plan for an end user

## Active plan

Returns the end user's active workout plan if any, with schedule (workoutsPerWeek, durationInWeeks, currentWeekNumber, daysSinceStart, completedWorkouts, totalWorkouts, completionPercentage).

---

### Related endpoints

- `GET .../plans/active/progress` - Get per-workout progress
- `POST .../plans/start` - Start a plan
- `POST .../plans/{planId}/end` - End active plan

**Parameters:**
- `organizationId` (path) (required) — The organization id (24-character hex string).
- `endUserId` (path) (required) — The user identifier. Can be email, externalUUID, or internal user ID.

**Responses:**
- 200 — Active plan or null.
- 401
- 403

### POST /v1/orgs/{organizationId}/endusers/{endUserId}/plans/start

- operationId: `OrganizationEndUserPlansApi_startPlan`
- summary: Start a plan for an end user

## Start plan

Starts the given plan as the end user's active plan. Any existing active plan is deactivated.

---

### Related endpoints

- `GET .../plans/active` - Get active plan details
- `GET .../plans/active/progress` - Get plan progress
- `POST .../plans/{planId}/end` - End active plan
- `GET /v1/plans/{id}` - Plan catalog details
- `GET /v1/plans/{id}/workouts` - Workouts in plan

**Parameters:**
- `organizationId` (path) (required) — The organization id (24-character hex string).
- `endUserId` (path) (required) — The user identifier. Can be email, externalUUID, or internal user ID.

**Request body (required):** application/json

**Responses:**
- 200 — Plan access record.
- 401
- 403

### POST /v1/orgs/{organizationId}/endusers/{endUserId}/plans/{planId}/end

- operationId: `OrganizationEndUserPlansApi_endPlan`
- summary: End an active plan for an end user

## End plan

Ends the specified plan for the end user. The plan must be the user's current active plan. Returns 404 if no active plan matches the given planId.

---

### Related endpoints

- `GET .../plans/active` - Get active plan details
- `GET .../plans/active/progress` - Get plan progress
- `POST .../plans/start` - Start a plan

**Parameters:**
- `organizationId` (path) (required) — The organization id (24-character hex string).
- `endUserId` (path) (required) — The user identifier. Can be email, externalUUID, or internal user ID.
- `planId` (path) (required) — The plan ID to end.

**Responses:**
- 200 — Updated plan access record.
- 401
- 403
- 404

## Tag: endusers-management

### GET /v1/orgs/{organizationId}/endusers

- operationId: `OrganizationEndUsersApi_getOrganizationEndUsers`
- summary: Get organization end users

Retrieves a paginated list of end users associated with the organization.

`endUserId` is present only when the API key is for this organization and the end user has an active (non-suspended) team membership.

### Pagination
Use the standard `offset`/`limit` cursors. The response contains the canonical `links` envelope:
- `links.next` — fully qualified URL for the next page (omitted on the last page)
- `links.total` — total number of matching end users

The top-level `total` field is **deprecated** and kept only for backwards compatibility; new integrations should read `links.total` instead.

**Parameters:**
- `organizationId` (path) (required) — The unique identifier (UUID) of the organization.
- `offset` (query) — Offset for pagination. Defaults to 0.
- `limit` (query) — Number of items per page. Defaults to 20, Max 100.
- `status` (query) — Filter clients by status: "active", "suspended", or "all"
- `sortBy` (query) — Sort clients by "name", "status", or "joined" (default)
- `sortDirection` (query) — Sort direction: "asc" (default) or "desc"

**Responses:**
- 200 — Successfully retrieved list of end users for the organization.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — The API key is not associated with the requested organization.
- 404 — The specified organization ID does not exist.

### DELETE /v1/orgs/{organizationId}/endusers

- operationId: `OrganizationEndUsersApi_removeEndUser`
- summary: Remove end user from organization

## Permanently Remove User

Completely removes a user from your organization. This action is **irreversible** and will:

- Delete all user data and progress
- Remove access to organization content
- Clear workout history and sessions
- Cannot be undone

---

### ⚠️ Important Considerations

**Before Removing:**
- User will lose all progress and data
- Consider suspending instead for temporary access removal
- Backup any important user data first
- Notify user if required by your policies

**Use Cases:**
- GDPR compliance (right to be forgotten)
- Account closure requests
- Data cleanup and maintenance
- User requested permanent deletion

---

### 🔄 Alternative Actions

**Temporary Removal:**
- `PUT /v1/orgs/{orgId}/endusers/suspend` - Temporarily disable access
- User data preserved, can be reactivated later
- Better for temporary access issues

**Permanent Removal:**
- `DELETE /v1/orgs/{orgId}/endusers` - This endpoint
- Complete data deletion, cannot be undone
- Use for final account closure

---

### 💡 Best Practices

✅ **Verify user identity** - Double-check email/UUID before deletion  
✅ **Consider suspension first** - Try suspend before permanent removal  
✅ **Log deletion events** - Track for audit and compliance  
✅ **Handle gracefully** - 404 is expected if user already removed  
❌ **Don't delete active users** - Consider suspending instead

---

### 🔗 Related Endpoints

- Find User: `GET /v1/orgs/{orgId}/endusers/find` - Locate user first
- Suspend User: `PUT /v1/orgs/{orgId}/endusers/suspend` - Temporary removal
- Reactivate User: `PUT /v1/orgs/{orgId}/endusers/reactivate` - Restore suspended user

**Parameters:**
- `organizationId` (path) (required) — The unique identifier (UUID) of the organization.

**Request body (required):** application/json

**Responses:**
- 200 — Successfully removed end user from organization.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — The API key is not associated with the requested organization.
- 404 — End user not found in the organization.

### POST /v1/orgs/{organizationId}/endusers/invite

- operationId: `OrganizationEndUsersApi_inviteEndUsers`
- summary: Invite end users to organization

## Add Users to Your Organization

Send invitations to one or more end users. Each user receives access to your organization's content and features.

---

### 📋 Identifier Options

**Email Only:**
- User invited via email address
- Hyperhuman creates account on first login
- Email must be valid and unique

**Email + External UUID:**
- Map to your existing user system
- Track users across platforms
- Sync user data between systems
- **Recommended for integrations**

---

### 🎯 Typical User Lifecycle

1. **Invite**: `POST /v1/orgs/{orgId}/endusers/invite` → Add users
2. **Verify**: `GET /v1/orgs/{orgId}/endusers/find?email=...` → Check status
3. **List**: `GET /v1/orgs/{orgId}/endusers` → View all users
4. **Manage**: Suspend/reactivate as needed
5. **Remove**: `DELETE /v1/orgs/{orgId}/endusers` → Permanent deletion

---

### 💡 Best Practices

✅ **Batch invitations** - Send up to 100 users at once for efficiency  
✅ **Use externalUUID** - Map to your user IDs for sync  
✅ **Validate emails** - Check format before sending  
✅ **Handle failures** - Review failed array for issues  
❌ **Don't re-invite** - Check if user exists first

---

### 🔍 Error Handling

Common failure reasons:
- `User already exists` - Email already in organization
- `Invalid email format` - Email validation failed
- `Duplicate in batch` - Same email appears twice
- `Organization quota exceeded` - User limit reached

---

### 🔗 Related Endpoints

- Find User: `GET /v1/orgs/{orgId}/endusers/find`
- List Users: `GET /v1/orgs/{orgId}/endusers`
- Suspend User: `PUT /v1/orgs/{orgId}/endusers/suspend`
- Remove User: `DELETE /v1/orgs/{orgId}/endusers`

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.

**Request body (required):** application/json

**Responses:**
- 200 — Invitation processing complete. Returns success count, any failures, and optionally invited with endUserId when the user already has an active team membership.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — The API key is not associated with the requested organization.

### PUT /v1/orgs/{organizationId}/endusers/suspend

- operationId: `OrganizationEndUsersApi_suspendEndUser`
- summary: Suspend end user in organization

## Temporarily Suspend User Access

Suspends a user's access to your organization content while preserving their data and progress. This is a **reversible action** that:

- Blocks access to workouts and plans
- Preserves user data and progress
- Can be reactivated later
- User cannot log in during suspension

---

### 🎯 When to Suspend

**Temporary Access Issues:**
- Payment/billing problems
- Policy violations (temporary)
- Account security concerns
- User requested break

**vs. Permanent Removal:**
- Suspension: Temporary, data preserved
- Deletion: Permanent, data lost
- Choose suspension for reversible actions

---

### 🔄 User Experience During Suspension

**What Users See:**
- Cannot access organization content
- Login blocked with suspension message
- All data and progress preserved
- Can be reactivated by admin

**What Admins Can Do:**
- View suspended users in user list
- Reactivate anytime with reactivate endpoint
- See suspension status in user details
- Track suspension history

---

### 💡 Best Practices

✅ **Communicate with user** - Explain why and duration  
✅ **Set clear policies** - Define suspension criteria  
✅ **Monitor suspension duration** - Don't leave users suspended indefinitely  
✅ **Use for temporary issues** - Permanent problems may need deletion  
✅ **Document reasons** - Track why users were suspended

---

### 🔗 Related Endpoints

- Find User: `GET /v1/orgs/{orgId}/endusers/find` - Locate user first
- Reactivate User: `PUT /v1/orgs/{orgId}/endusers/reactivate` - Restore access
- Remove User: `DELETE /v1/orgs/{orgId}/endusers` - Permanent deletion
- List Users: `GET /v1/orgs/{orgId}/endusers` - View all users (including suspended)

**Parameters:**
- `organizationId` (path) (required) — The unique identifier (UUID) of the organization.

**Request body (required):** application/json

**Responses:**
- 200 — Successfully suspended end user in organization.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — The API key is not associated with the requested organization.
- 404 — End user not found in the organization.

### PUT /v1/orgs/{organizationId}/endusers/reactivate

- operationId: `OrganizationEndUsersApi_reactivateEndUser`
- summary: Reactivate end user in organization

## Restore Suspended User Access

Reactivates a previously suspended user, restoring full access to your organization content. This action:

- Restores login access immediately
- Preserves all user data and progress
- Returns user to active status
- Can be done anytime after suspension

---

### 🎯 When to Reactivate

**Common Scenarios:**
- Payment issues resolved
- Policy violations addressed
- Security concerns cleared
- User requested restoration
- Administrative error correction

**Prerequisites:**
- User must be currently suspended
- Cannot reactivate already active users
- Cannot reactivate permanently deleted users

---

### 🔄 User Experience After Reactivation

**Immediate Access:**
- User can log in immediately
- Full access to organization content
- All previous data and progress intact
- No data loss from suspension period

**Status Changes:**
- User status changes from "suspended" to "active"
- Appears in active user lists
- Can participate in workouts and plans
- Normal user functionality restored

---

### 💡 Best Practices

✅ **Verify suspension reason resolved** - Ensure original issue is fixed  
✅ **Communicate with user** - Notify them of reactivation  
✅ **Monitor user activity** - Ensure they can access content  
✅ **Update user records** - Track reactivation for audit  
✅ **Test user access** - Verify login and content access works

---

### 🔗 Related Endpoints

- Find User: `GET /v1/orgs/{orgId}/endusers/find` - Check current status
- Suspend User: `PUT /v1/orgs/{orgId}/endusers/suspend` - Suspend if needed again
- List Users: `GET /v1/orgs/{orgId}/endusers` - View all users
- Remove User: `DELETE /v1/orgs/{orgId}/endusers` - Permanent deletion (if needed)

**Parameters:**
- `organizationId` (path) (required) — The unique identifier (UUID) of the organization.

**Request body (required):** application/json

**Responses:**
- 200 — Successfully reactivated end user in organization.
- 401 — Authentication failed - API key is missing or invalid.
- 403 — The API key is not associated with the requested organization.
- 404 — End user not found in the organization.

### GET /v1/orgs/{organizationId}/endusers/find

- operationId: `OrganizationEndUsersApi_getEndUserById`
- summary: Find end user by email or external UUID

## Find Specific User

Retrieve a single user from your organization by email or external UUID. Useful for checking if a user exists before inviting.

---

### 📋 Identifier Options

**By Email:**
```bash
GET /v1/orgs/{orgId}/endusers/find?email=john.doe@acme.com
```

**By External UUID:**
```bash
GET /v1/orgs/{orgId}/endusers/find?externalUUID=ext-user-12345
```

**Required:** Must provide either `email` OR `externalUUID` (not both)

---

### 🎯 Use Cases

✅ **Pre-invite Check** - Verify user doesn't exist before inviting  
✅ **User Lookup** - Find user details for support queries  
✅ **Sync Validation** - Confirm external UUID mapping is correct  
✅ **Duplicate Prevention** - Check before creating user in your system

---

### 💡 Best Practices

✅ **Email is case-insensitive** - john@acme.com = John@acme.com  
✅ **Use external UUID for integrations** - More reliable than email  
✅ **Handle 404 gracefully** - User not found is an expected case  
✅ **Cache lookups** - Reduce API calls for frequently accessed users

---

### 🔗 Related Endpoints

- Invite Users: `POST /v1/orgs/{orgId}/endusers/invite` - Add new users
- List Users: `GET /v1/orgs/{orgId}/endusers` - Browse all users
- Suspend User: `PUT /v1/orgs/{orgId}/endusers/suspend` - Deactivate user
- Remove User: `DELETE /v1/orgs/{orgId}/endusers` - Permanently delete

**Parameters:**
- `organizationId` (path) (required) — The unique identifier of the organization.
- `email` (query) — Email address of the user to find (case-insensitive)
- `externalUUID` (query) — External UUID of the user to find

**Responses:**
- 200 — Successfully retrieved end user profile. endUserId is present only when the end user has an active (non-suspended) team membership.
- 400 — Missing required identifier (email or externalUUID).
- 401 — Authentication failed - API key is missing or invalid.
- 403 — The API key is not associated with the requested organization.
- 404 — End user not found in the organization.
