@fundedfast/sdk 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,9 +21,17 @@ export type WebhookDeliveryStatus = 'pending' | 'delivered' | 'failed';
21
21
  /** Tenant integration mode */
22
22
  export type IntegrationMode = 'sync' | 'async';
23
23
  /** All possible rejection reason codes from the rules engine */
24
- export type RejectionCode = 'min_stake_not_met' | 'max_stake_exceeded' | 'odds_limit_exceeded' | 'min_odds_not_met' | 'max_payout_exceeded' | 'max_selections_exceeded' | 'max_win_exceeded' | 'lucky_bet_profit_limit' | 'daily_drawdown_exceeded' | 'max_drawdown_exceeded' | 'cooldown_active' | 'cashout_not_allowed_during_challenge';
24
+ export type RejectionCode = 'min_stake_not_met' | 'max_stake_exceeded' | 'odds_limit_exceeded' | 'min_odds_not_met' | 'max_payout_exceeded' | 'max_selections_exceeded' | 'max_win_exceeded' | 'lucky_bet_profit_limit' | 'daily_drawdown_exceeded' | 'max_drawdown_exceeded' | 'cooldown_active' | 'payout_pending' | 'cashout_not_allowed_during_challenge'
25
+ /** Settlements only. A won/lost after a different result for the same bet: held for an operator, no balance change. */
26
+ | 'conflicting_settlement'
27
+ /** Settlements only. Stake or odds differ from the accepted placement, or the payout exceeds stake x odds: held, no balance change. */
28
+ | 'settlement_mismatch'
29
+ /** Settlements only. FAST never accepted this bet (rejected, or placed with no programme). Recorded, no balance change. */
30
+ | 'no_accepted_placement'
31
+ /** Settlements only. Accepted on a programme that has closed since, typically a challenge that passed with the bet still open. Recorded under that programme, no balance change. */
32
+ | 'program_closed';
25
33
  /** Webhook event types emitted by FAST */
26
- export type WebhookEventType = 'challenge.activated' | 'challenge.passed' | 'challenge.completed' | 'challenge.failed' | 'challenge.cancelled' | 'challenge.expiring_soon' | 'challenge.stake_refunded' | 'challenge.bonus_voided' | 'funded_account.activated' | 'funded_account.revoked' | 'funded_account.payout' | 'funded_account.stake_refunded' | 'cooldown.triggered' | 'cooldown.lifted' | 'bet.validation_result' | 'achievement.earned' | 'reward.granted' | 'reward.applied' | 'user.self_excluded' | 'user.self_exclusion_lifted' | 'bonus.activated' | 'bonus.progress' | 'bonus.completed' | 'bonus.expired' | 'bonus.forfeited' | 'mission.enrolled' | 'mission.progress' | 'mission.completed' | 'mission.expired' | 'tournament.started' | 'tournament.user_joined' | 'tournament.user_invited' | 'tournament.settled' | 'tournament.cancelled' | 'loyalty.tier_upgraded' | 'loyalty.tier_downgraded' | 'loyalty.cashback_settled' | 'reward.expired';
34
+ export type WebhookEventType = 'challenge.activated' | 'challenge.passed' | 'challenge.completed' | 'challenge.failed' | 'challenge.cancelled' | 'challenge.expiring_soon' | 'challenge.stake_refunded' | 'challenge.bonus_voided' | 'funded_account.activated' | 'funded_account.revoked' | 'funded_account.payout' | 'funded_account.stake_refunded' | 'funded_account.payout_requested' | 'funded_account.payout_rejected' | 'cooldown.triggered' | 'cooldown.lifted' | 'bet.validation_result' | 'achievement.earned' | 'reward.granted' | 'reward.applied' | 'user.self_excluded' | 'user.self_exclusion_lifted' | 'bonus.activated' | 'bonus.progress' | 'bonus.completed' | 'bonus.expired' | 'bonus.forfeited' | 'mission.enrolled' | 'mission.progress' | 'mission.completed' | 'mission.expired' | 'tournament.started' | 'tournament.user_joined' | 'tournament.user_invited' | 'tournament.settled' | 'tournament.cancelled' | 'loyalty.tier_upgraded' | 'loyalty.tier_downgraded' | 'loyalty.cashback_settled' | 'reward.expired';
27
35
  /** Body for POST /challenges/purchase */
28
36
  export interface PurchaseRequest {
29
37
  /** Your sportsbook user identifier */
@@ -60,8 +68,91 @@ export interface BetEvent {
60
68
  }
61
69
  /** Body for POST /users/:userId/withdraw */
62
70
  export interface WithdrawRequest {
63
- /** Amount to withdraw. Must be <= withdrawable profit. */
71
+ /** Amount to pay out. Must be <= withdrawable and >= the minimum payout. Ignored on the last allowed payout, which is always the full available amount. */
72
+ amount?: number;
73
+ /** Leave the request pending for a later confirm/reject instead of executing it now. */
74
+ deferred?: boolean;
75
+ }
76
+ /** Why a payout is not possible yet. Every reason that applies is listed. */
77
+ export interface PayoutReason {
78
+ code: 'account_not_active' | 'payout_pending' | 'max_payouts_reached' | 'no_profit' | 'insufficient_active_days' | 'insufficient_bets' | 'below_min_payout' | 'payout_interval' | 'open_bets';
79
+ /** False when the reason only keeps the cycle share out and unpaid profit can still be requested. */
80
+ blocking: boolean;
81
+ current?: number;
82
+ required?: number;
83
+ missing?: number;
84
+ until?: string;
85
+ }
86
+ export interface PendingPayout {
87
+ id: string;
88
+ amount: number;
89
+ fromUnpaid: number;
90
+ fromCycle: number;
91
+ isFinal: boolean;
92
+ isMandatory: boolean;
93
+ createdAt: string;
94
+ }
95
+ /** Payout state of a funded account (the per-cycle model). */
96
+ export interface PayoutState {
97
+ currentBalance: number;
98
+ fundedAmount: number;
99
+ profitSplitPercent: number;
100
+ cycleProfit: number;
101
+ cycleShare: number;
102
+ /** Already split, not yet paid. Not part of the balance, never split again. */
103
+ unpaidProfit: number;
104
+ available: number;
105
+ withdrawableNow: number;
106
+ activeDays: number;
107
+ minActiveDays: number;
108
+ cycleBets: number;
109
+ minBets: number;
110
+ conditionsMet: boolean;
111
+ payoutCount: number;
112
+ maxPayouts: number | null;
113
+ remainingPayouts: number | null;
114
+ isFinal: boolean;
115
+ minPayout: number | null;
116
+ payoutIntervalDays: number | null;
117
+ lastPayoutAt: string | null;
118
+ nextPayoutAt: string | null;
119
+ mandatoryPayoutThreshold: number | null;
120
+ totalWithdrawn: number;
121
+ openStake: number;
122
+ pending: PendingPayout | null;
123
+ reasons: PayoutReason[];
124
+ canRequest: boolean;
125
+ }
126
+ export interface PayoutRequest {
127
+ id: string;
128
+ fundedAccountId: string;
129
+ userId: string;
64
130
  amount: number;
131
+ fromUnpaid: number;
132
+ fromCycle: number;
133
+ isFinal: boolean;
134
+ isMandatory: boolean;
135
+ status: 'pending' | 'executed' | 'rejected';
136
+ rejectionReason: string | null;
137
+ ownStakeReturned: number | null;
138
+ decidedBy: string | null;
139
+ createdAt: string;
140
+ decidedAt: string | null;
141
+ }
142
+ /** A payout request as the user's own list returns it: who decided it is not included. */
143
+ export type UserPayoutRequest = Omit<PayoutRequest, 'decidedBy'>;
144
+ /** Result of confirming a payout request. */
145
+ export interface PayoutExecution {
146
+ payoutRequestId: string;
147
+ userPayout: number;
148
+ ownStakeReturned: number;
149
+ totalPayout: number;
150
+ operatorShare: number;
151
+ newBalance: number;
152
+ unpaidProfit: number;
153
+ cycleClosed: boolean;
154
+ forfeitedProfit: number;
155
+ accountClosed: boolean;
65
156
  }
66
157
  /** A challenge instance */
67
158
  export interface Challenge {
@@ -88,6 +179,54 @@ export interface Challenge {
88
179
  createdAt: string;
89
180
  updatedAt: string;
90
181
  }
182
+ /** A challenge as GET /users/:userId/history returns it. */
183
+ export interface ChallengeHistory {
184
+ id: string;
185
+ status: ChallengeStatus;
186
+ failureReason?: string | null;
187
+ ownStake: number;
188
+ startingBalance: number;
189
+ currentBalance: number;
190
+ targetBalance: number;
191
+ startedAt?: string | null;
192
+ expiresAt?: string | null;
193
+ totalBetsPlaced: number;
194
+ totalBetsAccepted: number;
195
+ activeDaysCount: number;
196
+ createdAt?: string | null;
197
+ /** Name of the tier it was bought as. */
198
+ tierName?: string | null;
199
+ /** When it finished. Null while active or awaiting the continue/refund choice. */
200
+ endedAt?: string | null;
201
+ }
202
+ /** A payout request as listed on a funded account in the user history. */
203
+ export interface HistoryPayout {
204
+ amount: number;
205
+ status: 'pending' | 'executed' | 'rejected';
206
+ isFinal: boolean;
207
+ isMandatory: boolean;
208
+ createdAt: string;
209
+ decidedAt?: string | null;
210
+ }
211
+ /** A funded account as GET /users/:userId/history returns it. */
212
+ export interface FundedAccountHistory {
213
+ id: string;
214
+ status: string;
215
+ revocationReason?: string | null;
216
+ fundedAmount: number;
217
+ currentBalance: number;
218
+ totalWithdrawn: number;
219
+ profitSplitPercent: number;
220
+ createdAt?: string | null;
221
+ /** The passed challenge the account was created from. */
222
+ challengeId?: string | null;
223
+ payoutCount?: number;
224
+ maxPayouts?: number | null;
225
+ /** When it closed or was revoked. Null while active. */
226
+ endedAt?: string | null;
227
+ /** Payout requests on the account, newest first. */
228
+ payouts?: HistoryPayout[];
229
+ }
91
230
  /** A challenge product tier (as returned by the public /challenge-tiers endpoint) */
92
231
  export interface ChallengeTier {
93
232
  id: string;
@@ -123,21 +262,50 @@ export interface ChallengeTier {
123
262
  /** Lucky bet max profit as % of target */
124
263
  luckyBetMaxProfitPercent: number;
125
264
  }
126
- /** Full challenge tier with admin-level fields (from /admin/challenge-tiers) */
127
- export interface ChallengeTierFull extends ChallengeTier {
128
- /** Max payout as % of funded balance per cycle. null = no cap */
129
- payoutCapPercent: number | null;
130
- /** ISO 4217 currency code */
131
- currency: string;
132
- enabled: boolean;
265
+ /** Full challenge tier with admin-level fields (from /admin/challenge-tiers). The bet limits live on the referenced rulesets. */
266
+ export interface ChallengeTierFull {
267
+ id: string;
268
+ tenantId: string;
269
+ name: string;
270
+ /** Own stake the user pays to enter */
271
+ ownStake: number;
272
+ /** Conditional bonus granted by the operator */
273
+ bonusAmount: number;
274
+ targetBalance: number;
275
+ durationDays: number;
133
276
  challengeRulesetId: string;
134
277
  fundedRulesetId: string;
278
+ fundedAccountBalance: number;
279
+ /** Profit split in user's favor (e.g. 80 = 80%) */
280
+ profitSplitPercent: number;
281
+ /** Max payout as % of funded balance per cycle. null = no cap */
282
+ payoutCapPercent: number | null;
283
+ /** @deprecated Decides nothing: every payout that includes the cycle share returns the balance to the funded amount. */
135
284
  resetBalanceOnPayout: boolean;
136
285
  fundedMinActiveDays: number;
137
286
  fundedActiveDayPercent: number;
287
+ /** Whether the user may take back the own stake during the funded phase. */
288
+ fundedStakeRefundable?: boolean;
289
+ /** Lifetime payout cap on the funded account. Null = none. */
290
+ fundedTotalPayoutCap?: number | null;
291
+ /** Accepted bets required per payout cycle. */
292
+ fundedMinBets?: number;
293
+ /** Most payouts the funded account allows. Null = no limit. */
294
+ fundedMaxPayouts?: number | null;
295
+ /** Smallest payout that may be requested. Null = none. */
296
+ fundedMinPayout?: number | null;
297
+ /** Days between payouts. Null = no interval. */
298
+ fundedPayoutIntervalDays?: number | null;
299
+ /** Balance at which a payout becomes mandatory. Null = never. */
300
+ fundedMandatoryPayoutThreshold?: number | null;
301
+ /** ISO 4217 currency code */
302
+ currency: string;
303
+ enabled: boolean;
138
304
  createdAt: string;
139
305
  updatedAt: string;
140
306
  }
307
+ /** Body for creating a challenge tier: the fields the API requires, plus any optional ones. */
308
+ export type ChallengeTierCreateRequest = Pick<ChallengeTierFull, 'name' | 'ownStake' | 'bonusAmount' | 'targetBalance' | 'durationDays' | 'challengeRulesetId' | 'fundedRulesetId' | 'fundedAccountBalance' | 'profitSplitPercent' | 'payoutCapPercent' | 'resetBalanceOnPayout' | 'fundedMinActiveDays' | 'fundedActiveDayPercent'> & Partial<Omit<ChallengeTierFull, 'id' | 'tenantId' | 'createdAt' | 'updatedAt'>>;
141
309
  /** Rules engine validation result */
142
310
  export interface ValidationResult {
143
311
  /** accepted = passes all rules, rejected = violates rules, flagged = accepted but noted */
@@ -160,8 +328,17 @@ export interface Ruleset {
160
328
  dailyDrawdownPercent: number;
161
329
  totalDrawdownPercent: number;
162
330
  cooldownDurationHours: number | null;
163
- balanceCalculationBasis: 'original' | 'current';
164
- enabled: boolean;
331
+ balanceCalculationBasis?: 'original' | 'current';
332
+ /** Max selections in an accumulator bet. 0 = no limit. */
333
+ maxAccumulatorSelections?: number;
334
+ /** Absolute cap on potential win per ticket. Null = no cap. */
335
+ maxWinPerTicket?: number | null;
336
+ /** Basis for the daily drawdown calculation. */
337
+ sessionStopBasis?: 'original' | 'current';
338
+ enabled?: boolean;
339
+ tenantId?: string;
340
+ createdAt?: string;
341
+ updatedAt?: string;
165
342
  }
166
343
  /** Live challenge progress for sportsbook UI */
167
344
  export interface ChallengeProgress {
@@ -208,7 +385,18 @@ export interface FundedAccountProgress {
208
385
  fundedAccountId: string;
209
386
  currentBalance: number;
210
387
  fundedAmount: number;
211
- purchasePrice: number;
388
+ /** @deprecated Never returned by the API; always undefined. */
389
+ purchasePrice?: number;
390
+ /** Own stake the user paid to enter. */
391
+ ownStake?: number;
392
+ /** Operator bonus: never withdrawable. */
393
+ nonWithdrawableBonus?: number;
394
+ /** Own stake the user can still take back with `refundFundedStake` (0 when not allowed). */
395
+ refundableOwnStake?: number;
396
+ /** Whether the tier allows an own-stake refund in the funded phase. */
397
+ stakeRefundable?: boolean;
398
+ /** Lifetime payout cap, null for none. */
399
+ totalPayoutCap?: number | null;
212
400
  withdrawableProfit: number;
213
401
  earnedProfit: number;
214
402
  rawProfit: number;
@@ -218,8 +406,23 @@ export interface FundedAccountProgress {
218
406
  totalWithdrawn: number;
219
407
  activeDays: number;
220
408
  minActiveDays: number;
221
- status: 'active';
409
+ status: 'active' | 'closed' | 'revoked';
222
410
  reserved: number;
411
+ /** Stake on bets not yet settled. */
412
+ openStake?: number;
413
+ payoutCount?: number;
414
+ maxPayouts?: number | null;
415
+ unpaidProfit?: number;
416
+ /** Accepted bets in the current payout cycle. */
417
+ totalBetsAccepted?: number;
418
+ /** Stake a day needs for it to count as an active day. */
419
+ activeDayThreshold?: number;
420
+ /** Stake placed today towards `activeDayThreshold`. */
421
+ todayWagered?: number;
422
+ minBetsRequired?: number;
423
+ maxWinPerTicket?: number | null;
424
+ payout?: PayoutState | null;
425
+ lastPayoutRequest?: Pick<PayoutRequest, 'id' | 'amount' | 'status' | 'rejectionReason' | 'isFinal' | 'isMandatory' | 'createdAt' | 'decidedAt'> | null;
223
426
  currency?: string;
224
427
  selfExcluded?: boolean;
225
428
  }
@@ -227,6 +430,11 @@ export interface FundedAccountProgress {
227
430
  export interface NoProgress {
228
431
  type: 'none';
229
432
  message: string;
433
+ /** How the last programme ended: a failed challenge, or a funded account that finished. */
434
+ lastOutcome?: (Record<string, unknown> & {
435
+ type: 'challenge_failed' | 'funded_closed';
436
+ endedAt: string;
437
+ }) | null;
230
438
  selfExcluded?: boolean;
231
439
  }
232
440
  /** Discriminated union - check the `type` field */
@@ -241,12 +449,23 @@ export interface WithdrawableInfo {
241
449
  activeDaysCount: number;
242
450
  minActiveDays: number;
243
451
  activeDaysMet: boolean;
452
+ payout: PayoutState;
244
453
  }
245
- /** Withdrawal result with profit split breakdown */
454
+ /** Payout request result. `executed` carries the profit split breakdown. */
246
455
  export interface WithdrawalResult {
247
- newBalance: number;
248
- userPayout: number;
249
- operatorShare: number;
456
+ status: 'pending' | 'executed';
457
+ payoutRequestId: string;
458
+ amount: number;
459
+ isFinal: boolean;
460
+ newBalance?: number;
461
+ userPayout?: number;
462
+ ownStakeReturned?: number;
463
+ totalPayout?: number;
464
+ operatorShare?: number;
465
+ resetApplied?: boolean;
466
+ unpaidProfit?: number;
467
+ forfeitedProfit?: number;
468
+ accountClosed?: boolean;
250
469
  }
251
470
  /** Bet ingestion result (sync or async mode) */
252
471
  export interface BetIngestResult {
@@ -255,17 +474,35 @@ export interface BetIngestResult {
255
474
  validationResult?: ValidationResult;
256
475
  message?: string;
257
476
  }
477
+ /** One bet's outcome in a batch. */
478
+ export interface BatchBetItemResult {
479
+ /** Zero-based position of the bet in the submitted array. */
480
+ index: number;
481
+ /**
482
+ * The submitted bet's externalBetId, filled in by the SDK from `index`.
483
+ * @sdkComputed
484
+ */
485
+ externalBetId?: string;
486
+ /** processed (sync mode), accepted (async mode, queued), or error. */
487
+ status: 'processed' | 'accepted' | 'error';
488
+ validationResult?: ValidationResult;
489
+ /** Why the bet failed, when status is 'error'. */
490
+ error?: string;
491
+ }
258
492
  /** Batch bet ingestion result */
259
493
  export interface BatchBetResult {
494
+ status: 'completed';
260
495
  total: number;
496
+ /** Bets processed or accepted. */
497
+ succeeded: number;
498
+ /**
499
+ * @deprecated Use `succeeded`. Kept as an alias, filled in by the SDK.
500
+ * @sdkComputed
501
+ */
261
502
  processed: number;
262
503
  failed: number;
263
- results: Array<{
264
- externalBetId: string;
265
- status: 'processed' | 'failed';
266
- validationResult?: ValidationResult;
267
- error?: string;
268
- }>;
504
+ /** Per-bet results in submission order. */
505
+ results: BatchBetItemResult[];
269
506
  }
270
507
  /** Reconciliation data for cross-referencing with sportsbook records */
271
508
  export interface ReconciliationData {
@@ -280,13 +517,46 @@ export interface SseToken {
280
517
  ttl: number;
281
518
  userId: string;
282
519
  }
283
- /** Challenge tier terms and disclosures (regulatory compliance) */
520
+ /** The ruleset limits disclosed with a tier's terms. */
521
+ export interface TierTermsRules {
522
+ maxStakePercent: number;
523
+ minOdds: number;
524
+ maxOdds: number;
525
+ maxPayoutMultiplier: number;
526
+ maxWinPerTicket: number | null;
527
+ minBetsRequired: number;
528
+ minActiveDays: number;
529
+ dailyDrawdownPercent: number;
530
+ totalDrawdownPercent: number;
531
+ cooldownDurationHours: number | null;
532
+ luckyBetMaxProfitPercent: number;
533
+ luckyBetMaxOdds: number;
534
+ balanceCalculationBasis: string;
535
+ }
536
+ /** Language of the human-readable terms and disclosures. */
537
+ export type TermsLanguage = 'en' | 'hr' | 'de';
538
+ /** Challenge tier terms and disclosures (regulatory compliance), as GET /challenge-tiers/:id/terms returns them. */
284
539
  export interface TierTerms {
285
540
  tierId: string;
286
541
  tierName: string;
287
- rules: string[];
542
+ purchasePrice: number;
543
+ ownStake: number;
544
+ bonusAmount: number;
545
+ currency: string;
546
+ /** Balance the user plays with: own stake + bonus. */
547
+ challengeBalance: number;
548
+ targetBalance: number;
549
+ durationDays: number;
550
+ /** Base balance of the reward (funded) account on continuing. */
551
+ rewardAccountBalance: number;
552
+ profitSplitPercent: number;
553
+ payoutCapPercent: number | null;
554
+ /** @deprecated Decides nothing: every payout that includes the cycle share returns the balance to the funded amount. */
555
+ resetBalanceOnPayout?: boolean;
556
+ rules: TierTermsRules;
557
+ /** Sentences to show the user before purchase, in `lang`. */
288
558
  disclosures: string[];
289
- terms: string;
559
+ lang: TermsLanguage;
290
560
  }
291
561
  /** Webhook event payload delivered to your endpoint */
292
562
  export interface WebhookPayload<T = Record<string, unknown>> {
@@ -296,9 +566,8 @@ export interface WebhookPayload<T = Record<string, unknown>> {
296
566
  timestamp: string;
297
567
  data: T;
298
568
  }
299
- /** A user achievement (earned or locked) */
300
- export interface Achievement {
301
- id: string;
569
+ /** Fields every achievement carries, earned or not. */
570
+ export interface AchievementBase {
302
571
  code: string;
303
572
  name: string;
304
573
  description: string;
@@ -306,20 +575,33 @@ export interface Achievement {
306
575
  category: string;
307
576
  points: number;
308
577
  rarity: 'common' | 'rare' | 'epic' | 'legendary';
309
- earned: boolean;
310
- earnedAt: string | null;
311
- progress: number | null;
312
- target: number | null;
578
+ }
579
+ /** An achievement the user has earned. */
580
+ export interface Achievement extends AchievementBase {
581
+ earnedAt: string;
582
+ metadata: Record<string, unknown> | null;
583
+ }
584
+ /** An achievement not yet earned, with progress towards it. */
585
+ export interface LockedAchievement extends AchievementBase {
586
+ sortOrder: number;
587
+ /** What earns it: `progress` counts towards `threshold`. */
588
+ criteria: {
589
+ type: string;
590
+ threshold: number;
591
+ } | null;
592
+ progress: number;
313
593
  }
314
594
  /** User achievements response */
315
595
  export interface UserAchievements {
596
+ /** Earned achievements, most recent first. */
316
597
  achievements: Achievement[];
598
+ /** Achievements not yet earned, in display order. */
599
+ locked: LockedAchievement[];
317
600
  totalPoints: number;
318
601
  level: number;
319
602
  streak: {
320
603
  current: number;
321
604
  longest: number;
322
- lastActiveDate: string | null;
323
605
  };
324
606
  }
325
607
  /** Reward type */
@@ -330,11 +612,15 @@ export type RewardApplyMode = 'auto' | 'claim';
330
612
  export interface Reward {
331
613
  id: string;
332
614
  rewardType: RewardType;
615
+ /** In EUR for money types, otherwise percent or days. */
333
616
  rewardValue: number;
617
+ /** On claimable items the API omits this; the SDK fills `'claim'`. */
334
618
  applyMode: RewardApplyMode;
619
+ /** ISO 8601, or null when it never expires. */
335
620
  expiresAt: string | null;
336
621
  achievementCode: string | null;
337
622
  achievementName: string | null;
623
+ /** ISO 8601 */
338
624
  createdAt: string;
339
625
  }
340
626
  /** User rewards response */
@@ -344,10 +630,9 @@ export interface UserRewards {
344
630
  /** Rewards waiting for user to claim */
345
631
  claimable: Reward[];
346
632
  }
347
- /** Reward claim result */
633
+ /** Reward claim result. Read the reward back with `users.getRewards()` if you need it. */
348
634
  export interface RewardClaimResult {
349
635
  claimed: boolean;
350
- reward: Reward;
351
636
  }
352
637
  /** Leaderboard entry */
353
638
  export interface LeaderboardEntry {
@@ -380,27 +665,41 @@ export interface SandboxResetResult {
380
665
  export interface SandboxUserResetResult {
381
666
  status: string;
382
667
  userId: string;
383
- deleted: Record<string, number>;
668
+ /** Which kinds of data were cleared (challenges, fundedAccounts, betEvents, webhookDeliveries). */
669
+ deleted: Record<string, boolean>;
384
670
  }
385
671
  /** Bet event as returned by admin endpoints */
386
672
  export interface AdminBetEvent {
387
673
  id: string;
674
+ tenantId?: string;
388
675
  userId: string;
389
676
  externalBetId: string;
677
+ challengeId?: string | null;
678
+ fundedAccountId?: string | null;
390
679
  stakeAmount: number;
391
680
  odds: number;
681
+ betTypeIdentifier?: string | null;
392
682
  status: BetStatus;
683
+ isCashOut?: boolean;
393
684
  validationResult: ValidationStatus | null;
394
- reasonCodes: RejectionCode[];
685
+ /** Rule violation codes. Empty when accepted. */
686
+ rejectionReasons: RejectionCode[];
687
+ /**
688
+ * @deprecated The API calls this `rejectionReasons`; the SDK fills this alias from it.
689
+ * @sdkComputed
690
+ */
691
+ reasonCodes?: RejectionCode[];
395
692
  settlementAmount: number | null;
693
+ currency?: string;
396
694
  eventTimestamp: string;
695
+ processedAt?: string | null;
397
696
  createdAt: string;
398
697
  }
399
698
  /** Settle a bet event */
400
699
  export interface BetSettleRequest {
401
700
  outcome: 'won' | 'lost';
402
701
  }
403
- /** Bulk settle request */
702
+ /** Bulk settle request (max 50 settlements per call) */
404
703
  export interface BulkSettleRequest {
405
704
  settlements: Array<{
406
705
  betEventId: string;
@@ -411,6 +710,10 @@ export interface BulkSettleRequest {
411
710
  export interface BulkSettleResult {
412
711
  results: Array<{
413
712
  betEventId: string;
713
+ status: 'settled' | 'error';
714
+ /** Why it was not settled, e.g. `not_found`, `already_settled`. */
715
+ error?: string;
716
+ /** @deprecated Use `status === 'settled'`. Filled in by the SDK. */
414
717
  success: boolean;
415
718
  }>;
416
719
  settled: number;
@@ -422,120 +725,294 @@ export interface WebhookDelivery {
422
725
  eventId: string;
423
726
  eventType: WebhookEventType;
424
727
  userId: string;
728
+ /** The exact body that was (or will be) POSTed. */
729
+ payload: WebhookPayload;
730
+ webhookUrl: string;
425
731
  status: WebhookDeliveryStatus;
426
732
  responseStatus: number | null;
427
733
  attempts: number;
428
734
  lastAttemptAt: string | null;
429
735
  createdAt: string;
430
736
  }
431
- /** Audit log entry */
737
+ /** A webhook delivery that exhausted its retries. */
738
+ export interface WebhookDeadLetter {
739
+ id: string;
740
+ tenantId: string;
741
+ deliveryId: string;
742
+ eventId: string;
743
+ eventType: WebhookEventType;
744
+ userId: string;
745
+ payload: WebhookPayload;
746
+ webhookUrl: string;
747
+ finalAttempts: number;
748
+ lastError: string | null;
749
+ /** When it was replayed. Null until then; a dead letter replays once. */
750
+ replayedAt: string | null;
751
+ replayDeliveryId: string | null;
752
+ createdAt: string;
753
+ }
754
+ /** Result of rotating the webhook signing secret. */
755
+ export interface WebhookSecretRotation {
756
+ /** The new signing secret. Shown once: store it now. */
757
+ newSecret: string;
758
+ /** Hours the old secret keeps being accepted. */
759
+ gracePeriodHours: number;
760
+ oldSecretExpiresAt: string;
761
+ /** @deprecated Use `newSecret`. Filled in by the SDK. */
762
+ webhookSecret: string;
763
+ }
764
+ /** Admin audit log entry (GET /admin/audit-log) */
432
765
  export interface AuditEntry {
433
766
  id: string;
767
+ tenantId: string;
434
768
  action: string;
769
+ /** Admin email, or `api-key` for server-key calls. */
435
770
  actor: string;
436
- tenantId: string;
437
- targetId: string | null;
438
- targetType: string | null;
439
- metadata: Record<string, unknown>;
771
+ resourceType: string | null;
772
+ resourceId: string | null;
773
+ details: Record<string, unknown> | null;
774
+ ipAddress: string | null;
440
775
  createdAt: string;
441
776
  }
777
+ /** Which ledger GET /audit/log reads. */
778
+ export type AuditLogType = 'bets' | 'webhooks';
779
+ /** Page of GET /audit/log: bet events or webhook deliveries. */
780
+ export interface AuditLogPage {
781
+ /** `bet_events` or `webhook_deliveries`. */
782
+ type: string;
783
+ total: number;
784
+ limit: number;
785
+ offset: number;
786
+ entries: Array<Record<string, unknown>>;
787
+ }
442
788
  /** Tenant settings */
443
789
  export interface TenantSettings {
790
+ tenantId?: string;
791
+ name?: string;
792
+ environment?: string;
444
793
  integrationMode: IntegrationMode;
445
794
  webhookUrl: string | null;
446
795
  /** Single URL FAST calls before any money/irreversible action (type in payload). */
447
796
  approvalUrl: string | null;
448
797
  operatorChallengeLimit: number | null;
798
+ monthlyChallengeLimit?: number | null;
799
+ effectiveChallengeLimit?: number | null;
800
+ revenueSharePercent?: number;
801
+ exposureAlertThreshold?: number | null;
802
+ exportEnabled?: boolean;
803
+ exportEmail?: string | null;
804
+ exportWebhookUrl?: string | null;
805
+ defaultCurrency?: string;
806
+ supportedCurrencies?: string[];
807
+ widgetLayout?: {
808
+ fundedOnly: boolean;
809
+ history: boolean;
810
+ };
811
+ createdAt?: string;
449
812
  }
450
- /** Admin overview stats */
813
+ /** Admin overview stats (GET /admin/overview) */
451
814
  export interface AdminOverview {
815
+ period: {
816
+ month: number;
817
+ year: number;
818
+ };
819
+ /** Count per challenge status. */
452
820
  challenges: Record<string, number>;
821
+ /** Count per funded account status. */
453
822
  fundedAccounts: Record<string, number>;
454
- totalBets: number;
455
- uniqueUsers: number;
823
+ revenue: {
824
+ allTime: Record<string, number>;
825
+ /** Includes `uniqueUsers` for the month. */
826
+ month: Record<string, number>;
827
+ revenueShare: Record<string, number>;
828
+ };
829
+ exposure: Record<string, unknown>;
830
+ usage: Record<string, number>;
456
831
  }
457
- /** Bonus offer (admin) */
832
+ /** Bonus type */
833
+ export type BonusType = 'deposit_match' | 'free_bet' | 'no_deposit' | 'cashback' | 'reload';
834
+ /** Bonus offer (admin). Money fields are integer cents. */
458
835
  export interface BonusOffer {
459
836
  id: string;
837
+ tenantId?: string;
460
838
  name: string;
461
- bonusType: string;
839
+ description: string | null;
840
+ bonusType: BonusType;
462
841
  matchPercent: number | null;
842
+ /** Integer cents. */
463
843
  maxBonusAmount: number;
464
844
  wageringMultiplier: number;
465
- durationDays: number;
466
845
  minOdds: number | null;
846
+ /** Integer cents. */
847
+ maxBetFromBonus: number | null;
848
+ excludedMarkets: string[] | null;
849
+ durationDays: number;
850
+ /** Integer cents. */
851
+ minQualifyingDeposit: number | null;
852
+ maxActivations: number | null;
467
853
  maxPerUser: number | null;
854
+ stackable: boolean;
468
855
  forfeitOnWithdrawal: boolean;
469
856
  enabled: boolean;
857
+ startsAt: string | null;
858
+ endsAt: string | null;
859
+ sortOrder: number;
470
860
  createdAt: string;
861
+ updatedAt: string;
471
862
  }
472
- /** Bonus stats */
863
+ /** Bonus stats (GET /admin/bonuses/stats) */
473
864
  export interface BonusStats {
474
- totalActive: number;
475
- totalCompleted: number;
476
- totalForfeited: number;
477
- totalBonusAmount: number;
865
+ activeCount: number;
866
+ /** Percent of bonuses completed over the last 30 days. */
867
+ completionRate30d: number;
868
+ /** Bonus balance outstanding, integer cents. */
869
+ totalOutstanding: number;
870
+ avgProgress: number;
478
871
  }
479
872
  /** Mission definition (admin) */
480
873
  export interface MissionDef {
481
874
  id: string;
875
+ tenantId?: string;
482
876
  name: string;
483
877
  description: string | null;
878
+ icon: string | null;
484
879
  criteriaType: string;
485
880
  criteriaThreshold: number;
881
+ criteriaMeta: Record<string, unknown> | null;
486
882
  rewardType: string | null;
883
+ /** In cents for money types (bonus credit, free bet, fixed discount...), otherwise percent, days or points. Unlike reward rules and grants, which take EUR. */
487
884
  rewardValue: number;
488
- enrollmentMode: 'auto' | 'opt_in';
489
- durationHours: number | null;
490
- maxParticipants: number | null;
885
+ rewardApplyMode: string;
886
+ startsAt: string;
887
+ endsAt: string;
888
+ enrollmentMode: 'auto' | 'opt_in' | 'targeted';
889
+ repeatable: boolean;
890
+ maxCompletionsPerUser: number;
491
891
  enabled: boolean;
892
+ sortOrder: number;
492
893
  createdAt: string;
894
+ updatedAt: string;
895
+ }
896
+ /** Fields the API assigns itself and drops from a request body. */
897
+ type ServerManaged = 'id' | 'tenantId' | 'createdAt' | 'updatedAt';
898
+ /** Body for creating a mission: the fields the API requires, plus any optional ones. A new mission starts enabled. */
899
+ export type MissionCreateRequest = Pick<MissionDef, 'name' | 'criteriaType' | 'criteriaThreshold' | 'startsAt' | 'endsAt'> & Partial<Omit<MissionDef, ServerManaged | 'enabled'>>;
900
+ /** Body for updating a mission: any subset of its editable fields. */
901
+ export type MissionUpdateRequest = Partial<Omit<MissionDef, ServerManaged>>;
902
+ type RulesetRequired = 'name' | 'maxStakePercent' | 'minOdds' | 'maxOdds' | 'maxPayoutMultiplier' | 'dailyDrawdownPercent' | 'totalDrawdownPercent' | 'minActiveDays' | 'minBetsRequired' | 'luckyBetMaxOdds' | 'luckyBetMaxProfitPercent';
903
+ /** Body for creating a ruleset: the limits the API requires, plus any optional ones. */
904
+ export type RulesetCreateRequest = Pick<Ruleset, RulesetRequired> & Partial<Omit<Ruleset, RulesetRequired | ServerManaged>>;
905
+ /** Body for updating a ruleset: any subset of its editable fields. */
906
+ export type RulesetUpdateRequest = Partial<Omit<Ruleset, ServerManaged>>;
907
+ /** Body for creating a bonus offer. A new offer starts enabled. */
908
+ export type BonusOfferCreateRequest = Pick<BonusOffer, 'name' | 'bonusType' | 'maxBonusAmount'> & Partial<Omit<BonusOffer, 'name' | 'bonusType' | 'maxBonusAmount' | 'enabled' | ServerManaged>>;
909
+ /** Body for updating a bonus offer. The bonus type is fixed once created. */
910
+ export type BonusOfferUpdateRequest = Partial<Omit<BonusOffer, 'bonusType' | ServerManaged>>;
911
+ /** Body for updating the loyalty programme configuration. */
912
+ export type LoyaltyConfigUpdateRequest = Partial<Omit<LoyaltyConfig, ServerManaged>>;
913
+ /** Tournament lifecycle state */
914
+ export type TournamentStatus = 'upcoming' | 'active' | 'settling' | 'completed' | 'cancelled';
915
+ /** A prize band: ranks rankFrom..rankTo each receive the reward. */
916
+ export interface TournamentPrize {
917
+ rankFrom: number;
918
+ rankTo: number;
919
+ rewardType: string;
920
+ /** In cents for money types, otherwise percent, days or points. */
921
+ rewardValue: number;
493
922
  }
494
923
  /** Tournament definition (admin) */
495
924
  export interface TournamentDef {
496
925
  id: string;
926
+ tenantId?: string;
497
927
  name: string;
498
928
  description: string | null;
499
- status: 'draft' | 'scheduled' | 'active' | 'settling' | 'settled';
500
- entryMode: 'free' | 'paid';
501
- entryFee: number;
502
- prizePool: number;
503
- maxEntrants: number | null;
504
- scoringMetric: string;
929
+ scoringMetric: 'net_profit' | 'total_wagered' | 'win_count' | 'bet_count' | 'highest_odds_won' | 'longest_win_streak';
505
930
  startsAt: string;
506
931
  endsAt: string;
932
+ entryMode: 'auto' | 'opt_in' | 'invite';
933
+ minBetsToQualify: number;
934
+ maxParticipants: number | null;
935
+ prizes: TournamentPrize[];
936
+ tiebreaker: 'earliest_entry' | 'most_bets' | 'highest_single_win' | null;
937
+ status: TournamentStatus;
938
+ totalEntries: number;
939
+ settledAt: string | null;
507
940
  createdAt: string;
941
+ updatedAt?: string;
508
942
  }
943
+ /** Body for creating a tournament: the fields the API requires, plus any optional ones. */
944
+ export type TournamentCreateRequest = Pick<TournamentDef, 'name' | 'scoringMetric' | 'startsAt' | 'endsAt' | 'prizes'> & Partial<Pick<TournamentDef, 'description' | 'entryMode' | 'minBetsToQualify' | 'maxParticipants' | 'tiebreaker'>>;
509
945
  /** Tournament leaderboard entry */
510
946
  export interface TournamentLeaderboardEntry {
511
947
  rank: number;
512
948
  userId: string;
949
+ /** Masked user id for display. */
513
950
  displayName: string;
514
951
  score: number;
515
- prize: number | null;
952
+ qualifyingBets: number;
953
+ }
954
+ /** A tournament entry row as the admin entries list returns it. */
955
+ export interface TournamentEntry {
956
+ id: string;
957
+ tenantId: string;
958
+ tournamentId: string;
959
+ userId: string;
960
+ score: number | null;
961
+ qualifyingBets: number | null;
962
+ rank: number | null;
963
+ prizeType: string | null;
964
+ /** In cents for money types. */
965
+ prizeValue: number | null;
966
+ prizeGranted: boolean | null;
967
+ enteredAt: string | null;
968
+ lastScoreUpdate: string | null;
516
969
  }
517
- /** Loyalty config */
970
+ /** Loyalty config (GET/PUT /admin/loyalty/config) */
518
971
  export interface LoyaltyConfig {
519
- enabled: boolean;
520
- pointsPerEurWagered: number;
521
- cashbackPercent: number;
522
- decayDays: number | null;
972
+ id?: string;
973
+ tenantId?: string;
974
+ /** Activity-to-points rules, e.g. `{ activity: 'bet_placed', pointsPerEurWagered: 1, maxPerDay: 500 }`. */
975
+ pointRules: Array<Record<string, unknown>>;
976
+ decayMode: string;
523
977
  decayPercent: number | null;
978
+ cashbackEnabled: boolean;
979
+ cashbackSettleMode: string;
980
+ demotionEnabled: boolean;
981
+ demotionGraceDays: number;
982
+ enabled: boolean;
983
+ createdAt?: string;
984
+ updatedAt?: string;
524
985
  }
525
986
  /** Loyalty tier */
526
987
  export interface LoyaltyTier {
527
988
  id: string;
989
+ tenantId?: string;
528
990
  name: string;
991
+ /** Rank order; higher is more prestigious. Required on create. */
992
+ level: number;
529
993
  minPoints: number;
530
- multiplier: number;
531
- perks: string[];
994
+ cashbackPercent: number;
995
+ benefits: unknown[];
996
+ icon: string;
997
+ color: string;
998
+ createdAt?: string;
532
999
  }
1000
+ /** Body for creating a loyalty tier. */
1001
+ export type LoyaltyTierCreateRequest = Pick<LoyaltyTier, 'name' | 'level' | 'minPoints'> & Partial<Pick<LoyaltyTier, 'cashbackPercent' | 'benefits' | 'icon' | 'color'>>;
1002
+ /** Body for updating a loyalty tier: any subset of its editable fields. */
1003
+ export type LoyaltyTierUpdateRequest = Partial<Pick<LoyaltyTier, 'name' | 'level' | 'minPoints' | 'cashbackPercent' | 'benefits' | 'icon' | 'color'>>;
533
1004
  /** Loyalty stats */
534
1005
  export interface LoyaltyStats {
535
- totalMembers: number;
536
- totalPointsIssued: number;
1006
+ totalUsers: number;
1007
+ avgPoints: number;
537
1008
  totalCashbackPaid: number;
538
- tierDistribution: Record<string, number>;
1009
+ /** Users active in the last 7 days. */
1010
+ active7d: number;
1011
+ tierDistribution: Array<{
1012
+ name: string;
1013
+ level: number;
1014
+ userCount: number;
1015
+ }>;
539
1016
  }
540
1017
  /** Webhook echo entry */
541
1018
  export interface WebhookEchoEntry {
@@ -565,28 +1042,131 @@ export interface ReconciliationFlag {
565
1042
  flaggedBy: string;
566
1043
  createdAt: string;
567
1044
  }
1045
+ /** Reward types an operator can grant or attach to a reward rule. */
1046
+ export type GrantableRewardType = 'balance_bonus' | 'balance_boost_percent' | 'purchase_discount_percent' | 'purchase_discount_fixed' | 'profit_split_boost' | 'extended_duration' | 'payout_cap_boost_percent';
568
1047
  /** Reward rule definition (auto-grants rewards on criteria match) */
569
1048
  export interface RewardRule {
570
1049
  id: string;
1050
+ tenantId?: string;
571
1051
  name: string;
572
- triggerType: string;
573
- triggerThreshold: number;
574
- rewardType: RewardType;
1052
+ description: string | null;
1053
+ /** What triggers the grant: `type` reaching `threshold` (e.g. `{ type: 'active_days', threshold: 30 }`). */
1054
+ criteria: {
1055
+ type: string;
1056
+ threshold: number;
1057
+ };
1058
+ rewardType: GrantableRewardType;
1059
+ /** In EUR for money types, otherwise percent or days. Unlike missions and tournament prizes, which take cents. */
575
1060
  rewardValue: number;
576
1061
  applyMode: RewardApplyMode;
577
- maxGrantsPerUser: number | null;
1062
+ maxGrantsPerUser: number;
578
1063
  expiresInDays: number | null;
579
1064
  enabled: boolean;
580
1065
  sortOrder: number;
581
1066
  createdAt: string;
582
1067
  }
1068
+ /** Body for creating an achievement definition. */
1069
+ export interface AchievementCreateRequest {
1070
+ /** Unique code within the tenant. */
1071
+ code: string;
1072
+ name: string;
1073
+ description?: string;
1074
+ icon?: string;
1075
+ category?: AchievementCategory;
1076
+ points?: number;
1077
+ /** Unlock criteria, evaluated by the achievement engine. */
1078
+ criteria?: Record<string, unknown>;
1079
+ /** Reward granted on unlock, or null for none. Money values in EUR. */
1080
+ reward?: {
1081
+ type: GrantableRewardType;
1082
+ value: number;
1083
+ applyMode?: RewardApplyMode;
1084
+ expiresInDays?: number | null;
1085
+ } | null;
1086
+ }
1087
+ export type AchievementCategory = 'general' | 'streak' | 'volume' | 'profit' | 'milestone';
1088
+ /** Body for updating an achievement definition. Its code and reward are fixed once created. */
1089
+ export type AchievementUpdateRequest = Partial<Pick<AchievementCreateRequest, 'name' | 'description' | 'icon' | 'category' | 'points' | 'criteria'>> & {
1090
+ enabled?: boolean;
1091
+ };
1092
+ /** Body for updating the widget theme. */
1093
+ export interface ThemeUpdateRequest {
1094
+ primaryColor?: string;
1095
+ secondaryColor?: string;
1096
+ fontFamily?: string;
1097
+ /** Absolute URL of the logo image. */
1098
+ logoUrl?: string;
1099
+ /** Text overrides keyed by copy id. */
1100
+ customCopy?: Record<string, unknown>;
1101
+ }
1102
+ /** Events an operator can switch notifications on or off for. */
1103
+ export type NotificationPreferenceEvent = 'bet.validation_result' | 'challenge.activated' | 'challenge.completed' | 'challenge.failed' | 'cooldown.triggered' | 'cooldown.lifted' | 'funded_account.activated' | 'funded_account.payout' | 'funded_account.payout_requested' | 'funded_account.payout_rejected' | 'funded_account.revoked' | 'user.self_excluded' | 'user.self_exclusion_lifted';
1104
+ /** Body for updating notification preferences: the events to change, true to notify. */
1105
+ export type NotificationPreferencesUpdate = Partial<Record<NotificationPreferenceEvent, boolean>>;
1106
+ /** Body for excluding a user. Without expiresAt or durationDays the exclusion is permanent. */
1107
+ export interface SelfExclusionRequest {
1108
+ userId: string;
1109
+ reason?: string;
1110
+ /** ISO 8601 end of the exclusion, or null for permanent. Wins over durationDays. */
1111
+ expiresAt?: string | null;
1112
+ /** Exclusion length in days from now; the SDK turns it into expiresAt. */
1113
+ durationDays?: number;
1114
+ /** E.g. a national registry reference. */
1115
+ metadata?: Record<string, unknown>;
1116
+ }
1117
+ /** Body for creating or updating a reward rule. */
1118
+ export interface RewardRuleInput {
1119
+ name?: string;
1120
+ description?: string;
1121
+ criteria?: {
1122
+ type: string;
1123
+ threshold: number;
1124
+ };
1125
+ rewardType?: GrantableRewardType;
1126
+ /** In EUR for money types, otherwise percent or days. */
1127
+ rewardValue?: number;
1128
+ applyMode?: RewardApplyMode;
1129
+ maxGrantsPerUser?: number;
1130
+ expiresInDays?: number | null;
1131
+ sortOrder?: number;
1132
+ /** Update only. */
1133
+ enabled?: boolean;
1134
+ /** @deprecated Use `criteria.type`. Mapped to `criteria` by the SDK. */
1135
+ triggerType?: string;
1136
+ /** @deprecated Use `criteria.threshold`. Mapped to `criteria` by the SDK. */
1137
+ triggerThreshold?: number;
1138
+ }
1139
+ /** A reward that has been applied, from the grant history. */
1140
+ export interface RewardApplication {
1141
+ id: string;
1142
+ tenantId: string;
1143
+ userId: string;
1144
+ rewardId: string;
1145
+ /** What it was applied to (challenge / funded account / purchase). */
1146
+ appliedTo: string;
1147
+ rewardType: string;
1148
+ rewardValue: number;
1149
+ effectDescription: string | null;
1150
+ appliedAt: string;
1151
+ originalType: string;
1152
+ achievementId: string | null;
1153
+ achievementCode: string | null;
1154
+ achievementName: string | null;
1155
+ }
583
1156
  /** Self-exclusion record */
584
1157
  export interface SelfExclusionRecord {
1158
+ id: string;
1159
+ tenantId: string;
585
1160
  userId: string;
586
1161
  reason: string;
1162
+ excludedBy: string | null;
1163
+ /** ISO 8601 */
587
1164
  excludedAt: string;
1165
+ /** ISO 8601. Null = permanent. */
588
1166
  expiresAt: string | null;
589
- liftedAt: string | null;
1167
+ metadata: Record<string, unknown>;
1168
+ /** ISO 8601 */
1169
+ createdAt: string;
590
1170
  }
591
1171
  /** Standard API error response */
592
1172
  export interface FastError {
@@ -648,4 +1228,5 @@ export interface FastClientConfig {
648
1228
  */
649
1229
  dangerouslyAllowBrowser?: boolean;
650
1230
  }
1231
+ export {};
651
1232
  //# sourceMappingURL=types.d.ts.map