Create, read, and update progress records for lesson plans.
A plan play is a user's progress record for an ordered lesson plan. 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/plan-plays
The client ID is derived from the request host. The JSON body requires
planId:
{
"planId": "plan-123",
"userId": "user-456",
"plannedLessons": ["lesson-123", "lesson-456"],
"email": "user@example.com",
"displayName": "Example User",
"photoURL": "https://cdn.example.com/user.jpg",
"assignmentSource": "manual",
"riskLevel": "LOW"
}Optional fields are userId, plannedLessons, email,
displayName, photoURL, assignmentSource, and
riskLevel. assignmentSource accepts fall-risk, msk,
manual, or other. riskLevel accepts LOW,
INTERMEDIATE, or HIGH.
The 201 response wraps the created record:
{
"data": {
"id": "generated-plan-play-id",
"clientId": "your-client-id",
"planId": "plan-123",
"userId": "user-456",
"status": "started",
"currentLessonIndex": 0,
"plannedLessons": ["lesson-123", "lesson-456"],
"completedLessons": [],
"completedLessonPlays": [],
"totalLessonsCompleted": 0,
"createdAt": "2026-09-08T04:00:00.000Z"
}
}GET /api/plan-plays
The client is selected from the request host. Use these filters:
| Query parameter | Description |
|---|---|
userId | Optional user filter. |
status | Optional pending, started, paused, ended, or cancelled. |
planId | Optional plan filter. |
id | Optional comma-separated batch of IDs; at most 30 IDs. |
limit | Optional positive result limit. |
order | asc or desc; defaults to desc. |
Regular and batch responses have the shape:
{
"data": [],
"count": 0
}id batch requests return records that match the authenticated client and
requested filters. A Firebase user token is restricted to the effective user;
use the client token for a backend client integration.
GET /api/plan-plays/{planPlayId} returns:
{
"data": {
"id": "generated-plan-play-id",
"clientId": "your-client-id",
"planId": "plan-123",
"status": "started",
"currentLessonIndex": 0,
"plannedLessons": [],
"completedLessons": [],
"completedLessonPlays": [],
"totalLessonsCompleted": 0,
"createdAt": "2026-09-08T04:00:00.000Z"
}
}PATCH /api/plan-plays/{planPlayId} accepts the supported progress
fields status, currentLessonIndex, completedLessons,
completedLessonPlays, totalRepCount,
totalSecondsSpent, totalLessonsCompleted,
completedAt, and lastPlayedAt. The response is
{ data: PlanPlay }. The handler adds updatedAt; changing status to
ended sets completedAt when the record was not already ended.
Both single-record operations return 404 when the ID is not found and 403 when the authenticated client cannot access it.
GET /api/plan-plays/active?userId={userId}
The userId query parameter is required; planId optionally
restricts the lookup to one plan. The endpoint returns the most recent
started record:
{ "data": null }When a record exists, data is the complete PlanPlay object. A
Firebase user token can request only its own user ID.