Challenge Plays API
GET /api/challenge-plays
List all challenge plays for the authenticated client/user, with support for pagination, filtering, and sorting.
Query Parameters
clientId: string (required) — The client identifier. If not found, a 500 error is returned.page: number (optional, default: 1) — The page number (1-based).pageSize: number (optional, default: 20, max: 100) — Number of results per page.sortBy: string (optional, default:createdAt) — Field to sort by (e.g.,createdAt).sortOrder: string (optional, default:desc) — Sort order:ascordesc.userId: string (optional) — Filter by user ID.challengeId: string (optional) — Filter by challenge ID.startAfter: any (optional) — Cursor value for pagination (use the value of thesortByfield from the last item of the previous page).
Headers:
Authorization: Bearer <JWT>
Response
-
200 OK:Success Response{
"data": [ChallengePlay, ...],
"page": number,
"pageSize": number,
"hasMore": boolean,
"nextPageToken": any
} -
401 Unauthorized:{ "error": string } -
500 Internal Server Error:{ "error": "Client ID not found" | string } -
500 Firestore index required:{ "error": "Firestore index required", "details": string }
Use nextPageToken as the startAfter value for the next page request.
Filtering and sorting combinations may require Firestore composite indexes. If an index is missing, the error response will include a link to create it.
POST /api/challenge-plays
Create a new challenge play record for a user or guest.
Query Parameters
clientId: string (required) — The client identifier. If not found, a 500 error is returned.
Headers:
Authorization: Bearer <JWT>
Request Body:
{
"challengeId": "string", // Required. The challenge ID
"userId": "string", // Optional. User's ID
"email": "string", // Optional. User's email
"displayName": "string", // Optional. User's display name
"photoURL": "string", // Optional. User's profile photo URL
"targetRepCount": 50, // Optional. Target repetitions
"targetDuration": 60 // Optional. Target duration (seconds)
}
Response
-
200 OK:Success Response{
"challengePlayId": "string", // Unique ID for the challenge play
"redirectUrl": "string", // URL to redirect the user to the challenge play
"createdAt": "string" // ISO date of creation
} -
400 Bad Request:{ "error": "string" } -
500 Internal Server Error:{ "error": "string" }
ChallengePlay Object (for reference)
id: string — Unique challenge play IDclientId: string — Client IDuserId: string (optional) — User IDchallengeId: string — Challenge IDcreatedAt: string — ISO datecompletedAt: string (optional, nullable) — ISO dateactivityTime: number (optional) — Seconds spent in activityrepCount: number (optional) — Number of reps completedgrade: string (optional) — Grade achievedtargetRepCount: number (optional) — Target reps (Time Attack)targetDuration: number (optional) — Target duration in seconds (Endurance)caloriesBurned: number (optional)email: string (optional)displayName: string (optional)photoURL: string (optional)status: string (optional) —pending,started,completed,cancelled
{
"id": "play123",
"clientId": "gofa",
"userId": "user456",
"challengeId": "challenge123",
"createdAt": "2024-05-25T10:00:00.000Z",
"completedAt": null,
"activityTime": 60,
"repCount": 50,
"grade": "S",
"targetRepCount": 50,
"targetDuration": 60,
"caloriesBurned": 25,
"email": "user@example.com",
"displayName": "User Name",
"photoURL": "https://example.com/photo.jpg",
"status": "completed"
}
Requires a valid JWT for the client.
Validates challenge and user existence.
The actual schema may include additional fields. For the most up-to-date schema, refer to the challenge play model/interface in your codebase.
All fields may not be present on every challenge play. Optional and nullable fields are indicated above.