Playtests Endpoints
These endpoints are for game owners managing playtests for their games. All require the game_owner scope.
List Playtests for a Game
Section titled “List Playtests for a Game”GET /api/v1/games/:gameId/playtests
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 20 | Max 100 |
cursor | string | — | Pagination cursor |
Response
Section titled “Response”{ "data": { "playtests": [ { "id": "pt-uuid", "gameId": "game-uuid", "visibility": "private", "quantity": 3, "durationMinutes": 60, "playerCount": 1, "costPerSlotCents": 2000, "payoutCents": 1000, "notesForTesters": "Focus on the tutorial flow", "status": "active", "isFreePublicTrial": false, "targetingType": null, "createdAt": "2026-02-01T10:00:00.000Z", "slots": { "open": 1, "reserved": 1, "submitted": 1, "accepted": 0, "rejected": 0, "expired": 0 } } ], "eligibleForFreeTrial": true }, "meta": { "requestId": "req_abc123def456", "timestamp": "2026-03-02T12:00:00.000Z", "cursor": "eyJpZCI6ImFiYzEyMyJ9", "hasMore": false }}Create Playtest Request
Section titled “Create Playtest Request”POST /api/v1/games/:gameId/playtests
Order playtests for a game.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
visibility | string | No | public or private (default: private) |
quantity | integer | No | Number of slots to create, 1-100 (default: 1) |
durationMinutes | integer | No | 30, 60, 120, or 180 (default: 60) |
playerCount | integer | No | Players per session, 1-8 (default: 1) |
notesForTesters | string | No | Instructions for playtesters (max 5000 chars) |
keysForTesters | string[] | No | Game keys to distribute to individual slots |
isFreePublicTrial | boolean | No | Use free public trial (one per game, singleplayer 30min only) |
targetingType | string | No | new (new playtesters only) or past (returning playtesters) |
pastPlaytesterScope | string | No | Required when targetingType is past: any or specific |
targetedPlaytesterId | string | No | Required when pastPlaytesterScope is specific |
curl -X POST https://app.weplaytestgames.com/api/v1/games/game-uuid/playtests \ -H "Authorization: Bearer wpg_sk_..." \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "visibility": "private", "quantity": 3, "notesForTesters": "Focus on the tutorial and first boss fight" }'const response = await fetch( `https://app.weplaytestgames.com/api/v1/games/${gameId}/playtests`, { method: 'POST', headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID(), }, body: JSON.stringify({ visibility: 'private', quantity: 3, notesForTesters: 'Focus on the tutorial and first boss fight', }), },);Response (Paid Playtest)
Section titled “Response (Paid Playtest)”Paid playtests return with requiresPayment: true and pricing info:
{ "data": { "playtest": { "id": "pt-uuid", "gameId": "game-uuid", "visibility": "private", "quantity": 3, "durationMinutes": 60, "playerCount": 1, "costPerSlotCents": 2000, "payoutCents": 1000, "notesForTesters": "Focus on the tutorial and first boss fight", "status": "pending_payment", "isFreePublicTrial": false, "createdAt": "2026-03-02T12:00:00.000Z" }, "requiresPayment": true, "costPerSlotCents": 2000, "totalCents": 6000 }, "meta": { "requestId": "req_abc123def456", "timestamp": "2026-03-02T12:00:00.000Z" }}Response (Free Public Trial)
Section titled “Response (Free Public Trial)”Free trials are immediately active and require admin approval:
{ "data": { "playtest": { "..." }, "slots": [{ "id": "slot-uuid", "status": "open", "..." }], "needsAdminApproval": true }, "meta": { "..." }}Get Playtest Details
Section titled “Get Playtest Details”GET /api/v1/playtests/:id
Returns details for a specific playtest request with slot statistics.
Response
Section titled “Response”{ "data": { "playtest": { "id": "pt-uuid", "gameId": "game-uuid", "gameName": "Dungeon Crawlers", "visibility": "private", "quantity": 3, "durationMinutes": 60, "playerCount": 1, "costPerSlotCents": 2000, "payoutCents": 1000, "notesForTesters": "Focus on the tutorial", "status": "active", "isFreePublicTrial": false, "targetingType": null, "createdAt": "2026-02-01T10:00:00.000Z", "slots": { "open": 1, "reserved": 1, "submitted": 1, "accepted": 0, "rejected": 0, "expired": 0 } } }, "meta": { "requestId": "req_abc123def456", "timestamp": "2026-03-02T12:00:00.000Z" }}Update Playtest
Section titled “Update Playtest”PATCH /api/v1/playtests/:id
Update an active playtest request.
Request Body
Section titled “Request Body”| Field | Type | Description |
|---|---|---|
notesForTesters | string | Updated instructions for playtesters (max 5000 chars) |
Response
Section titled “Response”Returns the updated playtest request object.
List Slots for a Playtest
Section titled “List Slots for a Playtest”GET /api/v1/playtests/:id/slots
Returns all slots for a playtest request with their current status (cursor-paginated).
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 20 | Max 100 |
cursor | string | — | Pagination cursor |
Response
Section titled “Response”{ "data": { "slots": [ { "id": "slot-uuid", "requestId": "pt-uuid", "status": "submitted", "deadlineAt": "2026-02-16T14:00:00.000Z", "createdAt": "2026-02-15T14:00:00.000Z", "playtester": { "id": "playtester-uuid", "displayName": "TestGamer42" }, "submission": { "id": "sub-uuid", "videoFileSize": 524288000, "notes": "Found a bug in level 3", "createdAt": "2026-02-15T18:30:00.000Z" }, "rating": null, "rejection": null, "transcription": { "id": "trans-uuid", "status": "approved", "selectedVersion": "claude" } } ] }, "meta": { "requestId": "req_abc123def456", "timestamp": "2026-03-02T12:00:00.000Z", "hasMore": false }}When present, rating and rejection have these structures:
- rating:
{ "score": 5, "comment": "Great feedback!" }—scoreis an integer,commentis a string or null - rejection:
{ "reason": "Video was too short", "createdAt": "2026-02-16T10:00:00.000Z" }
Get Slot Details
Section titled “Get Slot Details”GET /api/v1/playtests/slots/:id
Returns full details for a specific slot including game info, submission, rating, rejection, and transcription.
Response
Section titled “Response”{ "data": { "slot": { "id": "slot-uuid", "requestId": "pt-uuid", "status": "submitted", "deadlineAt": "2026-02-16T14:00:00.000Z", "createdAt": "2026-02-15T14:00:00.000Z", "game": { "id": "game-uuid", "name": "Dungeon Crawlers" }, "playtest": { "id": "pt-uuid", "visibility": "private", "durationMinutes": 60, "playerCount": 1 }, "playtester": { "id": "playtester-uuid", "displayName": "TestGamer42" }, "submission": { "id": "sub-uuid", "videoFileSize": 524288000, "notes": "Found a bug in level 3", "createdAt": "2026-02-15T18:30:00.000Z" }, "rating": null, "rejection": null, "transcription": null } }, "meta": { "requestId": "req_abc123def456", "timestamp": "2026-03-02T12:00:00.000Z" }}The transcription object in slot detail responses includes an additional keyTakeaways field compared to the list view:
"transcription": { "id": "trans-uuid", "status": "approved", "selectedVersion": "claude", "keyTakeaways": ["Bug found in level 3", "Tutorial was confusing"]}Accept Submission
Section titled “Accept Submission”POST /api/v1/playtests/slots/:id/accept
Accept a submitted playtest. The playtester earns their payout.
No request body required.
Response
Section titled “Response”{ "data": { "message": "Submission accepted" }, "meta": { "requestId": "req_abc123def456", "timestamp": "2026-03-02T12:00:00.000Z" }}Reject Submission
Section titled “Reject Submission”POST /api/v1/playtests/slots/:id/reject
Reject a submitted playtest with an optional reason.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
reason | string | No | Explanation for the rejection (max 5000 chars) |
allowRetry | boolean | No | Allow the playtester to resubmit (default: false) |
Response
Section titled “Response”{ "data": { "message": "Submission rejected" }, "meta": { "requestId": "req_abc123def456", "timestamp": "2026-03-02T12:00:00.000Z" }}When allowRetry is true, a new slot is created in reserved status for the same playtester with a fresh 24-hour deadline. The response message will be "Submission rejected - playtester can try again".
Get Download URL
Section titled “Get Download URL”GET /api/v1/playtests/slots/:id/download-url
Returns a pre-signed URL to download the playtest video. URL expires in 1 hour.
Response
Section titled “Response”{ "data": { "downloadUrl": "https://storage.example.com/videos/...", "expiresAt": "2026-03-02T13:00:00.000Z" }, "meta": { "requestId": "req_abc123def456", "timestamp": "2026-03-02T12:00:00.000Z" }}Get Transcript
Section titled “Get Transcript”GET /api/v1/playtests/slots/:id/transcript
Returns the AI-generated transcript for a submission. Returns a pre-signed download URL for the transcript file.
Response
Section titled “Response”{ "data": { "downloadUrl": "https://storage.example.com/transcripts/...", "filename": "transcript-claude.txt", "version": "claude", "keyTakeaways": ["Bug found in level 3", "Tutorial was confusing"] }, "meta": { "requestId": "req_abc123def456", "timestamp": "2026-03-02T12:00:00.000Z" }}Returns 404 if the transcription is not yet available or still processing.
List All Submissions
Section titled “List All Submissions”GET /api/v1/playtests/submissions
List submissions across all your games (cursor-paginated). Defaults to showing submitted status.
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 20 | Max 100 |
cursor | string | — | Pagination cursor |
status | string | submitted | Filter by slot status: open, reserved, submitted, accepted, rejected, blocked, expired, cancelled, publishing |
Response
Section titled “Response”{ "data": { "submissions": [ { "slot": { "id": "slot-uuid", "requestId": "pt-uuid", "status": "submitted", "reservedBy": "playtester-uuid", "deadlineAt": "2026-02-16T14:00:00.000Z", "createdAt": "2026-02-15T14:00:00.000Z" }, "submission": { "id": "sub-uuid", "videoStorageKey": "submissions/...", "videoFileSize": 524288000, "notes": "Found a bug in level 3", "createdAt": "2026-02-15T18:30:00.000Z" }, "game": { "id": "game-uuid", "name": "Dungeon Crawlers" }, "playtest": { "id": "pt-uuid", "visibility": "private", "durationMinutes": 60, "playerCount": 1, "isFreePublicTrial": false }, "playtester": { "id": "playtester-uuid", "displayName": "TestGamer42" }, "awaitingAdminApproval": false, "blockedReport": null, "transcription": null } ] }, "meta": { "requestId": "req_abc123def456", "timestamp": "2026-03-02T12:00:00.000Z", "hasMore": false }}Dashboard Stats
Section titled “Dashboard Stats”GET /api/v1/playtests/dashboard/stats
Get summary statistics for the game owner dashboard.
Response
Section titled “Response”{ "data": { "totalGames": 3, "activePlaytests": 5, "pendingReviews": 2 }, "meta": { "requestId": "req_abc123def456", "timestamp": "2026-03-02T12:00:00.000Z" }}