Errors
All error responses follow a consistent format and include a machine-readable error code.
Error Response Format
Section titled “Error Response Format”{ "error": { "code": "VALIDATION_ERROR", "message": "Invalid email format", "details": [ { "field": "email", "message": "Must be a valid email address" } ] }, "meta": { "requestId": "req_abc123", "timestamp": "2026-03-02T12:00:00Z" }}The details array is optional and only present for validation errors, providing per-field error messages.
Error Codes
Section titled “Error Codes”| Code | HTTP Status | Description |
|---|---|---|
UNAUTHORIZED | 401 | Missing or invalid API key |
FORBIDDEN | 403 | Valid key but insufficient scope for this endpoint |
EMAIL_NOT_VERIFIED | 403 | Account email not verified — check inbox |
NOT_FOUND | 404 | Resource does not exist or you don’t have access |
VALIDATION_ERROR | 422 | Request body or parameters failed validation |
RATE_LIMITED | 429 | Rate limit exceeded — see Retry-After header |
CONFLICT | 409 | Duplicate resource or state conflict (e.g., slot already claimed) |
PAYMENT_REQUIRED | 402 | Insufficient credit balance |
IDEMPOTENCY_MISMATCH | 422 | Idempotency key reused with different request |
INTERNAL_ERROR | 500 | Unexpected server error |
SERVICE_UNAVAILABLE | 503 | Temporary outage — retry with backoff |
Handling Errors
Section titled “Handling Errors”Validation Errors (422)
Section titled “Validation Errors (422)”Inspect the details array for field-specific messages:
{ "error": { "code": "VALIDATION_ERROR", "message": "Request validation failed", "details": [ { "field": "name", "message": "Required" }, { "field": "tags", "message": "Must contain at least one tag" } ] }}Rate Limiting (429)
Section titled “Rate Limiting (429)”When rate limited, use the Retry-After header to determine when to retry:
HTTP/1.1 429 Too Many RequestsRetry-After: 30X-RateLimit-Limit: 100X-RateLimit-Remaining: 0X-RateLimit-Reset: 1709312400Wait the specified number of seconds before retrying. See Rate Limiting for details on tiers and limits.
Conflict (409)
Section titled “Conflict (409)”Returned when an operation conflicts with the current state:
{ "error": { "code": "CONFLICT", "message": "This playtest slot has already been claimed" }}Common causes: slot already reserved by another playtester, duplicate resource creation, or state transition conflict.
Payment Required (402)
Section titled “Payment Required (402)”Returned when a paid operation lacks sufficient credit:
{ "error": { "code": "PAYMENT_REQUIRED", "message": "Insufficient credit", "details": [ { "field": "balance", "message": "Current balance: $50.00, required: $100.00" } ] }}Add credit via POST /api/v1/billing/credit before retrying.
Server Errors (500, 503)
Section titled “Server Errors (500, 503)”Retry with exponential backoff. Include the requestId from the meta object when contacting support.
Request ID
Section titled “Request ID”Every response includes a requestId in the meta object. Use this when reporting issues — it lets us trace the exact request through our logs.
{ "meta": { "requestId": "req_abc123", "timestamp": "2026-03-02T12:00:00Z" }}