Lessons API
GET /api/lessons
List all lessons for the authenticated client.
Query Parameters
clientId: string (required) — The client identifier. Requests without this parameter will return a400error.
Authenticated B2B API requests to the lessons API family are usage-metered at the platform level. Commercial plans may apply request tracking, quota checks, or billing rules to these endpoints as GOFA's public API platform expands. This note does not change the current request or response contract for existing integrations.
Headers:
Authorization: Bearer <JWT>
Response
200 OK:{ data: Array<LessonConfig> }400 Bad Request:{ error: 'clientId is required.' }401 Unauthorized:{ error: string }500 Internal Server Error:{ error: string }
Each LessonConfig object includes (fields may be optional):
id: stringidHtmlEncoded: stringclientId: stringstatus: GENERAL_STATUSuniqueID: stringbaseLessonID: stringimageUrl: stringimageBlurDataUrl: stringthumbImageUrl: stringtitle: Localizable (object with language keys, e.g.{ en: "...", zh_Hans: "..." })description: LocalizablevideoURLs: string[]videoId: stringsubtitleUrl: LocalizableplanEnrollmentId: stringMET: numberduration: number (seconds)isIndividual: booleanintensity: number | stringalgorithm: WORKOUT_ALGORITHMsessions: SessionConfig[] (see below)groupByIntensity: GROUP_INTENSITYgroupByStyles: GROUP_STYLE[]groupByTypes: GROUP_TYPE[]groupByMuscles: GROUP_MUSCLE[]groupByGoals: GROUP_GOAL[]groupByEquipments: GROUP_EQUIPMENT[]groupByTrainers: string[]requiredWarmup: booleanrequiredCooldown: booleansync: booleanbatch: numberrelatedLessons: LessonConfig[]modifiedBy: stringmodifyDatetime:{ _seconds: number, _nanoseconds: number }createdBy: stringcreateDatetime:{ _seconds: number, _nanoseconds: number }
GET /api/lessons/[id]
Fetch a lesson by its ID.
Headers:
Authorization: Bearer <JWT>
Path Parameters:
id: string (required) — The lesson's unique identifier
Response:
200 OK:{ lesson: LessonConfig }404 Not Found:{ error: string }401 Unauthorized:{ error: string }500 Internal Server Error:{ error: string }
GET /api/b2b/lessons/catalog
List the lessons available in the B2B Lessons configuration workspace for the authenticated client admin.
This endpoint is intended for the GOFA B2B admin workspace. It requires administrator access for the target client. GOFA platform administrators may include a clientId query parameter when managing another client.
Headers:
Authorization: Bearer <Firebase JWT>
Query Parameters:
| Name | Required | Description |
|---|---|---|
clientId | No | Target client identifier. Only GOFA platform administrators can use this to manage a different client. |
Response:
200 OK: lesson catalog summary401 Unauthorized: missing or invalid authentication403 Forbidden: authenticated admin cannot manage the requested client404 Not Found: target client was not found500 Internal Server Error: unexpected server error
{
"clientId": "demo",
"lessons": [
{
"id": "lesson_123",
"name": { "en": "Balance Basics", "zh": "平衡基礎" },
"duration": 12,
"categories": ["Strength"],
"areas": ["Lower Body"],
"equipment": "No Equipment",
"intensity": "Low",
"algorithm": "Engagement",
"status": "active",
"createdDate": "2026-05-09T00:00:00.000Z",
"isLive": true,
"membershipTiers": ["standard"],
"subtitleUrl": { "en": "/api/media/path/to/subtitle.vtt" },
"sessions": [
{
"title": { "en": "Warm up" },
"startSeconds": 0,
"endSeconds": 60,
"type": "Warm Up"
}
]
}
],
"liveLessonIds": ["lesson_123"],
"allTiers": ["standard", "premium"]
}
PATCH /api/b2b/lessons/catalog
Update lesson visibility and membership-tier assignment for the authenticated client's B2B Lessons configuration workspace.
Headers:
Authorization: Bearer <Firebase JWT>Content-Type: application/json
Query Parameters:
| Name | Required | Description |
|---|---|---|
clientId | No | Target client identifier. Only GOFA platform administrators can use this to manage a different client. |
Request Body:
| Field | Type | Description |
|---|---|---|
liveLessonIds | string[] | Complete list of lessons that should be live for the managed lesson catalog. |
lessonTierUpdates | Array<{ lessonId: string, membershipTiers: string[] }> | Membership-tier assignment updates for one or more lessons. |
force | boolean | When true, allows a live-status update to proceed even when affected live plans also need to be turned off. |
At least one of liveLessonIds or lessonTierUpdates is required.
{
"liveLessonIds": ["lesson_123", "lesson_456"],
"lessonTierUpdates": [
{ "lessonId": "lesson_123", "membershipTiers": ["standard", "premium"] }
]
}
Response:
200 OK:{ "success": true, "catalog": { ... } }400 Bad Request: invalid payload or unknown lesson IDs401 Unauthorized: missing or invalid authentication403 Forbidden: authenticated admin cannot manage the requested client409 Conflict: one or more live plans include a lesson that would be turned off500 Internal Server Error: unexpected server error
{
"error": "Unable to update lessons catalog",
"conflicts": [
{
"id": "plan_123",
"title": { "en": "Balance Starter Plan" }
}
]
}
When a 409 response is returned, send the same intended liveLessonIds with force: true only after the administrator confirms that affected plans should be turned off together with the lesson.
POST /api/b2b/lessons/catalog
Create a lesson for the authenticated client's B2B Lessons workspace.
Headers:
Authorization: Bearer <Firebase JWT>Content-Type: application/json
Query Parameters:
| Name | Required | Description |
|---|---|---|
clientId | No | Target client identifier. Only GOFA platform administrators can use this to manage a different client. |
Request Body:
| Field | Type | Description |
|---|---|---|
title.en | string | Required English lesson title. |
title.zh_Hant | string | Traditional Chinese lesson title. |
description | object | Localized lesson description fields. |
duration | number | Lesson duration in minutes. |
MET | number | Metabolic equivalent value used for activity calculations. |
algorithm | string | Lesson AI type, such as Engagement, Reps, Mirroring, or Video. |
intensity | string | Display intensity, such as Low, Medium, or High. |
categories | string[] | Lesson categories shown in the lesson library. |
areas | string[] | Target areas shown in the lesson library. |
equipment | string | Main equipment label. |
imageUrl | string | Lesson poster image URL. |
thumbImageUrl | string | Lesson thumbnail URL. |
thumbnailTimeInSeconds | number | Video timestamp used when deriving a thumbnail from the lesson video. |
videoURLs | string[] | Lesson video URLs. |
subtitleUrl | object | Localized subtitle URLs, for example { "en": "...", "zh_Hant": "..." }. |
sessions | Array<object> | Lesson session timeline entries. Each entry can include title, startSeconds, endSeconds, type, repPoseId, and repCount. |
isLive | boolean | Whether the lesson should appear in the app lesson list. |
membershipTiers | string[] | Membership tiers that can access the lesson. |
{
"title": { "en": "Balance Basics", "zh_Hant": "平衡基礎" },
"description": { "en": "A guided balance lesson." },
"duration": 12,
"MET": 3,
"algorithm": "Engagement",
"intensity": "Low",
"categories": ["Strength"],
"areas": ["Lower Body"],
"equipment": "No Equipment",
"imageUrl": "https://cdn.example.com/lesson.jpg",
"thumbImageUrl": "https://cdn.example.com/lesson-thumb.jpg",
"videoURLs": ["https://cdn.example.com/lesson.m3u8"],
"subtitleUrl": { "en": "https://cdn.example.com/lesson-en.vtt" },
"sessions": [
{
"title": { "en": "Main set", "zh_Hant": "主要訓練" },
"startSeconds": 0,
"endSeconds": 600,
"type": "Engagement"
}
],
"isLive": false,
"membershipTiers": ["standard"]
}
Response:
201 Created:{ "success": true, "lessonId": "lesson_123", "catalog": { ... } }400 Bad Request: invalid payload401 Unauthorized: missing or invalid authentication403 Forbidden: authenticated admin cannot manage the requested client500 Internal Server Error: unexpected server error
PATCH /api/b2b/lessons/catalog/[lessonId]
Update editable lesson metadata, media URLs, lesson-list visibility, and membership-tier assignment for one lesson.
Headers:
Authorization: Bearer <Firebase JWT>Content-Type: application/json
Query Parameters:
| Name | Required | Description |
|---|---|---|
clientId | No | Target client identifier. Only GOFA platform administrators can use this to manage a different client. |
Request Body:
Send any subset of the fields accepted by POST /api/b2b/lessons/catalog. To hide a lesson from the app lesson list without deleting it, send isLive: false.
{
"title": { "en": "Balance Basics - Updated" },
"isLive": false,
"membershipTiers": ["premium"]
}
Response:
200 OK:{ "success": true, "catalog": { ... } }400 Bad Request: invalid payload401 Unauthorized: missing or invalid authentication403 Forbidden: authenticated admin cannot manage the requested client404 Not Found: lesson was not found for the managed client409 Conflict: one or more live plans include a lesson that would be turned off500 Internal Server Error: unexpected server error
POST /api/b2b/lessons/catalog/[lessonId]/subtitle
Upload or replace one localized WebVTT subtitle file for a lesson managed in the B2B Lessons workspace.
Headers:
Authorization: Bearer <Firebase JWT>Content-Type: multipart/form-data
Query Parameters:
| Name | Required | Description |
|---|---|---|
clientId | No | Target client identifier. Only GOFA platform administrators can use this to manage a different client. |
Form Data:
| Field | Type | Description |
|---|---|---|
locale | string | Subtitle locale. Supported values are en, zh_Hant, and zh_Hans. |
file | File | WebVTT subtitle file (.vtt). |
Response:
200 OK:{ "success": true, "subtitleUrl": "...", "catalog": { ... } }400 Bad Request: unsupported locale, missing file, or unsupported file type401 Unauthorized: missing or invalid authentication403 Forbidden: authenticated admin cannot manage the requested client404 Not Found: lesson was not found for the managed client500 Internal Server Error: unexpected server error
SessionConfig (in sessions array)
Each lesson contains an array of SessionConfig objects. See src/types/lesson/session-config.ts for full details, but typical fields include:
index: numbercategory: stringtitle: Localizablestart: number (seconds)end: number (seconds)repPoseId: stringtargetRepCount: number
All fields may not be present on every lesson.
For the most up-to-date schema, refer to src/types/lesson/lesson-config.ts and src/types/lesson/session-config.ts.
Only documented fields are guaranteed; additional fields may be present depending on lesson data.