Start, inspect, complete, and embed a lesson play.
A lesson play is a user's runtime record for one lesson. The endpoints are authenticated and client-scoped by the request host.
Use ClientToken: <client-token> for a backend integration or
Authorization: Bearer <firebase-id-token> for a signed-in user. See
Activities authentication.
POST /api/lesson-plays
The client ID is derived from the request host. The JSON body requires
lessonId:
{
"lessonId": "lesson-123",
"userId": "user-456",
"email": "user@example.com",
"displayName": "Example User",
"photoURL": "https://cdn.example.com/user.jpg",
"skipWarmUp": false,
"skipCoolDown": false,
"skipCalibration": false,
"locale": "en",
"playbackSpeed": 1,
"performanceTier": "medium",
"returnUrl": "https://partner.example.com/lesson-finished",
"planPlayId": "plan-play-123"
}Optional fields are userId, email, displayName, photoURL,
skipWarmUp, skipCoolDown, skipCalibration, locale, playbackSpeed,
performanceTier, returnUrl, and planPlayId. Supported locale values are
en, zh, zh-Hant, zh-Hans, and zh-TW. performanceTier accepts
high, medium, or low.
The response contains the created ID and the route used to launch it:
{
"lessonPlayId": "generated-lesson-play-id",
"redirectUrl": "/move/lesson/play/generated-lesson-play-id",
"createdAt": "2026-09-08T04:00:00.000Z"
}When a Firebase ID token is used, a supplied userId must equal the token's
UID. A client token is the backend integration credential for creating a play
on behalf of a client. returnUrl must be a valid URL when supplied.
GET /api/lesson-plays
Use clientId to select a client when the integration is authorized to do so;
when it is omitted, the request host supplies the client. The endpoint accepts
these query parameters:
| Query parameter | Description |
|---|---|
page | 1-based page number; defaults to 1. |
pageSize or limit | Positive page size; defaults to 20. |
sortBy | Sort field. |
sortOrder | asc or desc. |
lessonId | Filter by lesson. |
userId | Filter by user; a Firebase token is restricted to its own UID. |
period | all, week, or month. |
includeSummary | Include aggregate summary fields when true. |
startAfter | The opaque nextPageToken returned by the previous page. |
The response is paginated:
{
"data": [],
"page": 1,
"pageSize": 20,
"totalCount": 0,
"totalPages": 0,
"hasMore": false,
"nextPageToken": null
}When includeSummary=true, summary may include
totalLessons, completedLessons, openOnlyAttempts, totalMinutes,
totalCalories, averageQualityScore, and averagePoseSimilarity.
GET /api/lesson-plays/{lessonPlayId} returns the record under data.
When available, the response also includes display titles for its lesson and
sessions.
PATCH /api/lesson-plays/{lessonPlayId} currently supports only
returnUrl, including null to clear it:
{ "returnUrl": "https://partner.example.com/lesson-finished" }The response is:
{
"success": true,
"lessonPlayId": "generated-lesson-play-id",
"updatedFields": ["returnUrl"]
}These operations return 404 for an unknown play and 401 when the Firebase token does not belong to the play's user.
POST /api/lesson-plays/{lessonPlayId}/complete
The completion request requires activityTime as a finite number greater than
or equal to zero. sessions is optional and contains the recorded session
objects:
{
"activityTime": 183,
"sessions": []
}The endpoint requires a Firebase ID token whose UID is the play's user. A
successful request returns success, the lessonPlayId, the updated
activityTime, and any calculated offlineClass result.
The data object can contain the play ID, client and user IDs, lessonId, an
optional planPlayId, timestamps, completion state, activity metrics,
sessions, locale, skip flags, playback settings, and returnUrl. The exact
metric and session fields depend on the lesson configuration and recorded
runtime data; clients should preserve unknown response fields.
To embed the runtime player and exchange parent-frame events, see Lesson play postMessage integration.