Skip to content

Playtests Endpoints

These endpoints are for game owners managing playtests for their games. All require the game_owner scope.

GET /api/v1/games/:gameId/playtests

ParameterTypeDefaultDescription
limitinteger20Max 100
cursorstringPagination cursor
{
"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
}
}

POST /api/v1/games/:gameId/playtests

Order playtests for a game.

FieldTypeRequiredDescription
visibilitystringNopublic or private (default: private)
quantityintegerNoNumber of slots to create, 1-100 (default: 1)
durationMinutesintegerNo30, 60, 120, or 180 (default: 60)
playerCountintegerNoPlayers per session, 1-8 (default: 1)
notesForTestersstringNoInstructions for playtesters (max 5000 chars)
keysForTestersstring[]NoGame keys to distribute to individual slots
isFreePublicTrialbooleanNoUse free public trial (one per game, singleplayer 30min only)
targetingTypestringNonew (new playtesters only) or past (returning playtesters)
pastPlaytesterScopestringNoRequired when targetingType is past: any or specific
targetedPlaytesterIdstringNoRequired when pastPlaytesterScope is specific
Terminal window
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"
}'

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"
}
}

Free trials are immediately active and require admin approval:

{
"data": {
"playtest": { "..." },
"slots": [{ "id": "slot-uuid", "status": "open", "..." }],
"needsAdminApproval": true
},
"meta": { "..." }
}

GET /api/v1/playtests/:id

Returns details for a specific playtest request with slot statistics.

{
"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"
}
}

PATCH /api/v1/playtests/:id

Update an active playtest request.

FieldTypeDescription
notesForTestersstringUpdated instructions for playtesters (max 5000 chars)

Returns the updated playtest request object.


GET /api/v1/playtests/:id/slots

Returns all slots for a playtest request with their current status (cursor-paginated).

ParameterTypeDefaultDescription
limitinteger20Max 100
cursorstringPagination cursor
{
"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!" }score is an integer, comment is a string or null
  • rejection: { "reason": "Video was too short", "createdAt": "2026-02-16T10:00:00.000Z" }

GET /api/v1/playtests/slots/:id

Returns full details for a specific slot including game info, submission, rating, rejection, and transcription.

{
"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"]
}

POST /api/v1/playtests/slots/:id/accept

Accept a submitted playtest. The playtester earns their payout.

No request body required.

{
"data": {
"message": "Submission accepted"
},
"meta": {
"requestId": "req_abc123def456",
"timestamp": "2026-03-02T12:00:00.000Z"
}
}

POST /api/v1/playtests/slots/:id/reject

Reject a submitted playtest with an optional reason.

FieldTypeRequiredDescription
reasonstringNoExplanation for the rejection (max 5000 chars)
allowRetrybooleanNoAllow the playtester to resubmit (default: false)
{
"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 /api/v1/playtests/slots/:id/download-url

Returns a pre-signed URL to download the playtest video. URL expires in 1 hour.

{
"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 /api/v1/playtests/slots/:id/transcript

Returns the AI-generated transcript for a submission. Returns a pre-signed download URL for the transcript file.

{
"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.


GET /api/v1/playtests/submissions

List submissions across all your games (cursor-paginated). Defaults to showing submitted status.

ParameterTypeDefaultDescription
limitinteger20Max 100
cursorstringPagination cursor
statusstringsubmittedFilter by slot status: open, reserved, submitted, accepted, rejected, blocked, expired, cancelled, publishing
{
"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
}
}

GET /api/v1/playtests/dashboard/stats

Get summary statistics for the game owner dashboard.

{
"data": {
"totalGames": 3,
"activePlaytests": 5,
"pendingReviews": 2
},
"meta": {
"requestId": "req_abc123def456",
"timestamp": "2026-03-02T12:00:00.000Z"
}
}