Appearance
Error Codes
All FAST API error responses use a consistent format with machine-readable error codes, human-readable messages, and a correlation ID for tracing.
Response Format
json
{
"error": "error_code",
"message": "Human-readable description",
"correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}HTTP Status Codes
| Status | Error Code | Description |
|---|---|---|
| 400 | validation_error | Request body is missing required fields or contains invalid values. |
| 400 | missing_idempotency_key | Withdrawal or stake refund sent without the required X-Idempotency-Key header. |
| 401 | unauthorized | API key is missing, invalid, or expired. JWT token is missing or expired. |
| 402 | purchase_declined, withdrawal_declined, cancel_declined | Your approval URL declined the action. |
| 403 | approval_required | A widget-initiated purchase, withdrawal, refund or cancel on a production tenant with no approval URL configured. |
| 404 | not_found | Requested resource does not exist (user, challenge, funded account). |
| 409 | Specific code | Business rule conflict, e.g. active_challenge_exists, tier_disabled. See below. |
| 422 | mapping_error | Adapter mapping failed to translate the incoming payload. Check mapping configuration. |
| 429 | rate_limited | Request rate exceeded a limit (endpoint category, or 30 bet events per user per minute). |
| 429 | purchase_cooldown | Purchase attempted before midnight UTC after a failed or cancelled challenge. |
| 500 | internal_error | Unexpected server error. Include the correlationId when contacting support. |
| 503 | maintenance_mode | FAST is in maintenance mode. Write operations are blocked. Read operations continue. |
| 503 | service_unavailable | Idempotency store unavailable on a withdrawal or stake refund. Retry with the same key. |
Error Code Strings
| Error Code | HTTP Status | When It Occurs |
|---|---|---|
validation_error | 400 | Missing userId, invalid challengeTierId, malformed JSON, invalid date format. |
unauthorized | 401 | No X-API-Key header, invalid key hash, expired JWT, missing X-Super-Admin-Key. |
missing_idempotency_key | 400 | POST /v1/users/:userId/withdraw, /challenge/refund-stake or /funded/refund-stake without X-Idempotency-Key. |
insufficient_withdrawable_balance | 400 | Withdrawal grossAmount exceeds what can be taken now (payout.withdrawableGross). |
invalid_amount | 400 | Withdrawal amount that rounds to 0 cents. |
below_min_payout | 400 | Withdrawal share below the tier's fundedMinPayout. |
payout_interval | 400 | Withdrawal before the tier's gap after the previous executed payout has passed. |
max_payouts_reached | 400 | Withdrawal on an account that has used every payout it allows. |
first_payout_deadline | 400 | First withdrawal after the tier's fundedFirstPayoutDays deadline. The account is ended shortly after with funded_account.revoked. |
no_profit | 400 | Withdrawal with the virtual balance not above the funded amount. |
open_bets | 400 / 409 | The account's last payout before every open bet has settled (400 on the request, 409 on confirm). |
payout_pending | 400 | Withdrawal while another request waits for the operator's decision. |
payout_amount_changed | 409 | Confirming a last payout whose amount changed since the request; it has been updated, confirm again. |
confirmation_required | 400 | Stake refund or cancellation sent without "confirm": true. |
approval_required | 403 | Widget money action on a production tenant with no approval URL. |
not_found | 404 | User has no active challenge or funded account. Requested resource ID does not exist. |
no_pending_choice | 404 | Continue or refund-stake when no challenge is awaiting the choice. |
active_challenge_exists | 409 | POST /v1/challenges/purchase when the user already has an active challenge. |
challenge_awaiting_choice | 409 | Purchase while a passed challenge awaits continue/refund. |
active_funded_account_exists | 409 | Purchase while the user has an active funded account. |
tier_not_found / tier_disabled | 409 | Purchase of a missing or disabled tier. |
idempotency_conflict | 409 | An X-Idempotency-Key reused with a different body. |
purchase_cooldown | 429 | Purchase before midnight UTC after a failed or cancelled challenge. Response includes cooldownUntil. |
mapping_error | 422 | Adapter mapping cannot extract required fields from the bet payload. |
rate_limited | 429 | Per-minute request limit exceeded for the endpoint category, or more than 30 bet events for one user in a minute. |
internal_error | 500 | Unhandled exception in the API. Database connection failure. Queue processing error. |
maintenance_mode | 503 | Super admin activated maintenance mode. All write endpoints return this until maintenance is lifted. |
Rate Limit Error
The endpoint-category 429 response includes additional fields (the per-user bet limit returns only error, message and correlationId, with no Retry-After):
json
{
"error": "rate_limited",
"message": "Too many requests. Limit: 500/min for this endpoint.",
"retryAfter": 42,
"correlationId": "uuid"
}Headers:
Retry-After: 42
X-RateLimit-Limit: 500
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1706000042Correlation ID
Every response includes a correlationId. This ID is consistent across the request lifecycle - it appears in FAST's server logs, webhook deliveries, and error responses. Always log it for debugging and include it in support requests.