Skip to content

Webhook Events ​

FAST dispatches webhooks for all state changes. Each webhook is signed with HMAC-SHA256 and delivered to the tenant's configured webhook URL.

Payload Structure ​

Every webhook follows this format:

json
{
  "eventId": "uuid",
  "eventType": "event.type",
  "userId": "user-id",
  "timestamp": "2026-01-15T10:00:00.000Z",
  "data": {}
}

Verification ​

Verify the X-Webhook-Signature header:

HMAC-SHA256(webhookSecret, requestBody) === X-Webhook-Signature

HMAC the raw body bytes as received, not a re-serialised object. Respond with a 2xx quickly; timeouts, the retry schedule and deduplication are covered in Webhook Handling.


Challenge Events ​

challenge.activated ​

A new challenge was purchased and is now active.

json
{
  "data": {
    "challengeId": "uuid",
    "challengeTierId": "uuid",
    "ownStake": 100.00,
    "bonusAmount": 900.00,
    "startingBalance": 1000.00,
    "targetBalance": 2000.00,
    "expiresAt": "2026-02-04T10:00:00.000Z"
  }
}

challenge.passed ​

User met all challenge conditions (target balance, minimum bets, minimum active days). The challenge now waits in passed_pending_choice for the user to continue or refund their own stake; see User Progress.

json
{
  "data": {
    "challengeId": "uuid",
    "programBalance": 2000.00,
    "refundableOwnStake": 100.00
  }
}

challenge.completed ​

The user chose to continue after passing. The Reward Account is created (a funded_account.activated follows).

json
{
  "data": {
    "challengeId": "uuid",
    "finalBalance": 2000.00
  }
}

challenge.failed ​

Challenge ended without completion.

json
{
  "data": {
    "challengeId": "uuid",
    "reason": "expired"
  }
}

Possible reason values: expired, max_drawdown_exceeded, insufficient_active_days.

challenge.cancelled ​

The user cancelled an active (or passed, not yet chosen) challenge. No refund is issued: the own stake is forfeited and the bonus voided.

json
{
  "data": {
    "challengeId": "uuid",
    "ownStakeForfeited": 100.00,
    "bonusVoided": 900.00,
    "finalBalance": 870.00
  }
}

challenge.stake_refunded ​

The user exited after passing instead of continuing. Refund ownStake to the user.

json
{
  "data": {
    "challengeId": "uuid",
    "ownStake": 100.00
  }
}

challenge.bonus_voided ​

Sent alongside a stake refund (challenge or funded phase): the bonus is void.

json
{
  "data": {
    "challengeId": "uuid",
    "bonusAmount": 900.00
  }
}

challenge.expiring_soon ​

Challenge expires within 24 hours. Sent once per challenge.

json
{
  "data": {
    "challengeId": "uuid",
    "hoursRemaining": 18,
    "currentBalance": 85.00,
    "targetBalance": 100.00,
    "betsPlaced": 30,
    "activeDays": 8
  }
}

Funded Account Events ​

funded_account.activated ​

Funded account created after the user chooses to continue. rewardsApplied is present only when rewards were consumed at creation.

json
{
  "data": {
    "fundedAccountId": "uuid",
    "fundedAmount": 5000.00,
    "challengeId": "uuid",
    "profitSplitPercent": 80,
    "payoutCapPercent": null,
    "rewardsApplied": []
  }
}

funded_account.payout_requested ​

A payout request is waiting for the operator's decision (POST /v1/admin/payout-requests/:id/confirm or /reject). The account takes no new bets until it is decided. amount is the user's share, grossAmount what will leave the balance, operatorShare the difference. isMandatory is true when the tier's mandatory payout threshold created the request on its own; isFinal when it is the account's last allowed payout.

json
{
  "data": {
    "fundedAccountId": "uuid",
    "payoutRequestId": "uuid",
    "amount": 400.00,
    "grossAmount": 500.00,
    "operatorShare": 100.00,
    "isFinal": false,
    "isMandatory": false
  }
}

funded_account.payout_rejected ​

A pending request was rejected, by the operator or because the account closed (reason: "account_closed"). Nothing on the account changed.

json
{
  "data": {
    "fundedAccountId": "uuid",
    "payoutRequestId": "uuid",
    "amount": 400.00,
    "reason": "optional text"
  }
}

funded_account.payout ​

Payout executed from a funded account. grossAmount left the balance; userPayout is the user's profit split of it and operatorShare the rest. On the first payout, the user's own stake is returned alongside the profit share (ownStakeReturned > 0). Subsequent payouts set it to 0. rewardBonus is what the user's payout_bonus_percent rewards add on top (0 if none); pay totalPayout = userPayout + ownStakeReturned + rewardBonus. All figures are before any tax, which is the operator's matter. When accountClosed is true, this was the account's last allowed payout and the account is closed. isMandatory is true for a payout created by the mandatory payout threshold.

json
{
  "data": {
    "fundedAccountId": "uuid",
    "payoutRequestId": "uuid",
    "userPayout": 400.00,
    "grossAmount": 500.00,
    "ownStakeReturned": 100.00,
    "rewardBonus": 0,
    "totalPayout": 500.00,
    "operatorShare": 100.00,
    "newBalance": 5350.00,
    "forfeitedProfit": 0,
    "accountClosed": false,
    "isMandatory": false
  }
}

forfeitedProfit is the share a cap kept from the last payout, which can no longer be paid once the account ends; otherwise 0. A payout takes only its gross amount off the balance and the rest stays on it.

funded_account.revoked ​

Funded account closed by FAST, by the user, or by an admin.

json
{
  "data": {
    "fundedAccountId": "uuid",
    "reason": "funded_drawdown_exceeded",
    "finalBalance": 4400.00
  }
}

reason is funded_drawdown_exceeded, funded_inactivity (14 days without an accepted bet or a payout), first_payout_deadline (no payout requested within the tier's fundedFirstPayoutDays), voluntary_cancellation, or, for an admin revocation, the reason the admin gave (default manual_admin_revoke). No profit is held apart from the balance, so whatever was on it ends with the account, as finalBalance. A payout request still pending is rejected first, with a funded_account.payout_rejected.

funded_account.stake_refunded ​

Own stake refunded during the funded phase (tiers with fundedStakeRefundable). Refund ownStake to the user. A challenge.bonus_voided is sent with it.

json
{
  "data": {
    "fundedAccountId": "uuid",
    "ownStake": 100.00
  }
}

Cooldown Events ​

cooldown.triggered ​

User hit the daily loss limit and entered a cooldown period.

json
{
  "data": {
    "challengeId": "uuid",
    "fundedAccountId": null,
    "reason": "daily_drawdown_exceeded"
  }
}

cooldown.lifted ​

Cooldown period ended. User can place bets again. Exactly one of challengeId and fundedAccountId is set.

json
{
  "data": {
    "challengeId": "uuid",
    "fundedAccountId": null,
    "reason": "cooldown_duration_expired"
  }
}

Bet Events ​

bet.validation_result ​

Sent for every placed bet with its validation outcome.

json
{
  "data": {
    "userId": "user-001",
    "betId": "BET-12345",
    "status": "rejected",
    "reasonCodes": ["max_stake_exceeded"]
  }
}

betId is your externalBetId. status is accepted or rejected; see Rejection Codes.


Wagering Bonus Events ​

bonus.activated ​

A wagering bonus was activated for a user.

json
{
  "data": {
    "bonusId": "uuid",
    "offerId": "uuid",
    "offerName": "Weekend Reload",
    "bonusAmount": 25.00,
    "wageringTarget": 500.00,
    "expiresAt": "2026-02-15T00:00:00.000Z"
  }
}

bonus.progress ​

Wagering progress updated (sent when a milestone is crossed, not every bet).

json
{
  "data": {
    "bonusId": "uuid",
    "wageredSoFar": 250.00,
    "wageringTarget": 500.00,
    "progress": 0.5,
    "milestone": 50
  }
}

bonus.completed ​

User fulfilled wagering requirements. Bonus reward released.

json
{
  "data": {
    "bonusId": "uuid",
    "bonusAmount": 25.00,
    "totalWagered": 510.00,
    "completedAt": "2026-02-10T14:30:00.000Z"
  }
}

bonus.forfeited ​

Bonus was forfeited.

json
{
  "data": {
    "bonusId": "uuid",
    "reason": "withdrawal"
  }
}

Currently sent with reason: "withdrawal", when a withdrawal forfeits the user's active bonuses.

bonus.expired ​

Bonus expired before completion.

json
{
  "data": {
    "bonusId": "uuid",
    "wageredSoFar": 300.00,
    "wageringTarget": 500.00,
    "progress": 0.6
  }
}

Mission Events ​

mission.enrolled ​

User enrolled in a mission (auto-enroll or manual join).

json
{
  "data": {
    "missionId": "uuid",
    "missionName": "Weekend Warrior",
    "target": 10,
    "endsAt": "2026-01-22T00:00:00.000Z"
  }
}

mission.progress ​

Mission crossed 50% of its target (sent once per mission).

json
{
  "data": {
    "missionId": "uuid",
    "missionName": "Weekend Warrior",
    "progress": 5,
    "target": 10,
    "percent": 50
  }
}

mission.completed ​

User completed a mission.

json
{
  "data": {
    "missionId": "uuid",
    "missionName": "Weekend Warrior",
    "rewardType": "balance_bonus",
    "rewardValue": 10
  }
}

mission.expired ​

Mission expired before completion.

json
{
  "data": {
    "missionId": "uuid",
    "missionName": "Weekend Warrior"
  }
}

Tournament Events ​

tournament.started ​

A tournament transitioned from upcoming to active. Tournament-wide events (started, settled, cancelled) carry an empty top-level userId.

json
{
  "data": {
    "tournamentId": "uuid",
    "name": "Weekly High Roller"
  }
}

tournament.user_joined ​

A user joined a tournament.

json
{
  "data": {
    "tournamentId": "uuid",
    "tournamentName": "Weekly High Roller"
  }
}

tournament.user_invited ​

A user was invited to a tournament (invite-mode).

json
{
  "data": {
    "tournamentId": "uuid",
    "tournamentName": "Weekly High Roller",
    "userId": "user-001"
  }
}

tournament.settled ​

Tournament ended and prizes distributed.

json
{
  "data": {
    "tournamentId": "uuid",
    "name": "Weekly High Roller",
    "totalEntries": 128
  }
}

tournament.cancelled ​

Tournament was cancelled by admin.

json
{
  "data": {
    "tournamentId": "uuid"
  }
}

Loyalty Events ​

loyalty.tier_upgraded ​

User promoted to a higher loyalty tier.

json
{
  "data": {
    "newTier": "Gold",
    "newTierLevel": 3,
    "currentPoints": 5200
  }
}

loyalty.tier_downgraded ​

User demoted to a lower tier (after grace period).

json
{
  "data": {
    "newTier": "Silver",
    "currentPoints": 800
  }
}

loyalty.cashback_settled ​

Accrued cashback was settled (zeroed) and credited to user.

json
{
  "data": {
    "amount": 15.50
  }
}

Achievement and Reward Events ​

achievement.earned ​

User earned an achievement. When earned automatically:

json
{
  "data": {
    "achievementCode": "first_win",
    "achievementName": "First Win",
    "triggerEvent": "challenge_completed"
  }
}

When granted by an admin, data carries achievementId instead.

reward.granted ​

A reward was granted to the user (from achievement, rule, or manual grant).

json
{
  "data": {
    "rewardId": "uuid",
    "rewardType": "balance_bonus",
    "rewardValue": 25,
    "applyMode": "auto",
    "expiresAt": "2026-02-15T00:00:00.000Z",
    "achievementId": "uuid"
  }
}

reward.applied ​

A reward was consumed during purchase, at funded-account creation, or applied to an active account. When applied to an active account:

json
{
  "data": {
    "rewardId": "uuid",
    "rewardType": "balance_boost_percent",
    "rewardValue": 10,
    "appliedTo": "challenge-uuid",
    "effect": "+10% balance boost: +5.00 EUR on challenge"
  }
}

When consumed at purchase or funded-account creation, data has appliedTo, context (challenge_purchase, funded_account_creation or payout), a rewards array of { rewardId, rewardType, rewardValue, effect }, and the adjusted terms (adjustedStartingBalance, purchaseDiscount, adjustedDurationDays, or adjustedProfitSplit, adjustedPayoutCap, or for payout the payoutBonus in EUR, with appliedTo the payout request id).

reward.expired ​

A pending reward expired before being claimed or consumed.

json
{
  "data": {
    "rewardId": "uuid",
    "rewardType": "purchase_discount_percent"
  }
}

Self-Exclusion Events ​

user.self_excluded ​

User self-excluded from the platform. excludedBy is the admin's email, or api-key when recorded with a server key.

json
{
  "data": {
    "reason": "self_exclusion",
    "excludedBy": "compliance@operator.com",
    "expiresAt": "2026-07-15T00:00:00.000Z"
  }
}

user.self_exclusion_lifted ​

Self-exclusion period ended.

json
{
  "data": {
    "reinstatedBy": "compliance@operator.com"
  }
}

Event Type Reference ​

Event TypeCategoryDescription
challenge.activatedChallengeNew challenge purchased
challenge.passedChallengeChallenge passed; user must choose continue or refund
challenge.completedChallengeUser continued; challenge completed and reward account created
challenge.failedChallengeChallenge ended without completion
challenge.cancelledChallengeUser cancelled; no refund
challenge.expiring_soonChallengeExpires within 24h
challenge.stake_refundedChallengeUser exited after passing; own stake to be refunded by the operator
challenge.bonus_voidedChallengeBonus voided (on refund)
funded_account.activatedFundedAccount created after the user continues
funded_account.payout_requestedFundedPayout request waiting for the operator's decision
funded_account.payout_rejectedFundedPending payout request rejected
funded_account.payoutFundedPayout executed
funded_account.revokedFundedAccount ended (drawdown, inactivity, first-payout deadline, cancellation, admin)
funded_account.stake_refundedFundedOwn stake refunded during the reward account phase
cooldown.triggeredCooldownDaily loss limit hit
cooldown.liftedCooldownCooldown period ended
bet.validation_resultBetPlaced bet accepted or rejected
bonus.activatedBonusWagering bonus started
bonus.progressBonusWagering progress updated
bonus.completedBonusWagering requirements met
bonus.forfeitedBonusBonus cancelled/forfeited
bonus.expiredBonusBonus expired
mission.enrolledMissionUser enrolled in mission
mission.progressMissionMission passed 50% of target
mission.completedMissionMission completed
mission.expiredMissionMission expired
tournament.startedTournamentTournament went active
tournament.user_joinedTournamentUser joined tournament
tournament.user_invitedTournamentUser invited (invite-mode)
tournament.settledTournamentPrizes distributed
tournament.cancelledTournamentTournament cancelled
loyalty.tier_upgradedLoyaltyUser promoted
loyalty.tier_downgradedLoyaltyUser demoted
loyalty.cashback_settledLoyaltyCashback credited
achievement.earnedAchievementAchievement unlocked
reward.grantedRewardReward given to user
reward.appliedRewardReward consumed/applied
reward.expiredRewardReward expired unclaimed
user.self_excludedSelf-ExclusionUser self-excluded
user.self_exclusion_liftedSelf-ExclusionExclusion lifted

BLACKLINE CONSULTING d.o.o.