Skip to content

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.

FieldExampleDescription
ownStake100 EURThe user's own stake (real money) - part of the program balance, refundable per the rules
bonusAmount900 EURConditional operator bonus (non-withdrawable). Program balance = ownStake + bonusAmount
targetBalance2,000 EURBalance needed to pass the challenge
durationDays20Calendar days before expiry
fundedAccountBalance5,000 EURReward Account balance if the user continues
challengeRulesetIdUUIDRules governing the challenge phase
fundedRulesetIdUUIDRules governing the Reward Account phase
profitSplitPercent80User keeps 80% of Reward Account profits
fundedStakeRefundablefalseIf true, the own stake can also be refunded during the Reward Account phase
fundedTotalPayoutCapnullOptional lifetime payout cap per Reward Account
minBetsRequired10Minimum bets before the challenge can pass
minActiveDays5Minimum 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-tiers

Challenge lifecycle ​

States ​

StateDescription
activeChallenge is in progress. User is betting.
passed_pending_choiceChallenge passed. New bets are blocked; the user must choose to continue or refund.
completedUser chose to continue; the Reward Account was created.
refundedUser exited after passing; own stake refunded (by the operator), bonus voided.
failedChallenge 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 ​

FromToTrigger
(none)activePOST /v1/challenges/purchase
activepassed_pending_choiceBalance >= target AND min bets met AND min active days met (latched)
activefailedDuration expired OR drawdown exceeded OR insufficient active days at expiry
passed_pending_choicecompleted (+ Reward Account)POST /v1/users/:id/challenge/continue
passed_pending_choicerefundedPOST /v1/users/:id/challenge/refund-stake (requires { "confirm": true })
Reward Account liveReward Account revokedDrawdown 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:

  1. Current balance >= target balance
  2. Total accepted bets >= minimum bets required (from ruleset)
  3. 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/progress

Response:

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 codeTriggerDescription
expiredDuration ran outCalendar days exceeded before all completion criteria were met
max_drawdown_exceededTotal loss limit breachedCumulative losses exceeded the configured drawdown percentage
insufficient_active_daysExpiry + not enough active daysDuration 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) * 100

When 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:

  1. cooldown.triggered webhook fires
  2. All bets are rejected with reason cooldown_active until the cooldown lifts
  3. Cooldown lifts after the configured duration (hours) or at the next calendar day
  4. cooldown.lifted webhook 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 ​

EventWhen it fires
challenge.activatedChallenge purchased and started
challenge.completedAll completion criteria met
challenge.failedChallenge ended due to expiry, drawdown, or insufficient days
cooldown.triggeredUser hit daily loss limit
cooldown.liftedCooldown 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 (ownStakeReturned in 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: true in 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 fundedFirstPayoutDays of 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 ​

  1. Show progress clearly. Display balance, target, days remaining, and active days in your UI. Use GET /v1/users/:userId/progress.

  2. Handle cooldowns in the UI. When a user is in cooldown, disable bet placement and show when it lifts.

  3. Map rejection codes to friendly messages. When /bets/validate returns rejected, show the user why (odds too high, stake too large, etc.).

  4. Reconcile daily. Use the reconciliation endpoint to verify your bet counts match FAST's records.

  5. Disclose rules upfront. Use GET /v1/challenge-tiers/:id/terms to display rules before purchase.

BLACKLINE CONSULTING d.o.o.