Create and query authenticated challenge play records.
Challenge plays are records for a user attempting a challenge. The endpoints use the client selected by the request host for create and single-record requests. The list route requires the client ID in the query string.
Use ClientToken: <client-token> for a backend integration or
Authorization: Bearer <firebase-id-token> for a signed-in user. See
Activities authentication.
POST /api/challenge-plays
The client ID is derived from the request host. Send JSON with a required
challengeId:
{
"challengeId": "challenge-123",
"userId": "user-456",
"email": "user@example.com",
"displayName": "Example User",
"photoURL": "https://cdn.example.com/user.jpg",
"targetRepCount": 30,
"targetDuration": 60
}userId, email, displayName, photoURL, targetRepCount, and
targetDuration are optional. The referenced challenge must exist. If a
Firebase user token is used, a supplied userId must match the token UID. A
successful request returns 200:
{
"challengePlayId": "generated-play-id",
"redirectUrl": "/move/challenge/play/generated-play-id",
"createdAt": "2026-09-08T04:00:00.000Z"
}The returned redirectUrl is a path on the same GOFA client host. The API
creates the record with pending status and an initial activityTime of zero.
GET /api/challenge-plays
This route requires clientId as a query parameter:
/api/challenge-plays?clientId=your-client-id&page=1&pageSize=20&sortBy=createdAt&sortOrder=desc| Query parameter | Description |
|---|---|
clientId | Required client identifier. |
page | One-based page number; defaults to 1. |
pageSize | Results per page; defaults to 20 and is capped at 100. |
sortBy | Firestore field to order by; defaults to createdAt. |
sortOrder | asc or desc; defaults to desc. |
userId | Optional user filter. |
challengeId | Optional challenge filter. |
startAfter | Optional cursor value. Pass the returned nextPageToken on a later request. |
The response is:
{
"data": [],
"page": 1,
"pageSize": 20,
"hasMore": false,
"nextPageToken": null
}Filtering by userId is checked against the Firebase token UID unless the
credential has an authorized integration scope. A missing Firestore composite
index is reported as 500 with error: "Firestore index required".
GET /api/challenge-plays/{challengePlayId}
The client is selected by the request host and the response is:
{
"data": {
"id": "generated-play-id",
"clientId": "your-client-id",
"challengeId": "challenge-123",
"createdAt": "2026-09-08T04:00:00.000Z",
"activityTime": 0,
"status": "pending"
}
}If the record has a userId, a Firebase user token must have the same UID.
The ChallengePlay object can additionally contain completedAt,
repCount, grade, targetRepCount, targetDuration,
caloriesBurned, email, displayName, and photoURL.
Status values are pending, started, completed, and
cancelled.
404 means the play ID was not found, and 401 means the user token does not
match the record. The single-record handler should be treated as a client-host
request and token check; confirm tenant ownership behavior with GOFA before
using an untrusted play ID as a cross-tenant lookup key.