Appearance
Challenges
A challenge is the core product. A user pays their own stake (real money) and receives a conditional, non-withdrawable operator bonus; together they form the program balance they bet with. The user must grow it to a target balance within a time limit while following strict betting rules. On passing the challenge the user chooses to continue into a Reward Account or to exit and refund only their own stake (which voids the bonus).
Challenge tiers
Each tenant configures one or more challenge tiers. Tiers define the economics of the product.
| Field | Example | Description |
|---|---|---|
ownStake | 100 EUR | The user's own stake (real money) - part of the program balance, refundable per the rules |
bonusAmount | 900 EUR | Conditional operator bonus (non-withdrawable). Program balance = ownStake + bonusAmount |
targetBalance | 2,000 EUR | Balance needed to pass the challenge |
durationDays | 20 | Calendar days before expiry |
fundedAccountBalance | 5,000 EUR | Reward Account balance if the user continues |
challengeRulesetId | UUID | Rules governing the challenge phase |
fundedRulesetId | UUID | Rules governing the Reward Account phase |
profitSplitPercent | 80 | User keeps 80% of Reward Account profits |
fundedStakeRefundable | false | If true, the own stake can also be refunded during the Reward Account phase |
fundedTotalPayoutCap | null | Optional lifetime payout cap per Reward Account |
minBetsRequired | 10 | Minimum bets before the challenge can pass |
minActiveDays | 5 | Minimum active betting days required |
Manage tiers in the operator backoffice or via the Admin API:
bash
# List enabled tiers
curl -H "X-API-Key: YOUR_API_KEY" \
https://api.fundedfast.eu/v1/challenge-tiersChallenge lifecycle
States
| State | Description |
|---|---|
active | Challenge is in progress. User is betting. |
passed_pending_choice | Challenge passed. New bets are blocked; the user must choose to continue or refund. |
completed | User chose to continue; the Reward Account was created. |
refunded | User exited after passing; own stake refunded (by the operator), bonus voided. |
failed | Challenge ended without completion. User can re-purchase. |
There is no paused or cancelled state. Once purchased, a challenge runs until it passes, fails, or is refunded.
State transitions
| From | To | Trigger |
|---|---|---|
| (none) | active | POST /v1/challenges/purchase |
active | passed_pending_choice | Balance >= target AND min bets met AND min active days met (latched) |
active | failed | Duration expired OR drawdown exceeded OR insufficient active days at expiry |
passed_pending_choice | completed (+ Reward Account) | POST /v1/users/:id/challenge/continue |
passed_pending_choice | refunded | POST /v1/users/:id/challenge/refund-stake (requires { "confirm": true }) |
Reward Account live | Reward Account revoked | Drawdown breach during funded phase |
Refunds are irreversible
Exiting with a refund voids the entire bonus and cannot be undone. In the Reward Account phase it also forfeits any accrued profit above the funded base. Both refund endpoints require { "confirm": true } in the request body; without it the API returns 400 confirmation_required.
Completion criteria
A challenge completes when ALL of these are true simultaneously:
- Current balance >= target balance
- Total accepted bets >= minimum bets required (from ruleset)
- Active days >= minimum active days (from ruleset)
If the balance reaches the target but other requirements are not met, the challenge stays active. The user must continue betting until all criteria are satisfied or the duration expires.
bash
# Check progress toward completion
curl -H "X-API-Key: YOUR_API_KEY" \
https://api.fundedfast.eu/v1/users/player-42/progressResponse:
json
{
"type": "challenge",
"challengeId": "uuid",
"currentBalance": 87.50,
"targetBalance": 100.00,
"startingBalance": 50.00,
"daysRemaining": 12,
"activeDays": 4,
"minActiveDays": 5,
"totalBetsAccepted": 18,
"minBetsRequired": 10,
"status": "active"
}Failure modes
| Reason code | Trigger | Description |
|---|---|---|
expired | Duration ran out | Calendar days exceeded before all completion criteria were met |
max_drawdown_exceeded | Total loss limit breached | Cumulative losses exceeded the configured drawdown percentage |
insufficient_active_days | Expiry + not enough active days | Duration expired with target reached but active days requirement not met |
How expiry works
FAST runs a background check every 60 seconds. When a challenge's createdAt + durationDays passes, it is marked as failed with reason expired.
The check uses UTC calendar days. A 20-day challenge purchased at 23:59 UTC still gets the full 20 calendar days.
How drawdown works
Drawdown is calculated as:
totalDrawdown = startingBalance - lowestBalance
drawdownPercent = (totalDrawdown / startingBalance) * 100When drawdownPercent exceeds the configured limit, the challenge fails immediately with reason max_drawdown_exceeded.
Daily drawdown triggers a cooldown (temporary betting block) rather than immediate failure. Total drawdown triggers failure.
One active challenge per user
FAST enforces exactly one active challenge or Reward Account per user at any time. This is enforced with a database-level lock.
bash
# Attempting a second purchase returns 409
curl -X POST https://api.fundedfast.eu/v1/challenges/purchase \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"userId": "player-42", "challengeTierId": "tier-uuid"}'If the user already has an active challenge or Reward Account:
json
{
"error": "active_challenge_exists",
"message": "User already has an active challenge or funded account",
"correlationId": "uuid"
}After failure or Reward Account revocation, the user can immediately purchase a new challenge. No cooldown between attempts.
Active days
A day counts as "active" if the user placed at least one accepted bet on that calendar day (UTC).
Active days are a challenge condition only. They play no part in the Reward Account phase.
Cooldown periods
When a user hits the daily loss limit, FAST triggers a cooldown:
cooldown.triggeredwebhook fires- All bets are rejected with reason
cooldown_activeuntil the cooldown lifts - Cooldown lifts after the configured duration (hours) or at the next calendar day
cooldown.liftedwebhook fires
Cooldown is a protective mechanism. It prevents tilt-driven losses from cascading into total drawdown failure.
json
{
"eventType": "cooldown.triggered",
"userId": "player-42",
"data": {
"reason": "daily_drawdown",
"cooldownUntil": "2026-06-05T00:00:00Z",
"challengeId": "uuid"
}
}Purchase flow
bash
curl -X POST https://api.fundedfast.eu/v1/challenges/purchase \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"userId": "player-42",
"challengeTierId": "tier-uuid"
}'Response (201):
json
{
"challenge": {
"id": "challenge-uuid",
"userId": "player-42",
"status": "active",
"currentBalance": 50.00,
"targetBalance": 100.00,
"durationDays": 20,
"expiresAt": "2026-06-25T14:30:00Z",
"createdAt": "2026-06-05T14:30:00Z"
}
}FAST also fires the challenge.activated webhook.
Important: Collect payment on your side BEFORE calling this endpoint. FAST does not handle money. If payment fails, do not call purchase.
Webhooks for challenge events
| Event | When it fires |
|---|---|
challenge.activated | Challenge purchased and started |
challenge.completed | All completion criteria met |
challenge.failed | Challenge ended due to expiry, drawdown, or insufficient days |
cooldown.triggered | User hit daily loss limit |
cooldown.lifted | Cooldown period ended |
See Webhook Handling for signature verification and payload formats.
After completion: Reward Account
When a challenge completes, FAST auto-creates a Reward Account in the same database transaction:
- Balance starts at the configured funded amount (e.g., 5,000 EUR)
- User bets with this balance. Profits accumulate.
- Gross profit = virtual balance - funded amount, where the virtual balance is the ledger balance less the stake of open bets
- The user takes any part of the gross profit; their share is the profit split of it, and exactly that gross amount leaves the balance. The rest stays on the balance
- On the first withdrawal, the user's own stake is returned alongside the profit share (
ownStakeReturnedin the response and webhook). Subsequent payouts return only profit. - No minimum active days or bets in this phase
- Optional minimum payout, measured on the user's share
- Optional gap between payouts (none by default)
- Optional payout cap per payout and optional lifetime payout cap
- Optional max payouts limit: the last payout takes the whole gross profit, waits for open bets, and closes the account (
accountClosed: truein the webhook) - Mandatory payout threshold: at that balance FAST requests the whole gross profit on its own (150% of the funded amount by default on new tiers)
- First-payout deadline: the first payout must be requested within
fundedFirstPayoutDaysof the account opening (30 by default on new tiers), or the account ends - Max stake is measured on the lower of the starting and the current virtual balance, so a winning run does not raise it
- Revoked if total drawdown limit is breached, or after 14 days without an accepted bet or a payout
See Withdrawals for the payout flow.
Best practices
Show progress clearly. Display balance, target, days remaining, and active days in your UI. Use
GET /v1/users/:userId/progress.Handle cooldowns in the UI. When a user is in cooldown, disable bet placement and show when it lifts.
Map rejection codes to friendly messages. When
/bets/validatereturnsrejected, show the user why (odds too high, stake too large, etc.).Reconcile daily. Use the reconciliation endpoint to verify your bet counts match FAST's records.
Disclose rules upfront. Use
GET /v1/challenge-tiers/:id/termsto display rules before purchase.