@smartico/public-api 0.0.380 → 0.0.382

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.
Files changed (178) hide show
  1. package/CLAUDE.md +103 -0
  2. package/README.md +32 -10
  3. package/dist/Base/ClassId.d.ts +2 -0
  4. package/dist/Jackpots/JackpotDetails.d.ts +14 -15
  5. package/dist/Jackpots/JackpotPot.d.ts +10 -6
  6. package/dist/MiniGames/SAWGameType.d.ts +3 -1
  7. package/dist/MiniGames/SAWTemplateUI.d.ts +7 -0
  8. package/dist/Raffle/GetRaffleWonPrizesRequest.d.ts +9 -0
  9. package/dist/Raffle/GetRaffleWonPrizesResponse.d.ts +52 -0
  10. package/dist/Raffle/index.d.ts +2 -0
  11. package/dist/SmarticoAPI.d.ts +6 -1
  12. package/dist/SmarticoLib/index.d.ts +5 -1
  13. package/dist/WSAPI/WSAPIAvatars.d.ts +267 -49
  14. package/dist/WSAPI/WSAPIBonuses.d.ts +224 -10
  15. package/dist/WSAPI/WSAPIGamePick.d.ts +538 -253
  16. package/dist/WSAPI/WSAPIGeneral.d.ts +156 -14
  17. package/dist/WSAPI/WSAPIInbox.d.ts +547 -58
  18. package/dist/WSAPI/WSAPIJackpots.d.ts +292 -57
  19. package/dist/WSAPI/WSAPILeaderBoard.d.ts +127 -13
  20. package/dist/WSAPI/WSAPIMiniGames.d.ts +465 -49
  21. package/dist/WSAPI/WSAPIRaffles.d.ts +576 -52
  22. package/dist/WSAPI/WSAPITournaments.d.ts +3 -3
  23. package/dist/WSAPI/WSAPITypes.d.ts +243 -132
  24. package/dist/WSAPI/WSAPIUser.d.ts +193 -43
  25. package/dist/index.js +3412 -616
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.modern.mjs +3395 -609
  28. package/dist/index.modern.mjs.map +1 -1
  29. package/docs/Callbacks.md +162 -0
  30. package/docs/SmarticoObject.md +445 -0
  31. package/docs/Transforms.md +99 -0
  32. package/docs/api/README.md +26 -0
  33. package/docs/api/classes/WSAPIAvatars.md +282 -54
  34. package/docs/api/classes/WSAPIBonuses.md +233 -12
  35. package/docs/api/classes/WSAPIClans.md +380 -10
  36. package/docs/api/classes/WSAPIGamePick.md +555 -26
  37. package/docs/api/classes/WSAPIGeneral.md +165 -16
  38. package/docs/api/classes/WSAPIInbox.md +588 -64
  39. package/docs/api/classes/WSAPIJackpots.md +320 -66
  40. package/docs/api/classes/WSAPILeaderBoard.md +133 -15
  41. package/docs/api/classes/WSAPIMiniGames.md +501 -64
  42. package/docs/api/classes/WSAPIMissions.md +58 -79
  43. package/docs/api/classes/WSAPIRaffles.md +647 -69
  44. package/docs/api/classes/WSAPIStore.md +83 -108
  45. package/docs/api/classes/WSAPITournaments.md +495 -28
  46. package/docs/api/classes/WSAPIUser.md +520 -83
  47. package/docs/api/documents/Callbacks.md +162 -0
  48. package/docs/api/documents/SmarticoObject.md +445 -0
  49. package/docs/api/documents/Transforms.md +99 -0
  50. package/docs/api/enumerations/BonusStatus-1.md +11 -6
  51. package/docs/api/enumerations/JoinClanErrorCode.md +81 -0
  52. package/docs/api/enumerations/TournamentRegistrationError.md +12 -0
  53. package/docs/api/functions/clanTournamentPlayersTransform.md +13 -0
  54. package/docs/api/interfaces/AvatarCustomized.md +25 -0
  55. package/docs/api/interfaces/AvatarDefinition.md +94 -0
  56. package/docs/api/interfaces/AvatarPrompt.md +45 -0
  57. package/docs/api/interfaces/BonusMetaMap-1.md +4 -1
  58. package/docs/api/interfaces/BonusTemplateMetaMap-1.md +9 -4
  59. package/docs/api/interfaces/Clan.md +4 -0
  60. package/docs/api/interfaces/ClanInfo.md +109 -0
  61. package/docs/api/interfaces/ClanMember.md +53 -0
  62. package/docs/api/interfaces/ClanTournamentPlayerRaw.md +67 -0
  63. package/docs/api/interfaces/GemsDiamondsLog.md +43 -0
  64. package/docs/api/interfaces/GetClanInfoResponse.md +61 -0
  65. package/docs/api/interfaces/GetClanTournamentPlayersResponse.md +81 -0
  66. package/docs/api/interfaces/GetRaffleWonPrizesRequest.md +59 -0
  67. package/docs/api/interfaces/GetRaffleWonPrizesResponse.md +95 -0
  68. package/docs/api/interfaces/InboxMarkMessageAction.md +10 -2
  69. package/docs/api/interfaces/JackpotDetails.md +14 -15
  70. package/docs/api/interfaces/JackpotPot.md +9 -6
  71. package/docs/api/interfaces/LeaderBoardDetailsT.md +14 -8
  72. package/docs/api/interfaces/LeaderBoardUserT.md +8 -5
  73. package/docs/api/interfaces/LeaderBoardsRewardsT.md +7 -0
  74. package/docs/api/interfaces/PointsLog.md +43 -0
  75. package/docs/api/interfaces/RaffleWonPrize.md +59 -0
  76. package/docs/api/interfaces/RaffleWonPrizePublicMeta.md +27 -0
  77. package/docs/api/interfaces/RaffleWonPrizeUser.md +43 -0
  78. package/docs/api/interfaces/TAchCategory.md +2 -7
  79. package/docs/api/interfaces/TAvatarCustomized.md +4 -4
  80. package/docs/api/interfaces/TAvatarDefinition.md +13 -14
  81. package/docs/api/interfaces/TAvatarPrompt.md +6 -6
  82. package/docs/api/interfaces/TBonus.md +16 -8
  83. package/docs/api/interfaces/TBuyStoreItemResult.md +4 -29
  84. package/docs/api/interfaces/TClaimBonusResult.md +5 -5
  85. package/docs/api/interfaces/TClan.md +8 -4
  86. package/docs/api/interfaces/TClanInfo.md +47 -0
  87. package/docs/api/interfaces/TClanJoinResult.md +11 -1
  88. package/docs/api/interfaces/TClanTournamentPlayer.md +55 -0
  89. package/docs/api/interfaces/TClanTournamentPlayersResult.md +27 -0
  90. package/docs/api/interfaces/TClans.md +3 -1
  91. package/docs/api/interfaces/TGetTranslations.md +4 -0
  92. package/docs/api/interfaces/TInboxMessage.md +14 -6
  93. package/docs/api/interfaces/TInboxMessageBody.md +18 -12
  94. package/docs/api/interfaces/TLevel.md +17 -20
  95. package/docs/api/interfaces/TLevelCurrent.md +19 -18
  96. package/docs/api/interfaces/TMiniGamePlayBatchResult.md +14 -6
  97. package/docs/api/interfaces/TMiniGamePlayResult.md +9 -4
  98. package/docs/api/interfaces/TMissionClaimRewardResult.md +1 -9
  99. package/docs/api/interfaces/TMissionOptInResult.md +1 -5
  100. package/docs/api/interfaces/TMissionOrBadge.md +4 -7
  101. package/docs/api/interfaces/TRaffleOptinResponse.md +4 -2
  102. package/docs/api/interfaces/TSegmentCheckResult.md +10 -0
  103. package/docs/api/interfaces/TSetAvatarResult.md +3 -3
  104. package/docs/api/interfaces/TStoreItem.md +3 -13
  105. package/docs/api/interfaces/TTournament.md +1 -1
  106. package/docs/api/interfaces/TTournamentDetailed.md +1 -1
  107. package/docs/api/interfaces/TTournamentRegistrationResult.md +4 -2
  108. package/docs/api/interfaces/TUICustomSection.md +23 -19
  109. package/docs/api/interfaces/TUserProfile.md +18 -16
  110. package/docs/api/interfaces/TransformedRaffleClaimPrizeResponse.md +6 -2
  111. package/docs/api/interfaces/UserLevelExtraCountersT.md +9 -2
  112. package/docs/api/type-aliases/ActivityLogEntry.md +3 -0
  113. package/docs/native/ADDING_METHODS.md +7 -2
  114. package/docs/native/PROTOCOL.md +337 -0
  115. package/docs/ui/avatars/UIGuide_getAvatarPrompts.md +100 -0
  116. package/docs/ui/avatars/UIGuide_getAvatarsCustomized.md +90 -0
  117. package/docs/ui/avatars/UIGuide_getAvatarsList.md +105 -0
  118. package/docs/ui/avatars/UIGuide_setAvatar.md +118 -0
  119. package/docs/ui/bonuses/UIGuide_claimBonus.md +144 -0
  120. package/docs/ui/bonuses/UIGuide_getBonuses.md +176 -0
  121. package/docs/ui/gamepick/UIGuide_gamePickGetActiveRound.md +99 -0
  122. package/docs/ui/gamepick/UIGuide_gamePickGetActiveRounds.md +76 -0
  123. package/docs/ui/gamepick/UIGuide_gamePickGetBoard.md +81 -0
  124. package/docs/ui/gamepick/UIGuide_gamePickGetGameInfo.md +78 -0
  125. package/docs/ui/gamepick/UIGuide_gamePickGetHistory.md +55 -0
  126. package/docs/ui/gamepick/UIGuide_gamePickGetRoundInfoForUser.md +71 -0
  127. package/docs/ui/gamepick/UIGuide_gamePickGetUserInfo.md +64 -0
  128. package/docs/ui/gamepick/UIGuide_gamePickSubmitSelection.md +103 -0
  129. package/docs/ui/gamepick/UIGuide_gamePickSubmitSelectionQuiz.md +67 -0
  130. package/docs/ui/general/UIGuide_getCustomSections.md +137 -0
  131. package/docs/ui/general/UIGuide_getRelatedItemsForGame.md +140 -0
  132. package/docs/ui/inbox/UIGuide_deleteInboxMessage.md +135 -0
  133. package/docs/ui/inbox/UIGuide_getInboxMessageBody.md +133 -0
  134. package/docs/ui/inbox/UIGuide_getInboxMessages.md +186 -0
  135. package/docs/ui/inbox/UIGuide_markInboxMessageAsRead.md +122 -0
  136. package/docs/ui/inbox/UIGuide_markUnmarkInboxMessageAsFavorite.md +84 -0
  137. package/docs/ui/jackpots/UIGuide_getJackpotEligibleGames.md +73 -0
  138. package/docs/ui/jackpots/UIGuide_getJackpotWinners.md +83 -0
  139. package/docs/ui/jackpots/UIGuide_jackpotGet.md +138 -0
  140. package/docs/ui/jackpots/UIGuide_jackpotOptIn.md +85 -0
  141. package/docs/ui/jackpots/UIGuide_jackpotOptOut.md +65 -0
  142. package/docs/ui/leaderboard/UIGuide_getLeaderBoard.md +166 -0
  143. package/docs/ui/minigames/UIGuide_getMiniGames.md +153 -0
  144. package/docs/ui/minigames/UIGuide_getMiniGamesHistory.md +90 -0
  145. package/docs/ui/minigames/UIGuide_playMiniGame.md +150 -0
  146. package/docs/ui/minigames/UIGuide_playMiniGameBatch.md +128 -0
  147. package/docs/ui/raffles/UIGuide_claimRafflePrize.md +99 -0
  148. package/docs/ui/raffles/UIGuide_getRaffleDrawRun.md +117 -0
  149. package/docs/ui/raffles/UIGuide_getRaffleDrawRunsHistory.md +92 -0
  150. package/docs/ui/raffles/UIGuide_getRaffleWonPrizes.md +120 -0
  151. package/docs/ui/raffles/UIGuide_getRaffles.md +175 -0
  152. package/docs/ui/raffles/UIGuide_requestRaffleOptin.md +114 -0
  153. package/docs/ui/user/UIGuide_getActivityLog.md +130 -0
  154. package/docs/ui/user/UIGuide_getLevels.md +47 -42
  155. package/package.json +1 -1
  156. package/src/Base/ClassId.ts +2 -0
  157. package/src/Jackpots/JackpotDetails.ts +14 -15
  158. package/src/Jackpots/JackpotPot.ts +10 -6
  159. package/src/MiniGames/SAWGameType.ts +3 -0
  160. package/src/MiniGames/SAWTemplateUI.ts +10 -0
  161. package/src/Raffle/GetRaffleWonPrizesRequest.ts +12 -0
  162. package/src/Raffle/GetRaffleWonPrizesResponse.ts +56 -0
  163. package/src/Raffle/index.ts +3 -1
  164. package/src/SmarticoAPI.ts +14 -1
  165. package/src/SmarticoLib/index.ts +4 -0
  166. package/src/WSAPI/WSAPIAvatars.ts +267 -49
  167. package/src/WSAPI/WSAPIBonuses.ts +224 -10
  168. package/src/WSAPI/WSAPIGamePick.ts +538 -253
  169. package/src/WSAPI/WSAPIGeneral.ts +156 -14
  170. package/src/WSAPI/WSAPIInbox.ts +545 -56
  171. package/src/WSAPI/WSAPIJackpots.ts +297 -61
  172. package/src/WSAPI/WSAPILeaderBoard.ts +127 -13
  173. package/src/WSAPI/WSAPIMiniGames.ts +465 -49
  174. package/src/WSAPI/WSAPIRaffles.ts +592 -64
  175. package/src/WSAPI/WSAPITournaments.ts +3 -3
  176. package/src/WSAPI/WSAPITypes.ts +243 -132
  177. package/src/WSAPI/WSAPIUser.ts +193 -43
  178. package/tsconfig.json +9 -1
@@ -153,6 +153,8 @@ var ClassId;
153
153
  ClassId[ClassId["RAF_CLAIM_PRIZE_RESPONSE"] = 909] = "RAF_CLAIM_PRIZE_RESPONSE";
154
154
  ClassId[ClassId["RAF_OPTIN_REQUEST"] = 916] = "RAF_OPTIN_REQUEST";
155
155
  ClassId[ClassId["RAF_OPTIN_RESPONSE"] = 917] = "RAF_OPTIN_RESPONSE";
156
+ ClassId[ClassId["RAF_GET_WON_PRIZES_REQUEST"] = 920] = "RAF_GET_WON_PRIZES_REQUEST";
157
+ ClassId[ClassId["RAF_GET_WON_PRIZES_RESPONSE"] = 921] = "RAF_GET_WON_PRIZES_RESPONSE";
156
158
  /*
157
159
  RAF_GET_TICKETS_REQUEST = 902,
158
160
  RAF_GET_TICKETS_RESPONSE = 903,
@@ -356,6 +358,7 @@ var SAWGameType;
356
358
  SAWGameType[SAWGameType["Voyager"] = 10] = "Voyager";
357
359
  SAWGameType[SAWGameType["Plinko"] = 11] = "Plinko";
358
360
  SAWGameType[SAWGameType["CoinFlip"] = 12] = "CoinFlip";
361
+ SAWGameType[SAWGameType["CustomMinigame"] = 13] = "CustomMinigame";
359
362
  })(SAWGameType || (SAWGameType = {}));
360
363
  var SAWGameTypeName;
361
364
  (function (SAWGameTypeName) {
@@ -371,6 +374,7 @@ var SAWGameTypeName;
371
374
  SAWGameTypeName["Voyager"] = "voyager";
372
375
  SAWGameTypeName["Plinko"] = "plinko";
373
376
  SAWGameTypeName["CoinFlip"] = "coin_flip";
377
+ SAWGameTypeName["CustomMinigame"] = "custom_minigame";
374
378
  SAWGameTypeName["Unknown"] = "unknown";
375
379
  })(SAWGameTypeName || (SAWGameTypeName = {}));
376
380
  /** @hidden */
@@ -387,7 +391,8 @@ const SAWGameTypeNamed = type => {
387
391
  [SAWGameType.TreasureHunt]: SAWGameTypeName.TreasureHunt,
388
392
  [SAWGameType.Voyager]: SAWGameTypeName.Voyager,
389
393
  [SAWGameType.Plinko]: SAWGameTypeName.Plinko,
390
- [SAWGameType.CoinFlip]: SAWGameTypeName.CoinFlip
394
+ [SAWGameType.CoinFlip]: SAWGameTypeName.CoinFlip,
395
+ [SAWGameType.CustomMinigame]: SAWGameTypeName.CustomMinigame
391
396
  }[type] || SAWGameTypeName.Unknown;
392
397
  };
393
398
 
@@ -2436,7 +2441,7 @@ class WSAPIUser extends WSAPIBase {
2436
2441
  *
2437
2442
  * **Inbox unread count** — `core_inbox_unread_count` on this profile
2438
2443
  * is push-updated in real time (under 1 second). For an inbox badge,
2439
- * prefer this field over {@link getInboxUnreadCount} (which is cached
2444
+ * prefer this field over `getInboxUnreadCount` (which is cached
2440
2445
  * for 30 s) — same value, fresher signal.
2441
2446
  *
2442
2447
  * **Language**: `core_user_language` reflects the server's stored
@@ -2506,15 +2511,75 @@ class WSAPIUser extends WSAPIBase {
2506
2511
  throw new Error('Tracker is not initialized, cannot getUserProfile');
2507
2512
  }
2508
2513
  }
2509
- /** Check if user belongs to specific segments
2510
- * **Example**:
2511
- * ```
2512
- * _smartico.api.checkSegmentMatch(1).then((result) => {
2513
- * console.log(result);
2514
+ /**
2515
+ * Checks whether the current user matches a single segment, returning
2516
+ * `true` / `false` directly. Use this as a decision primitive to gate
2517
+ * UI surfaces (e.g. show a "VIP lounge" tab only when the user is in
2518
+ * the VIP segment), or to drive segment-specific CTA copy.
2519
+ *
2520
+ * Every call is a server round-trip — there is no SDK-side cache. For
2521
+ * checking multiple segments in one round-trip, use
2522
+ * {@link checkSegmentListMatch} instead.
2523
+ *
2524
+ * @remarks
2525
+ * **Refresh model**
2526
+ * - **No client cache.** Every call sends a request; the SDK does not
2527
+ * memoize results.
2528
+ * - **No push event** notifies the consumer when segment membership
2529
+ * flips (the user crosses a qualification threshold, or the
2530
+ * operator changes the segment definition). Membership changes
2531
+ * become visible only on the next `checkSegmentMatch` call.
2532
+ * - **Behavioral segments (configured against BigQuery) have batch
2533
+ * lag** — even if the user's activity has changed (deposit, wager,
2534
+ * etc.), the segment-membership result may not reflect that change
2535
+ * until the next batch evaluation run (typically up to several
2536
+ * hours).
2537
+ *
2538
+ * **Rate limit** (important)
2539
+ * The server caps `checkSegment*` calls at **10 requests per
2540
+ * 60-second window**, with a minimum gap of **~5 seconds between
2541
+ * consecutive calls**. Avoid calling on every render — cache the
2542
+ * result client-side for the session, or until you have a reason to
2543
+ * believe the user's state changed (e.g. after a deposit completes,
2544
+ * a level changes via {@link getUserProfile}'s `props_change`, or a
2545
+ * specific custom event fires).
2546
+ *
2547
+ * **`is_matching: false` is ambiguous on the server** — both "the
2548
+ * segment doesn't exist for this label" and "the user doesn't
2549
+ * qualify" return `false` with no distinguishing signal. Pass IDs
2550
+ * you've verified are valid for your label.
2551
+ *
2552
+ * **Idempotency**: safe. Read-only. No side effects (no analytics
2553
+ * events, no DB writes).
2554
+ *
2555
+ * **Visitor mode**: not supported.
2556
+ *
2557
+ * @param segment_id The segment ID. Segment IDs are label-scoped;
2558
+ * the same numeric ID can refer to different
2559
+ * segments under different labels. Use the IDs
2560
+ * configured for your label.
2561
+ * @returns `true` if the user currently matches the
2562
+ * segment, `false` otherwise. `false` also covers
2563
+ * the "segment doesn't exist for this label"
2564
+ * case — these are not distinguishable.
2565
+ *
2566
+ * @example
2567
+ * ```ts
2568
+ * const isVip = await window._smartico.api.checkSegmentMatch(vipSegmentId);
2569
+ * if (isVip) {
2570
+ * console.log('[smartico] user is in VIP segment — render the VIP-only widget surface');
2571
+ * } else {
2572
+ * console.log('[smartico] user not in VIP segment (or segment id misconfigured) — hide the VIP surface');
2573
+ * }
2574
+ *
2575
+ * // Cache the result for the session; re-check after material user-state changes.
2576
+ * window._smartico.on('props_change', async (changed) => {
2577
+ * if ('ach_points_ever' in changed || 'ach_level_current_id' in changed) {
2578
+ * const fresh = await window._smartico.api.checkSegmentMatch(vipSegmentId);
2579
+ * console.log('[smartico] user state changed — VIP membership now:', fresh);
2580
+ * }
2514
2581
  * });
2515
2582
  * ```
2516
- *
2517
- * **Visitor mode: not supported**
2518
2583
  */
2519
2584
  async checkSegmentMatch(segment_id) {
2520
2585
  const r = await this.api.coreCheckSegments(this.userExtId, [segment_id]);
@@ -2524,14 +2589,67 @@ class WSAPIUser extends WSAPIBase {
2524
2589
  return false;
2525
2590
  }
2526
2591
  }
2527
- /** Check if user belongs to specific list of segments
2528
- * **Example**:
2529
- * ```
2530
- * _smartico.api.checkSegmentListMatch([1, 2, 3]).then((result) => {
2531
- * console.log(result);
2532
- * });
2592
+ /**
2593
+ * Checks the current user's membership in multiple segments in a
2594
+ * single server round-trip — returns a `TSegmentCheckResult[]` with
2595
+ * one entry per *unique* segment ID. Use this instead of looping
2596
+ * {@link checkSegmentMatch} when you need to evaluate several
2597
+ * segments at once (e.g. multi-segment composite gating, A/B-style
2598
+ * cohort detection).
2599
+ *
2600
+ * @remarks
2601
+ * **Response order is NOT guaranteed**
2602
+ * The server returns results in an unspecified order — different
2603
+ * from the input array. Always correlate by the `segment_id` field on
2604
+ * each `TSegmentCheckResult` rather than by array position. The
2605
+ * returned array length may also be smaller than the input array
2606
+ * (duplicate IDs are silently de-duplicated server-side).
2607
+ *
2608
+ * **Single round-trip**
2609
+ * The entire `segment_ids` array is sent in one request. Two segments
2610
+ * cost the same as one in terms of network latency and rate-limit
2611
+ * consumption (one call = one rate-limit slot, regardless of how many
2612
+ * segments).
2613
+ *
2614
+ * **Shared semantics with `checkSegmentMatch`**
2615
+ * Both methods wrap the same underlying server endpoint and share
2616
+ * the same caching, refresh, rate-limit, and ambiguity behavior. See
2617
+ * {@link checkSegmentMatch} for: no client cache · no push event for
2618
+ * membership changes · behavioral segments have batch lag · rate
2619
+ * limit (10 requests / 60 s, ~5 s minimum gap between calls) ·
2620
+ * `is_matching: false` doesn't distinguish "not in segment" from
2621
+ * "segment doesn't exist for this label".
2622
+ *
2623
+ * **Idempotency**: safe. Read-only. No side effects.
2624
+ *
2625
+ * **Visitor mode**: not supported.
2626
+ *
2627
+ * @param segment_ids Array of segment IDs to check. Duplicates are
2628
+ * silently de-duplicated server-side. Segment IDs
2629
+ * are label-scoped — use the IDs configured for
2630
+ * your label.
2631
+ * @returns Array of `{ segment_id, is_matching }` results.
2632
+ * Order is NOT guaranteed; correlate by
2633
+ * `segment_id`. Length may be less than the input
2634
+ * when duplicate IDs were sent.
2635
+ *
2636
+ * @example
2637
+ * ```ts
2638
+ * const segmentIds = [vipSegmentId, newPlayerSegmentId, freeRollSegmentId];
2639
+ * const results = await window._smartico.api.checkSegmentListMatch(segmentIds);
2640
+ *
2641
+ * // Always correlate by segment_id — the response order is unspecified.
2642
+ * const byId = new Map(results.map(r => [r.segment_id, r.is_matching]));
2643
+ *
2644
+ * if (byId.get(vipSegmentId)) {
2645
+ * console.log('[smartico] user is in VIP segment — render VIP surface');
2646
+ * }
2647
+ * if (byId.get(newPlayerSegmentId) && byId.get(freeRollSegmentId)) {
2648
+ * console.log('[smartico] new player AND eligible for free roll — show the welcome promo');
2649
+ * }
2650
+ *
2651
+ * // Cache the result map for the session; refresh on material state changes.
2533
2652
  * ```
2534
- * **Visitor mode: not supported**
2535
2653
  */
2536
2654
  async checkSegmentListMatch(segment_ids) {
2537
2655
  return await this.api.coreCheckSegments(this.userExtId, Array.isArray(segment_ids) ? segment_ids : [segment_ids]);
@@ -2689,7 +2807,7 @@ class WSAPIUser extends WSAPIBase {
2689
2807
  * The SDK exposes raw numeric values; the meaning of each counter
2690
2808
  * (deposit amount, wager amount, lifetime spend, etc.) is fully
2691
2809
  * operator-defined per label. Resolve labels for display via
2692
- * {@link getTranslations} — the operator's display strings are
2810
+ * `getTranslations` — the operator's display strings are
2693
2811
  * stored under the translation keys `levelsCounter1Name` and
2694
2812
  * `levelsCounter2Name`.
2695
2813
  *
@@ -2747,36 +2865,68 @@ class WSAPIUser extends WSAPIBase {
2747
2865
  return OCache.use(onUpdateContextKey.LevelExtraCounters, ECacheContext.WSAPI, () => this.api.getUserGamificationInfoT(this.userExtId), CACHE_DATA_SEC);
2748
2866
  }
2749
2867
  /**
2750
- * Returns the activity log for a user within a specified time range.
2751
- * The response includes both points changes and gems/diamonds changes.
2752
- * Each log entry contains information about the change amount, balance, and source.
2753
- * The returned list is cached for 30 seconds.
2754
- * You can pass the onUpdate callback as a parameter, it will be called every time the activity log is updated and will provide the updated list of activity logs for the last 10 minutes.
2868
+ * Returns the user's unified balance-change history — every points, gems, and
2869
+ * diamonds transaction within the requested time window, ordered newest-first.
2870
+ * Use to power an "Activity" / "History" tab showing wins, claims, purchases,
2871
+ * level-up rewards, and operator adjustments.
2755
2872
  *
2756
- * **Example**:
2757
- * ```
2758
- * const startTime = Math.floor(Date.now() / 1000) - 86400 * 30; // 30 days ago
2759
- * const endTime = Math.floor(Date.now() / 1000); // now
2760
- *
2761
- * _smartico.api.getActivityLog({
2762
- * startTimeSeconds: startTime,
2763
- * endTimeSeconds: endTime,
2764
- * from: 0,
2765
- * to: 50,
2766
- * onUpdate: (data) => console.log('Updated:', data)
2767
- * }).then((result) => {
2768
- * console.log(result);
2769
- * });
2770
- * ```
2873
+ * The returned shape is the same regardless of currency type — `type` (see
2874
+ * {@link UserBalanceType}) distinguishes points / gems / diamonds and
2875
+ * `source_type_id` (see {@link PointChangeSourceType}) names the originating
2876
+ * event.
2771
2877
  *
2772
- * **Visitor mode: not supported**
2878
+ * @remarks
2879
+ * **Preconditions**
2880
+ * - User must be authenticated. Visitor mode is not guarded at the SDK level
2881
+ * but is not meaningful — activity is per-user.
2882
+ *
2883
+ * **Pagination — `from` / `to` are offset + ceiling, not timestamps**
2884
+ * The SDK derives `offset = from`, `limit = min(to - from, 50)` — the server
2885
+ * hard-caps a single response at 50 entries. For infinite scroll, advance
2886
+ * `from` by 50 between calls. Both `startTimeSeconds` and `endTimeSeconds`
2887
+ * are epoch seconds bounding the window the server scans.
2888
+ *
2889
+ * **Subscription model (`onUpdate`)**
2890
+ * The callback fires when the user's `ach_points_balance`,
2891
+ * `ach_gems_balance`, or `ach_diamonds_balance` changes (i.e. whenever a
2892
+ * wallet event lands). The pushed payload is a FIXED re-fetch of the
2893
+ * **last 10 minutes / first 50 entries** — it does NOT honor the original
2894
+ * call's `startTimeSeconds` / `endTimeSeconds` / `from` / `to`. Consumers
2895
+ * maintaining a long historical view must re-call `getActivityLog` with
2896
+ * their own params after receiving an `onUpdate` notification.
2897
+ *
2898
+ * **Refresh**
2899
+ * - The SDK caches results for 30 seconds.
2900
+ * - Push triggers fire only on balance changes; transactions that don't
2901
+ * alter a balance (theoretical zero-amount entries) won't refresh.
2902
+ *
2903
+ * **Visitor mode**: not meaningful (no per-user history available).
2904
+ *
2905
+ * **UI guidance**: see [UI Guide — `getActivityLog`](../../docs/ui/user/UIGuide_getActivityLog.md).
2773
2906
  *
2774
- * @param params - Activity log parameters
2775
- * @param params.startTimeSeconds - Start time in seconds (epoch timestamp)
2776
- * @param params.endTimeSeconds - End time in seconds (epoch timestamp)
2777
- * @param params.from - Start index of records to return
2778
- * @param params.to - End index of records to return
2779
- * @param params.onUpdate - Optional callback function that will be called when the activity log is updated
2907
+ * @returns Array of {@link TActivityLog} ordered newest-first.
2908
+ *
2909
+ * @example
2910
+ * ```ts
2911
+ * const now = Math.floor(Date.now() / 1000);
2912
+ * const start = now - 86400 * 30; // 30 days
2913
+ *
2914
+ * const log = await window._smartico.api.getActivityLog({
2915
+ * startTimeSeconds: start,
2916
+ * endTimeSeconds: now,
2917
+ * from: 0,
2918
+ * to: 50,
2919
+ * onUpdate: (refreshed) => {
2920
+ * console.log('[smartico] wallet changed — refreshed payload is last 10 min / 50 entries:', refreshed.length, 'rows');
2921
+ * // If the consumer is showing a full 30-day view, re-call getActivityLog with the original params here.
2922
+ * },
2923
+ * });
2924
+ *
2925
+ * for (const row of log) {
2926
+ * const sign = row.amount >= 0 ? '+' : '';
2927
+ * console.log('[smartico] activity row — render with', row.type === 0 ? 'points' : row.type === 1 ? 'gems' : 'diamonds', 'icon, color by sign:', sign + row.amount, 'balance after:', row.balance, 'source:', row.source_type_id);
2928
+ * }
2929
+ * ```
2780
2930
  */
2781
2931
  async getActivityLog({
2782
2932
  startTimeSeconds,
@@ -2800,11 +2950,114 @@ class WSAPIUser extends WSAPIBase {
2800
2950
 
2801
2951
  /** @group General */
2802
2952
  class WSAPIGeneral extends WSAPIUser {
2953
+ /**
2954
+ * Returns the operator-configured "custom sections" the current user is eligible
2955
+ * to see. Custom sections are nav-level entries the operator builds in the BO —
2956
+ * HTML pages, themed mission grids, store / raffle / tournament category pages,
2957
+ * lootbox calendars, redirect links, and Liquid-templated dashboards — each
2958
+ * surfaced with its own icon and label.
2959
+ *
2960
+ * Use the response to build the side menu / bottom-bar / nav grid of a
2961
+ * gamification widget. Each section's `section_type_id` (see
2962
+ * {@link AchCustomSectionType}) tells the consumer which page component to mount
2963
+ * when the user opens the section.
2964
+ *
2965
+ * @remarks
2966
+ * **Preconditions**
2967
+ * - No prerequisite calls. Typically fetched once at app boot to build the
2968
+ * navigation skeleton.
2969
+ *
2970
+ * **Server-side eligibility filtering**
2971
+ * Sections are filtered server-side by the user's segments / level / brand and
2972
+ * the section's `active_from_date` / `active_till_date` window. Sections the
2973
+ * user is ineligible for are omitted from the response entirely — there is no
2974
+ * "locked" state to render.
2975
+ *
2976
+ * **Ordering**
2977
+ * Response is server-ordered. Render in the order received; do NOT re-sort
2978
+ * client-side (the operator's order is the intended nav order).
2979
+ *
2980
+ * **Refresh**
2981
+ * - The SDK caches results for 30 seconds.
2982
+ * - No push subscription. Re-call after a segment / level change might
2983
+ * make new sections eligible, or just rely on the 30 s cache turnover.
2984
+ *
2985
+ * **Visitor mode**: supported.
2986
+ *
2987
+ * **UI guidance**: see [UI Guide — `getCustomSections`](../../docs/ui/general/UIGuide_getCustomSections.md).
2988
+ *
2989
+ * @returns Array of {@link TUICustomSection} — empty if the label has no
2990
+ * active custom sections or the user is ineligible for all of them.
2991
+ *
2992
+ * @example
2993
+ * ```ts
2994
+ * const sections = await window._smartico.api.getCustomSections();
2995
+ *
2996
+ * for (const s of sections) {
2997
+ * console.log('[smartico] nav entry', s.menu_name, 'type', s.section_type_id, '→ mount the matching page component');
2998
+ * // Render s.menu_img (64x64) as the nav icon and s.menu_name as the label.
2999
+ * // For section_type_id === AchCustomSectionType.REDIRECT_LINK, do NOT mount a page —
3000
+ * // resolve s.url_or_dp on click (DP string or external URL).
3001
+ * }
3002
+ * ```
3003
+ */
2803
3004
  async getCustomSections() {
2804
3005
  return OCache.use(onUpdateContextKey.CustomSections, ECacheContext.WSAPI, () => this.api.customSectionsGetT(this.userExtId), CACHE_DATA_SEC);
2805
3006
  }
2806
3007
  /**
2807
- * Requests translations for the given language. Returns the object including translation key/translation value pairs. All possible translation keys defined in the back office.
3008
+ * Returns the full set of operator-defined translation key/value pairs for the
3009
+ * label, merged across the requested language and the English baseline.
3010
+ *
3011
+ * Translation keys are free-form strings defined by the operator in the BO. The
3012
+ * SDK consumer treats the result as a flat dictionary: `translations[key]`. If
3013
+ * the key is missing entirely, do NOT trust `undefined` semantics — defensively
3014
+ * fall back to the key string itself or a hardcoded literal.
3015
+ *
3016
+ * @remarks
3017
+ * **Preconditions**
3018
+ * - No prerequisite calls.
3019
+ * - `lang_code` is the ISO language code the operator uses in the BO
3020
+ * (e.g. `"EN"`, `"FR"`, `"ES"`). Case is not normalised — match what the
3021
+ * operator configured.
3022
+ *
3023
+ * **EN fallback**
3024
+ * The SDK issues an English-baseline fetch first, then overlays the
3025
+ * `lang_code` translations on top. Any key the operator has only defined in
3026
+ * English (i.e. missing in the requested language) is returned with the
3027
+ * English value. Keys missing in both are absent from the map — the consumer
3028
+ * must handle missing keys (the default Smartico UI returns the key string
3029
+ * itself as the fallback text).
3030
+ *
3031
+ * **What's included**
3032
+ * The SDK passes an empty `areas` filter to the server, which returns ALL
3033
+ * translation namespaces configured for the label (Gamification, Casino,
3034
+ * Trading, Affiliation, etc. — whichever are populated). For a Gamification-
3035
+ * only widget, the consumer can ignore unused namespaces — keys are flat
3036
+ * strings, not nested.
3037
+ *
3038
+ * **Refresh**
3039
+ * - Cached internally per `(lang_code, label, brand)` for ~30 seconds.
3040
+ * - No push subscription. Operators editing translations in the BO will see
3041
+ * the change after the cache window expires.
3042
+ *
3043
+ * **Visitor mode**: supported. Translations are label-scoped, not user-
3044
+ * scoped — `lang_code` is the sole language selector and is independent of
3045
+ * the `vapi(lang)` argument used elsewhere.
3046
+ *
3047
+ * @param lang_code ISO language code (e.g. `"EN"`, `"FR"`). Defaults to
3048
+ * `"EN"` server-side if empty.
3049
+ * @returns {@link TGetTranslations} — `{ translations: { [key]: string } }`.
3050
+ *
3051
+ * @example
3052
+ * ```ts
3053
+ * const r = await window._smartico.api.getTranslations('FR');
3054
+ *
3055
+ * // Defensive lookup: fall back to the key itself if the operator hasn't
3056
+ * // defined the string.
3057
+ * const t = (k: string) => r.translations[k] ?? k;
3058
+ *
3059
+ * console.log('[smartico] localized label "missions_title" →', t('missions_title'));
3060
+ * ```
2808
3061
  */
2809
3062
  async getTranslations(lang_code) {
2810
3063
  const r = await this.api.getTranslationsT(this.userExtId, lang_code, []);
@@ -2813,21 +3066,60 @@ class WSAPIGeneral extends WSAPIUser {
2813
3066
  };
2814
3067
  }
2815
3068
  /**
2816
- * Returns all the related tournaments and missions for the provided game id for the current user
2817
- * The provided Game ID should correspond to the ID from the Games Catalog - https://help.smartico.ai/welcome/technical-guides/games-catalog-api
3069
+ * Returns the missions and tournaments associated with a casino / sportsbook
3070
+ * game in the operator's games catalog — the reverse of "given this mission,
3071
+ * which games count for it" — so you can surface "play this game to progress
3072
+ * mission X" / "tournament Y features this game" badges on game tiles or
3073
+ * detail screens.
2818
3074
  *
2819
- * **Example**:
2820
- * ```
2821
- * _smartico.api.getRelatedItemsForGame('gold-slot2').then((result) => {
2822
- * console.log(result);
2823
- * });
2824
- * ```
3075
+ * The shape mirrors `getMissions` (`UserAchievement[]`) and `getTournamentsList`
3076
+ * (`Tournament[]`) — same wire types, no reduced subset.
2825
3077
  *
2826
- * **Example in the Visitor mode**:
2827
- * ```
2828
- * _smartico.vapi('EN').getRelatedItemsForGame('gold-slot2').then((result) => {
2829
- * console.log(result);
2830
- * });
3078
+ * @remarks
3079
+ * **Preconditions**
3080
+ * - `related_game_id` must be a Games Catalog ID (the same external string the
3081
+ * operator uses in the catalog at
3082
+ * {@link https://help.smartico.ai/welcome/technical-guides/games-catalog-api}).
3083
+ *
3084
+ * **Server-side eligibility filtering**
3085
+ * Missions and tournaments are filtered by the user's segments / level / brand
3086
+ * before being returned. A user who is ineligible for a mission segment will
3087
+ * NOT see that mission in the result even if it lists the game. The same
3088
+ * filtering applies to tournaments. Treat the response as "what this user
3089
+ * should see" — no client-side eligibility pass needed.
3090
+ *
3091
+ * **No cache**
3092
+ * Every call is a live server round-trip. Missions/tournaments are operator-
3093
+ * mutated state (opt-in, completion, expiry) that changes during a session;
3094
+ * caching staler than the user's last action would mis-render eligibility.
3095
+ * Avoid building a consumer-side cache for UI-critical paths.
3096
+ *
3097
+ * **Refresh**
3098
+ * - Re-call when the user navigates to a new game tile / detail surface.
3099
+ * - Re-call after the user opts into / completes a related mission to refresh
3100
+ * the "play this game" badge.
3101
+ *
3102
+ * **Visitor mode**: supported via `_smartico.vapi(lang).getRelatedItemsForGame(...)`.
3103
+ *
3104
+ * **UI guidance**: see [UI Guide — `getRelatedItemsForGame`](../../docs/ui/general/UIGuide_getRelatedItemsForGame.md).
3105
+ *
3106
+ * @param related_game_id External Games Catalog ID of the game (e.g. `"gold-slot2"`).
3107
+ * @returns {@link GetRelatedAchTourResponse} — `{ achievements, tournaments? }`.
3108
+ * `tournaments` may be `undefined` (treat as empty); both arrays may be empty
3109
+ * when no eligible entities reference the game.
3110
+ *
3111
+ * @example
3112
+ * ```ts
3113
+ * const r = await window._smartico.api.getRelatedItemsForGame('gold-slot2');
3114
+ *
3115
+ * const missions = r.achievements ?? [];
3116
+ * const tournaments = r.tournaments ?? [];
3117
+ *
3118
+ * if (missions.length === 0 && tournaments.length === 0) {
3119
+ * console.log('[smartico] no related missions / tournaments — hide the related badge on the game tile');
3120
+ * } else {
3121
+ * console.log('[smartico] related entities:', missions.length, 'missions /', tournaments.length, 'tournaments — show a "X missions / Y tournaments" badge');
3122
+ * }
2831
3123
  * ```
2832
3124
  */
2833
3125
  async getRelatedItemsForGame(related_game_id) {
@@ -3381,12 +3673,125 @@ class WSAPIMissions extends WSAPIGeneral {
3381
3673
  /** @group Bonuses */
3382
3674
  class WSAPIBonuses extends WSAPIMissions {
3383
3675
  /**
3384
- * Returns all the bonuses for the current user
3385
- * The returned bonuses are cached for 30 seconds. But you can pass the onUpdate callback as a parameter.
3386
- * Note that each time you call getBonuses with a new onUpdate callback, the old one will be overwritten by the new one.
3387
- * The onUpdate callback will be called on bonus claimed and the updated bonuses will be passed to it.
3676
+ * Returns the bonuses awarded to the current user — both pending
3677
+ * (player-claim required) and already-redeemed. Each bonus carries
3678
+ * a `bonus_status_id` ({@link BonusStatus}) describing its lifecycle
3679
+ * stage and an `is_redeemable` flag that signals whether the
3680
+ * consumer must trigger a manual {@link claimBonus} call.
3681
+ *
3682
+ * Bonuses originate from many sources (CRM rules, mission/tournament
3683
+ * rewards, store redemptions, mini-game prizes, manual operator
3684
+ * awards) and all surface through this single list. The consumer
3685
+ * cannot distinguish the source server-side without operator
3686
+ * coordination.
3388
3687
  *
3389
- * **Visitor mode: not supported**
3688
+ * @remarks
3689
+ * **Subscription model (`onUpdate`)**
3690
+ * The callback receives the FULL refreshed bonuses list (never a
3691
+ * diff/patch). Each subsequent call to `getBonuses({ onUpdate })`
3692
+ * REPLACES the prior callback. Pass `onUpdate: undefined` (or omit
3693
+ * it) to keep the prior callback in place; the callback is never
3694
+ * auto-cleared.
3695
+ *
3696
+ * **Update triggers** — the callback fires after every
3697
+ * {@link claimBonus} call resolves on this connection (success OR
3698
+ * failure). This is the ONLY trigger.
3699
+ *
3700
+ * Does NOT fire when: a new bonus is awarded server-side
3701
+ * (CRM rule, mission completion, store purchase, etc.), a bonus
3702
+ * expires, or an operator manually issues / cancels a bonus. Those
3703
+ * changes surface only on the next cache miss (after the 30 s TTL).
3704
+ * Re-call `getBonuses()` manually if your UI needs near-live state
3705
+ * for server-driven awards.
3706
+ *
3707
+ * **Reading state from the returned bonus**
3708
+ * Drive UI bucketing from `bonus_status_id` (enum {@link BonusStatus}):
3709
+ *
3710
+ * - `COUPON_ISSUED` (2) — actionable; player must claim. Pair with
3711
+ * `is_redeemable === true` to gate the Claim button.
3712
+ * - `REDEEM_FAILED` (4) — also actionable; the previous claim
3713
+ * attempt failed but the bonus is still valid. Surface a retry CTA
3714
+ * — the default Smartico UI treats this identically to
3715
+ * `COUPON_ISSUED` from the player's perspective.
3716
+ * - `REDEEMED` (3) — historical; show in a "claimed" tab.
3717
+ * - `New` (1), `COUPON_ISSUE_FAILED` (5), `EXPIRED` (6) — these
3718
+ * should not normally reach the client and are typically filtered
3719
+ * out by operator-side widget configuration; if they do appear,
3720
+ * they have no player-actionable state.
3721
+ *
3722
+ * `is_redeemable` is the authoritative gate for showing the Claim
3723
+ * button — it's `true` only when the bonus is in a claimable status
3724
+ * AND the operator's integration uses a coupon model (where the
3725
+ * player must explicitly trigger delivery). Auto-redeemed bonuses
3726
+ * arrive already in `REDEEMED` state with `is_redeemable: false`.
3727
+ *
3728
+ * **Field hierarchy — display amount**
3729
+ * Bonuses carry both template-level (`label_bonus_template_meta_map.description`)
3730
+ * and instance-level (`bonus_meta_map.uiAmount`) display strings.
3731
+ * Prefer `bonus_meta_map.uiAmount` when present — it's the dynamic
3732
+ * amount computed at issuance time (e.g. from a dynamic-bonus
3733
+ * formula). Fall back to `label_bonus_template_meta_map.description`
3734
+ * (which can include HTML; sanitize before injecting).
3735
+ *
3736
+ * **Date format**
3737
+ * `create_date` and `redeem_date` are ISO 8601 UTC strings WITHOUT
3738
+ * a timezone suffix (`"YYYY-MM-DDTHH:MM:SS"`). Parse as UTC and
3739
+ * display in the user's local timezone. `redeem_date` is absent
3740
+ * until the bonus reaches `REDEEMED` status.
3741
+ *
3742
+ * **Cache TTL**: the SDK caches the response for 30 seconds. Cache
3743
+ * is fully cleared on login / logout.
3744
+ *
3745
+ * **Idempotency / Side effects**: safe. Read-only.
3746
+ *
3747
+ * **UI guidance**: see [UI Guide — `getBonuses`](../../docs/ui/bonuses/UIGuide_getBonuses.md).
3748
+ *
3749
+ * **Visitor mode**: not supported.
3750
+ *
3751
+ * @param params Optional. Omit to fetch without subscribing.
3752
+ * @param params.onUpdate Callback invoked with the full refreshed
3753
+ * bonuses list after every
3754
+ * {@link claimBonus} resolves. Each call
3755
+ * to `getBonuses` overwrites the prior
3756
+ * callback.
3757
+ * @returns Promise resolving to the bonuses list.
3758
+ * Empty array if no bonuses are visible.
3759
+ *
3760
+ * @example
3761
+ * ```ts
3762
+ * const bonuses = await window._smartico.api.getBonuses({
3763
+ * onUpdate: (refreshed) => {
3764
+ * console.log('[smartico] bonuses refreshed (after claimBonus) — re-render the bonuses UI from this array:', refreshed);
3765
+ * },
3766
+ * });
3767
+ *
3768
+ * // Bucket into pending vs redeemed tabs. The default Smartico UI groups
3769
+ * // COUPON_ISSUED + REDEEM_FAILED into the same "Pending" tab.
3770
+ * const pending = bonuses.filter(b =>
3771
+ * b.bonus_status_id === 2 || b.bonus_status_id === 4 // COUPON_ISSUED or REDEEM_FAILED
3772
+ * );
3773
+ * const redeemed = bonuses.filter(b => b.bonus_status_id === 3); // REDEEMED
3774
+ *
3775
+ * console.log('[smartico] render bonus tabs: pending=', pending.length, 'redeemed=', redeemed.length);
3776
+ *
3777
+ * // Render each pending bonus card; show Claim button only when redeemable.
3778
+ * for (const b of pending) {
3779
+ * const amount = b.bonus_meta_map?.uiAmount || b.label_bonus_template_meta_map?.description;
3780
+ * if (b.is_redeemable) {
3781
+ * console.log('[smartico] render Claim button for bonus', b.bonus_id, '— amount:', amount);
3782
+ * } else {
3783
+ * console.log('[smartico] render bonus as informational (no Claim button) — amount:', amount);
3784
+ * }
3785
+ * }
3786
+ *
3787
+ * // Server-awarded bonuses (e.g. from a CRM campaign) do NOT push;
3788
+ * // poll if you need near-live awareness.
3789
+ * setInterval(async () => {
3790
+ * const fresh = await window._smartico.api.getBonuses();
3791
+ * const newCount = fresh.filter(b => b.bonus_status_id === 2 || b.bonus_status_id === 4).length;
3792
+ * console.log('[smartico] poll tick — pending bonuses now:', newCount);
3793
+ * }, 30_000);
3794
+ * ```
3390
3795
  */
3391
3796
  async getBonuses({
3392
3797
  onUpdate
@@ -3397,12 +3802,113 @@ class WSAPIBonuses extends WSAPIMissions {
3397
3802
  return OCache.use(onUpdateContextKey.Bonuses, ECacheContext.WSAPI, () => this.api.bonusesGetItemsT(this.userExtId), CACHE_DATA_SEC);
3398
3803
  }
3399
3804
  /**
3400
- * Claim the bonus by bonus_id. Returns the err_code in case of success or error.
3401
- * Note that this method can be used only on integrations where originally failed bonus can be claimed again.
3402
- * For example, user won a bonus in the mini-game, but Operator rejected this bonus.
3403
- * This bonus will be available for the user to claim again.
3805
+ * Triggers manual redemption of a coupon-model bonus the user has
3806
+ * been awarded. For bonuses with `is_redeemable === true` returned
3807
+ * by {@link getBonuses}, this is the call that asks the operator's
3808
+ * integration to actually credit the bonus to the player's account.
3809
+ * Bonuses on integrations that auto-redeem arrive already in
3810
+ * `REDEEMED` state and do not require this call.
3404
3811
  *
3405
- * **Visitor mode: not supported**
3812
+ * Use this for `bonus_status_id` of `COUPON_ISSUED` (2) as the
3813
+ * first-time claim, and for `REDEEM_FAILED` (4) as a retry after a
3814
+ * prior unsuccessful attempt.
3815
+ *
3816
+ * @remarks
3817
+ * **Preconditions**
3818
+ * Read the candidate bonus from {@link getBonuses} and gate the
3819
+ * call on `is_redeemable === true`. The SDK forwards the request
3820
+ * unconditionally — calling on a non-claimable bonus returns
3821
+ * `err_code = -1`.
3822
+ *
3823
+ * **Error codes** (in `err_code`)
3824
+ *
3825
+ * The SDK currently types `err_code` as {@link SAWSpinErrorCode} for
3826
+ * historical reasons, but the values returned by this method come
3827
+ * from the server's general error space and are NOT mini-game codes.
3828
+ * Branch on the numeric values:
3829
+ *
3830
+ * - `0` — success; the bonus has been redeemed and the operator's
3831
+ * integration has credited the player's account.
3832
+ * - `-1` — bonus not claimable: not found, already claimed by
3833
+ * another session, or its status is no longer `COUPON_ISSUED` /
3834
+ * `REDEEM_FAILED`. Treat as idempotent — refresh
3835
+ * {@link getBonuses} and reconcile.
3836
+ * - `1` — generic server error. Transient; allow retry after a brief
3837
+ * delay.
3838
+ * - `9999` — uncaught server exception. Treat as a transient failure;
3839
+ * surface a generic error and allow retry.
3840
+ * - other non-zero — generic server error. Surface `err_message` if
3841
+ * any.
3842
+ *
3843
+ * **`success` field is unreliable**: the wire response does not
3844
+ * carry a `success` boolean — the field on `TClaimBonusResult` will
3845
+ * be `undefined` in practice. Always branch on `err_code === 0`
3846
+ * for the success check.
3847
+ *
3848
+ * **Refresh after success (and after failure)**
3849
+ * The SDK automatically refreshes the bonuses cache on every
3850
+ * response (success OR failure) and fires any `onUpdate` callback
3851
+ * registered via {@link getBonuses}'s `onUpdate`. After
3852
+ * `err_code === 0`, the redeemed bonus shifts from
3853
+ * `COUPON_ISSUED` / `REDEEM_FAILED` to `REDEEMED` on the refreshed
3854
+ * array, with `redeem_date` populated. After a failure, the bonus
3855
+ * typically lands in `REDEEM_FAILED` and remains claimable for a
3856
+ * retry.
3857
+ *
3858
+ * **Idempotency**: NOT idempotent at the SDK level. A second call
3859
+ * returns `err_code = -1` once the first has succeeded — treat that
3860
+ * as success in the UI rather than as a real error. Guard the call
3861
+ * site against double-clicks (set a local "claiming" flag on click,
3862
+ * clear it on response).
3863
+ *
3864
+ * **Side effects** (on `err_code === 0`)
3865
+ * - Bonus status transitions to `REDEEMED`; `redeem_date` is set.
3866
+ * - The operator's integration credits the player's account
3867
+ * externally. The SDK does not observe that credit directly —
3868
+ * balance updates flow over the user-properties channel from the
3869
+ * operator's side, not from this response.
3870
+ * - Server-side analytics events fire downstream (not directly
3871
+ * observable from the SDK).
3872
+ *
3873
+ * **UI guidance**: see [UI Guide — `claimBonus`](../../docs/ui/bonuses/UIGuide_claimBonus.md).
3874
+ *
3875
+ * **Visitor mode**: not supported.
3876
+ *
3877
+ * @param bonus_id The `bonus_id` from a `TBonus` returned by
3878
+ * {@link getBonuses}.
3879
+ * @returns `{ err_code, err_message, success? }`; success when
3880
+ * `err_code === 0` (or `err_code === -1` when treated as
3881
+ * idempotent no-op). The `success` field is unreliable —
3882
+ * ignore it.
3883
+ *
3884
+ * @example
3885
+ * ```ts
3886
+ * const bonuses = await window._smartico.api.getBonuses({
3887
+ * onUpdate: (refreshed) => console.log('[smartico] bonuses refreshed — re-render from this array', refreshed),
3888
+ * });
3889
+ * const bonus = bonuses.find(b => b.bonus_id === bonusId);
3890
+ *
3891
+ * if (!bonus) {
3892
+ * console.log('[smartico] bonus no longer visible — refresh list and hide CTA');
3893
+ * return;
3894
+ * }
3895
+ * if (!bonus.is_redeemable) {
3896
+ * console.log('[smartico] bonus is not claimable (auto-redeemed or already finalised) — keep CTA hidden');
3897
+ * return;
3898
+ * }
3899
+ *
3900
+ * console.log('[smartico] claim starting — set in-flight flag, show loading dots on the Claim button, keep the modal open');
3901
+ * const r = await window._smartico.api.claimBonus(bonus.bonus_id);
3902
+ * console.log('[smartico] claim response received — clear in-flight flag');
3903
+ *
3904
+ * if (r.err_code === 0 || r.err_code === -1) {
3905
+ * console.log('[smartico] claim succeeded (or was already done) — show a success toast; the getBonuses onUpdate above will fire with the refreshed list');
3906
+ * } else if (r.err_code === 9999) {
3907
+ * console.error('[smartico] uncaught server exception — show a generic error toast and allow retry');
3908
+ * } else {
3909
+ * console.error('[smartico] claim failed — surface this error message to the user; the bonus will move to REDEEM_FAILED state and remain claimable for a retry:', r.err_message);
3910
+ * }
3911
+ * ```
3406
3912
  */
3407
3913
  async claimBonus(bonus_id) {
3408
3914
  const r = await this.api.bonusClaimItem(this.userExtId, bonus_id);
@@ -4481,7 +4987,7 @@ class WSAPITournaments extends WSAPIStore {
4481
4987
  *
4482
4988
  * For clan tournaments (`is_clan_based === true`), the user must
4483
4989
  * already belong to a clan before calling. If they don't, prompt them
4484
- * to pick a clan first via {@link joinClan}, then call
4990
+ * to pick a clan first via `joinClan`, then call
4485
4991
  * `registerInTournament` after the clan-join resolves. See "Error
4486
4992
  * codes" `1010` below for the server-side safety net.
4487
4993
  *
@@ -4490,8 +4996,8 @@ class WSAPITournaments extends WSAPIStore {
4490
4996
  * tournament lobby refresh fires.
4491
4997
  * - `1010` — `TOURNAMENT_USER_CANNOT_JOIN_WITHOUT_CLAN`: clan-based
4492
4998
  * tournament and the user has no clan. Surface the clan-pick flow
4493
- * (e.g. open a clan-pick modal driven by {@link getClans} +
4494
- * {@link joinClan}), then retry registration.
4999
+ * (e.g. open a clan-pick modal driven by `getClans` +
5000
+ * `joinClan`), then retry registration.
4495
5001
  * - `30001` — `TOURNAMENT_INSTANCE_NOT_FOUND`: instance ID is invalid
4496
5002
  * or the tournament was deleted. Refresh the lobby via
4497
5003
  * `getTournamentsList()` and hide the entry from the UI.
@@ -5049,23 +5555,81 @@ class WSAPIJackpots extends WSAPIClans {
5049
5555
  OCache.clear(ECacheContext.WSAPI, onUpdateContextKey.Pots);
5050
5556
  OCache.clear(ECacheContext.WSAPI, onUpdateContextKey.JackpotWinners);
5051
5557
  }
5052
- /** Returns list of Jackpots that are active in the system and matching to the filter definition.
5053
- * If filter is not provided, all active jackpots will be returned.
5054
- * Filter can be used to get jackpots related to specific game or specific jackpot template.
5055
- * You can call this method every second in order to get up to date information about current value of the jackpot(s) and present them to the end-users
5558
+ /**
5559
+ * Returns the user-eligible jackpots active for the label, fused with their live
5560
+ * pot values. Each entry carries the static template (name, description, opt-in
5561
+ * state, eligible games, currency) plus a freshly-fetched `pot` snapshot.
5056
5562
  *
5057
- * **Example**:
5058
- * ```
5059
- * _smartico.api.jackpotGet({ related_game_id: 'wooko-slot' }).then((result) => {
5060
- * console.log(result);
5061
- * });
5062
- * ```
5563
+ * Designed to be polled at high frequency (every 1 second is fine) — the SDK
5564
+ * splits the response into a 30-second template cache and a 1-second pot-value
5565
+ * cache so consumers can render a live-counting pot amount without hammering
5566
+ * the server for the static template data.
5063
5567
  *
5064
- * **Example in the Visitor mode**:
5065
- * ```
5066
- * _smartico.vapi('EN').jackpotGet({ related_game_id: 'wooko-slot' }).then((result) => {
5067
- * console.log(result);
5068
- * });
5568
+ * @remarks
5569
+ * **Preconditions**
5570
+ * - No prerequisite calls.
5571
+ * - Visitor mode supported via `_smartico.vapi(lang).jackpotGet(...)`.
5572
+ *
5573
+ * **Server-side eligibility filtering**
5574
+ * Jackpots are filtered server-side by the user's segments before being
5575
+ * returned. Templates the user is ineligible for never appear in this
5576
+ * response — there is no "locked" state to render. An empty result for a
5577
+ * known-good user typically means none of the configured templates match
5578
+ * the user's segments / level / brand.
5579
+ *
5580
+ * **Filter behavior**
5581
+ * Passing `related_game_id` returns only jackpots linked to that game (from
5582
+ * the operator's games catalog). Passing `jp_template_id` returns just that
5583
+ * single template. With no filter, all active templates the user is
5584
+ * eligible for are returned. Changing the filter between calls flushes both
5585
+ * the template cache and the pot cache.
5586
+ *
5587
+ * **Two-tier cache**
5588
+ * - Template definitions: 30 s cache. Re-fetched on filter change or when
5589
+ * opt-in / opt-out / a jackpot-win push lands.
5590
+ * - Pot values: 1 s cache. Re-fetched on every call past the 1-second
5591
+ * window so a 1 Hz poll renders a smoothly counting pot.
5592
+ *
5593
+ * **`jp_public_meta.custom_data`**
5594
+ * Server returns this as a JSON-encoded string; the SDK parses it inline so
5595
+ * consumers always receive either a parsed object or the raw string.
5596
+ *
5597
+ * **Personal vs MultiUser jackpots** (see {@link JackpotType})
5598
+ * - `MultiUser` (1) — shared pot; `registration_count` reflects real opted-in
5599
+ * user count; temperature transitions fire push events.
5600
+ * - `Personal` (2) — independent per-user pot; `registration_count` is
5601
+ * always 1; temperature push events are suppressed.
5602
+ *
5603
+ * **Refresh**
5604
+ * - `jp_status_id`, segment changes, and operator template edits surface
5605
+ * after the 30 s template cache TTL.
5606
+ * - Opt-in / opt-out / jackpot-win push events all clear the template and
5607
+ * pot caches — the next call re-fetches.
5608
+ *
5609
+ * **Visitor mode**: supported.
5610
+ *
5611
+ * **UI guidance**: see [UI Guide — `jackpotGet`](../../docs/ui/jackpots/UIGuide_jackpotGet.md).
5612
+ *
5613
+ * @param filter Optional filter narrowing the result set.
5614
+ * @param filter.related_game_id Limit to jackpots linked to this game ID
5615
+ * (from the operator's games catalog).
5616
+ * @param filter.jp_template_id Limit to a single template.
5617
+ * @returns Array of {@link JackpotDetails} — empty if the user is eligible
5618
+ * for no active jackpots.
5619
+ *
5620
+ * @example
5621
+ * ```ts
5622
+ * // Poll every second for live pot counter
5623
+ * setInterval(async () => {
5624
+ * const jackpots = await window._smartico.api.jackpotGet();
5625
+ * for (const jp of jackpots) {
5626
+ * console.log('[smartico] update pot widget for', jp.jp_public_meta.name, '— amount:', jp.pot.current_pot_amount_user_currency, jp.user_currency, '— temperature:', jp.pot.current_pot_temperature);
5627
+ * }
5628
+ * }, 1000);
5629
+ *
5630
+ * // Filtered fetch for a specific game tile
5631
+ * const linked = await window._smartico.api.jackpotGet({ related_game_id: 'gold-slot2' });
5632
+ * console.log('[smartico] linked jackpots for game:', linked.length);
5069
5633
  * ```
5070
5634
  */
5071
5635
  async jackpotGet(filter) {
@@ -5102,18 +5666,68 @@ class WSAPIJackpots extends WSAPIClans {
5102
5666
  });
5103
5667
  }
5104
5668
  /**
5105
- * Opt-in currently logged in user to the jackpot with the specified jp_template_id.
5106
- * You may call jackpotGet method after doing optin to see that user is opted in to the jackpot.
5669
+ * Opts the authenticated user into a jackpot. From this point on, the user
5670
+ * contributes to the pot (per the template's `contribution_type` and
5671
+ * `contribution_value`) and is eligible to win when the pot explodes.
5107
5672
  *
5108
- * **Example**:
5109
- * ```
5110
- * _smartico.api.jackpotOptIn({ jp_template_id: 123 }).then((result) => {
5111
- * console.log('Opted in to the jackpot');
5112
- * });
5113
- * ```
5673
+ * On success the SDK clears the jackpot caches — the next
5674
+ * {@link jackpotGet} call returns the updated `is_opted_in: true` and an
5675
+ * incremented `registration_count`.
5114
5676
  *
5115
- * **Visitor mode: not supported**
5677
+ * @remarks
5678
+ * **Preconditions**
5679
+ * - User must be authenticated. Visitor mode not supported.
5680
+ * - `jp_template_id` is mandatory — the SDK throws synchronously if missing.
5681
+ * - The jackpot must currently be active and the user must be in its
5682
+ * eligibility segment. Ineligible jackpots are filtered out of
5683
+ * {@link jackpotGet} entirely, so a card the user sees is opt-in-able.
5684
+ *
5685
+ * **Error handling**
5686
+ * The server returns a non-zero `errCode` with a human-readable `errMsg`
5687
+ * for any failure. Common causes:
5688
+ * - User is in a control group.
5689
+ * - Jackpot template is not active (status changed since the user opened
5690
+ * the card).
5691
+ * - User is already opted in (the SDK has no client-side guard against a
5692
+ * double-tap; the server enforces).
5693
+ * - DB race / generic server error.
5694
+ *
5695
+ * The SDK does NOT enumerate distinct numeric codes for each cause — branch
5696
+ * on `errCode === 0` for success and surface `errMsg` as-is for failures.
5697
+ *
5698
+ * **Idempotency / re-opt-in semantics**
5699
+ * - **Already opted in** → returns non-zero `errCode` ("User already opted in").
5700
+ * - **Previously opted in then opted out** → succeeds silently; the server
5701
+ * re-activates the registration. This is not an error path — a user can
5702
+ * freely cycle opt-in / opt-out.
5703
+ *
5704
+ * **Refresh after success**
5705
+ * - The SDK clears the template, pot, and winners caches.
5706
+ * - The next {@link jackpotGet} call returns `is_opted_in: true` for this
5707
+ * template.
5708
+ *
5709
+ * **Side effects**
5710
+ * Fires a `JACKPOT_OPT_IN` engagement event (visible to operator-side
5711
+ * automation rules). Does NOT immediately move points / gems — contributions
5712
+ * happen per-bet via the user's gameplay, not on opt-in.
5713
+ *
5714
+ * **Visitor mode**: not supported.
5715
+ *
5716
+ * **UI guidance**: see [UI Guide — `jackpotOptIn`](../../docs/ui/jackpots/UIGuide_jackpotOptIn.md).
5116
5717
  *
5718
+ * @param filter.jp_template_id ID of the jackpot template to join.
5719
+ * @returns {@link JackpotsOptinResponse} — `errCode === 0` on success.
5720
+ *
5721
+ * @example
5722
+ * ```ts
5723
+ * const r = await window._smartico.api.jackpotOptIn({ jp_template_id: 42 });
5724
+ *
5725
+ * if (r.errCode === 0) {
5726
+ * console.log('[smartico] opted in — refresh jackpot card to show opt-out CTA');
5727
+ * } else {
5728
+ * console.error('[smartico] opt-in failed — show this message in an error toast:', r.errMsg);
5729
+ * }
5730
+ * ```
5117
5731
  */
5118
5732
  async jackpotOptIn(filter) {
5119
5733
  if (!filter.jp_template_id) {
@@ -5123,18 +5737,63 @@ class WSAPIJackpots extends WSAPIClans {
5123
5737
  return result;
5124
5738
  }
5125
5739
  /**
5126
- * Opt-out currently logged in user from the jackpot with the specified jp_template_id.
5127
- * You may call jackpotGet method after doing optout to see that user is not opted in to the jackpot.
5740
+ * Opts the authenticated user out of a jackpot. From this point on the user
5741
+ * no longer contributes to the pot and is not eligible to win.
5128
5742
  *
5129
- * **Example**:
5130
- * ```
5131
- * _smartico.api.jackpotOptOut({ jp_template_id: 123 }).then((result) => {
5132
- * console.log('Opted out from the jackpot');
5133
- * });
5134
- * ```
5743
+ * The user can re-join later via {@link jackpotOptIn} — opt-out is not
5744
+ * permanent.
5135
5745
  *
5136
- * **Visitor mode: not supported**
5746
+ * @remarks
5747
+ * **Preconditions**
5748
+ * - User must be authenticated. Visitor mode not supported.
5749
+ * - `jp_template_id` is mandatory — the SDK throws synchronously if missing.
5750
+ *
5751
+ * **Error handling**
5752
+ * The server returns a non-zero `errCode` with a human-readable `errMsg` for
5753
+ * any failure. Common causes:
5754
+ * - User has never opted in OR is already opted out — server returns
5755
+ * non-zero `errCode` for the "already opted out" case. Calling opt-out
5756
+ * on a jackpot the user has never registered for typically returns
5757
+ * success (the server finds no registration row to update).
5758
+ * - DB race / generic server error.
5759
+ *
5760
+ * Unlike {@link jackpotOptIn}, opt-out does NOT check the template's status
5761
+ * — the user can opt out of a jackpot that has been deactivated by the
5762
+ * operator.
5763
+ *
5764
+ * **Idempotency**
5765
+ * - First opt-out → success.
5766
+ * - Second opt-out → non-zero `errCode` ("Already opted out").
5767
+ * - Opt-out of a never-joined jackpot → typically succeeds silently
5768
+ * (no registration row to act on).
5769
+ *
5770
+ * **Refresh after success**
5771
+ * - The SDK clears the template, pot, and winners caches.
5772
+ * - The next {@link jackpotGet} call returns `is_opted_in: false` for this
5773
+ * template and a decremented `registration_count`.
5137
5774
  *
5775
+ * **Side effects**
5776
+ * Fires a `JACKPOT_OPT_OUT` engagement event for operator-side automation
5777
+ * rules. The user's existing contributions to the current pot remain in
5778
+ * place — opt-out stops future contributions but does not refund past ones.
5779
+ *
5780
+ * **Visitor mode**: not supported.
5781
+ *
5782
+ * **UI guidance**: see [UI Guide — `jackpotOptOut`](../../docs/ui/jackpots/UIGuide_jackpotOptOut.md).
5783
+ *
5784
+ * @param filter.jp_template_id ID of the jackpot template to leave.
5785
+ * @returns {@link JackpotsOptoutResponse} — `errCode === 0` on success.
5786
+ *
5787
+ * @example
5788
+ * ```ts
5789
+ * const r = await window._smartico.api.jackpotOptOut({ jp_template_id: 42 });
5790
+ *
5791
+ * if (r.errCode === 0) {
5792
+ * console.log('[smartico] opted out — refresh jackpot card to show join CTA');
5793
+ * } else {
5794
+ * console.error('[smartico] opt-out failed — show this message in an error toast:', r.errMsg);
5795
+ * }
5796
+ * ```
5138
5797
  */
5139
5798
  async jackpotOptOut(filter) {
5140
5799
  if (!filter.jp_template_id) {
@@ -5144,25 +5803,64 @@ class WSAPIJackpots extends WSAPIClans {
5144
5803
  return result;
5145
5804
  }
5146
5805
  /**
5147
- * Returns jackpot winners for the given `jp_template_id` (paginated on the server).
5148
- * Default page size on the wire is 20; use `limit`, `offset`, and repeated calls to load more.
5149
- * The full protocol response also includes `has_more`; this method returns only the `winners` array.
5806
+ * Returns past winners of a specific jackpot template, paginated. Use to
5807
+ * power a "Recent winners" panel inside the jackpot detail modal.
5150
5808
  *
5151
- * **Example**:
5152
- * ```
5153
- * _smartico.api.getJackpotWinners({
5154
- * jp_template_id: 123,
5155
- * }).then((result) => {
5156
- * console.log(result);
5157
- * });
5158
- * ```
5809
+ * **Important**: respect the `expose_winners_over_api` flag on the jackpot's
5810
+ * {@link JackpotDetails} before calling this method. The server-side handler
5811
+ * does NOT enforce that flag — it is a UI-only gate. Custom UIs that ignore
5812
+ * the flag will display winners the operator intended to keep private.
5159
5813
  *
5160
- * **Visitor mode: not supported**
5814
+ * @remarks
5815
+ * **Preconditions**
5816
+ * - User must be authenticated. Visitor mode not supported.
5817
+ * - `jp_template_id` is mandatory — the SDK throws synchronously if missing.
5818
+ * - The consumer SHOULD check
5819
+ * {@link JackpotDetails.expose_winners_over_api} before calling, since
5820
+ * the server does not enforce it.
5821
+ *
5822
+ * **Pagination**
5823
+ * Server default page size is 20. Pass `limit` and `offset` to paginate
5824
+ * (e.g. infinite-scroll). The wire response carries a `has_more` flag, but
5825
+ * this SDK method returns only the `winners` array — detect end-of-list by
5826
+ * a returned array shorter than `limit`.
5827
+ *
5828
+ * **Currency caveat**
5829
+ * Winner amounts in the response (`winner.winning_amount_jp_currency`) are
5830
+ * in the jackpot's NATIVE currency, NOT the user's wallet currency. If a
5831
+ * user-currency display is needed, the consumer must convert client-side.
5832
+ *
5833
+ * **Refresh**
5834
+ * - The SDK caches per `jp_template_id` for 30 seconds.
5835
+ * - Caches clear on jackpot-win push events and on opt-in / opt-out.
5836
+ *
5837
+ * **Error handling**
5838
+ * Non-zero `errCode` on control-group users or generic server errors. The
5839
+ * SDK does NOT enumerate distinct codes — branch on `errCode === 0` and
5840
+ * surface `errMsg` on failure.
5841
+ *
5842
+ * **Visitor mode**: not supported.
5843
+ *
5844
+ * **UI guidance**: see [UI Guide — `getJackpotWinners`](../../docs/ui/jackpots/UIGuide_getJackpotWinners.md).
5845
+ *
5846
+ * @returns Array of {@link JackpotWinnerHistory} entries newest-first.
5847
+ *
5848
+ * @example
5849
+ * ```ts
5850
+ * const [jp] = await window._smartico.api.jackpotGet({ jp_template_id: 42 });
5851
+ *
5852
+ * if (!jp || !jp.expose_winners_over_api) {
5853
+ * console.log('[smartico] winners hidden by operator config — hide the winners tab');
5854
+ * return;
5855
+ * }
5161
5856
  *
5162
- * @param params - Jackpot winners parameters
5163
- * @param params.jp_template_id - Jackpot template id (required; throws if missing)
5164
- * @param params.limit - Page size (server default 20 when omitted)
5165
- * @param params.offset - Offset into the winner list
5857
+ * const winners = await window._smartico.api.getJackpotWinners({
5858
+ * jp_template_id: 42,
5859
+ * limit: 20,
5860
+ * offset: 0,
5861
+ * });
5862
+ * console.log('[smartico] render', winners.length, 'recent winner rows (amounts in', jp.jp_currency + ')');
5863
+ * ```
5166
5864
  */
5167
5865
  async getJackpotWinners({
5168
5866
  limit,
@@ -5172,29 +5870,67 @@ class WSAPIJackpots extends WSAPIClans {
5172
5870
  return OCache.use(onUpdateContextKey.JackpotWinners + jp_template_id, ECacheContext.WSAPI, () => this.api.getJackpotWinnersT(this.userExtId, limit, offset, jp_template_id), JACKPOT_WINNERS_CACHE_SEC);
5173
5871
  }
5174
5872
  /**
5175
- * Returns the eligible games for the jackpot with the specified jp_template_id.
5873
+ * Returns the casino games eligible to contribute to a specific jackpot —
5874
+ * the catalog the user must play to grow the pot. Use to power an
5875
+ * "Eligible games" carousel inside the jackpot detail modal.
5176
5876
  *
5177
- * **Example**:
5178
- * ```
5179
- * _smartico.api.getJackpotEligibleGames({ jp_template_id: 123 }).then((result) => {
5180
- * console.log(result);
5181
- * });
5182
- * ```
5877
+ * **Shortcut**: when the jackpot's
5878
+ * {@link JackpotDetails.ach_related_game_allow_all} flag is `true`, every
5879
+ * game in the operator catalog contributes — there's no point calling this
5880
+ * method (the result would be the full catalog). The default Smartico UI
5881
+ * hides the Eligible Games tab entirely in that case.
5183
5882
  *
5184
- * **Visitor mode: not supported**
5883
+ * @remarks
5884
+ * **Preconditions**
5885
+ * - User must be authenticated. Visitor mode not supported.
5886
+ * - `jp_template_id` is mandatory.
5185
5887
  *
5186
- */
5187
- async getJackpotEligibleGames({
5188
- jp_template_id,
5189
- onUpdate
5190
- }) {
5191
- if (onUpdate) {
5192
- this.onUpdateCallback.set(onUpdateContextKey.JackpotEligibleGames, onUpdate);
5193
- }
5194
- return OCache.use(onUpdateContextKey.JackpotEligibleGames + jp_template_id, ECacheContext.WSAPI, () => this.api.getJackpotEligibleGamesT(this.userExtId, {
5195
- jp_template_id
5196
- }), JACKPOT_ELIGIBLE_GAMES_CACHE_SEC);
5197
- }
5888
+ * **`onUpdate` caveat**
5889
+ * The `onUpdate` callback is accepted to match the SDK's subscription
5890
+ * contract but is NOT auto-invoked by any current server push. It will only
5891
+ * fire if a consumer (or a future SDK version) explicitly pushes an update.
5892
+ * Treat it as a no-op for now.
5893
+ *
5894
+ * **Refresh**
5895
+ * - The SDK caches per `jp_template_id` for 30 seconds.
5896
+ * - Cache does NOT clear on opt-in / opt-out / jackpot-win — eligible-game
5897
+ * lists rarely change during a session. Wait for the 30 s TTL.
5898
+ *
5899
+ * **Error handling**
5900
+ * Non-zero `errCode` on control-group users or generic server errors.
5901
+ * Branch on `errCode === 0` and surface `errMsg` on failure.
5902
+ *
5903
+ * **Visitor mode**: not supported.
5904
+ *
5905
+ * **UI guidance**: see [UI Guide — `getJackpotEligibleGames`](../../docs/ui/jackpots/UIGuide_getJackpotEligibleGames.md).
5906
+ *
5907
+ * @returns {@link TGetJackpotEligibleGamesResponse} —
5908
+ * `{ eligible_games: JackpotEligibleGame[] }`.
5909
+ *
5910
+ * @example
5911
+ * ```ts
5912
+ * const [jp] = await window._smartico.api.jackpotGet({ jp_template_id: 42 });
5913
+ *
5914
+ * if (jp?.ach_related_game_allow_all) {
5915
+ * console.log('[smartico] all games eligible — hide the eligible-games tab');
5916
+ * return;
5917
+ * }
5918
+ *
5919
+ * const r = await window._smartico.api.getJackpotEligibleGames({ jp_template_id: 42 });
5920
+ * console.log('[smartico] render', r.eligible_games.length, 'eligible game tiles');
5921
+ * ```
5922
+ */
5923
+ async getJackpotEligibleGames({
5924
+ jp_template_id,
5925
+ onUpdate
5926
+ }) {
5927
+ if (onUpdate) {
5928
+ this.onUpdateCallback.set(onUpdateContextKey.JackpotEligibleGames, onUpdate);
5929
+ }
5930
+ return OCache.use(onUpdateContextKey.JackpotEligibleGames + jp_template_id, ECacheContext.WSAPI, () => this.api.getJackpotEligibleGamesT(this.userExtId, {
5931
+ jp_template_id
5932
+ }), JACKPOT_ELIGIBLE_GAMES_CACHE_SEC);
5933
+ }
5198
5934
  }
5199
5935
 
5200
5936
  /** @group Raffles */
@@ -5204,26 +5940,112 @@ class WSAPIRaffles extends WSAPIJackpots {
5204
5940
  this.updateEntity(onUpdateContextKey.Raffles, payload);
5205
5941
  }
5206
5942
  /**
5207
- * Returns the list of Raffles available for user
5208
- * The returned list of Raffles is cached for 30 seconds. But you can pass the onUpdate callback as a parameter. Note that each time you call getRaffles with a new onUpdate callback, the old one will be overwritten by the new one.
5209
- * The onUpdate callback will be called on claiming prize. Updated Raffles will be passed to onUpdate callback.
5943
+ * Returns all raffles visible to the current user, each carrying an
5944
+ * embedded `draws[]` array with per-draw state (current state, ticket
5945
+ * counts, opt-in status, prize structure). Use this to power a
5946
+ * raffles lobby screen and to drive per-draw CTAs (opt-in, view
5947
+ * winners, claim).
5948
+ *
5949
+ * The embedded `draws[i]` objects are sufficient for rendering all
5950
+ * draw cards — winner details with usernames / avatars require a
5951
+ * follow-up {@link getRaffleDrawRun} call (the embedded form carries
5952
+ * only `winners_total` / `winners_limit` / `winners_offset` metadata,
5953
+ * not individual winner rows).
5210
5954
  *
5211
- * **Example**:
5955
+ * @remarks
5956
+ * **Subscription model (`onUpdate`)**
5957
+ * The callback receives the FULL refreshed raffle list (never a
5958
+ * diff/patch). Each subsequent call to `getRaffles({ onUpdate })`
5959
+ * REPLACES the prior callback. Pass `onUpdate: undefined` (or omit
5960
+ * it) to keep the prior callback in place; the callback is never
5961
+ * auto-cleared.
5212
5962
  *
5213
- * ```
5214
- * _smartico.api.getRaffles().then((result) => {
5215
- * console.log(result);
5216
- * });
5217
- * ```
5963
+ * **Update triggers** — the callback fires when:
5218
5964
  *
5219
- * **Example in the Visitor mode**:
5965
+ * 1. {@link claimRafflePrize} resolves on this connection (any
5966
+ * response code) — the refreshed list reflects the new
5967
+ * `has_unclaimed_prize` / `claimed_date` state.
5968
+ * 2. {@link requestRaffleOptin} resolves on this connection (any
5969
+ * response code) — the refreshed list reflects the new
5970
+ * `user_opted_in` state on the affected draw.
5971
+ *
5972
+ * Does NOT fire for: server-side draw executions, ticket increments
5973
+ * (other users earning tickets), or operator-side raffle/draw config
5974
+ * changes. Those changes surface only on the next cache miss (after
5975
+ * the 30 s TTL) — poll manually if your UI needs sub-30s freshness
5976
+ * during an in-progress draw.
5977
+ *
5978
+ * **Reading state from the returned raffle**
5979
+ * - `current_tickets_count` vs `max_tickets_count` drives the
5980
+ * `ticket_cap_visualization` banner (enum
5981
+ * {@link RaffleTicketCapVisualization}: `Empty` = no banner,
5982
+ * `Counter` = show "X tickets remaining", `Message` = show
5983
+ * sold-out message).
5984
+ * - Within each draw, `current_state`
5985
+ * ({@link RaffleDrawInstanceState}) buckets the draw into Open
5986
+ * (accepting tickets), WinnerSelection (currently drawing), or
5987
+ * Executed (winners selected).
5988
+ * - `execution_type` ({@link RaffleDrawTypeExecution}) distinguishes
5989
+ * one-shot (`ExecDate`), repeating (`Recurring`), or special
5990
+ * one-shot (`Grand`) draws. Recurring draws share a stable
5991
+ * `draws[i].id` (the schedule definition) across runs — `run_id`
5992
+ * is the per-execution instance.
5220
5993
  *
5221
- * ```
5222
- * _smartico.vapi('EN').getRaffles().then((result) => {
5223
- * console.log(result);
5994
+ * **Cache TTL**: the SDK caches the response for 30 seconds. Cache
5995
+ * is fully cleared on login / logout. The server also invalidates
5996
+ * the SDK cache when a draw executes on the server side.
5997
+ *
5998
+ * **Idempotency / Side effects**: safe. Read-only.
5999
+ *
6000
+ * **UI guidance**: see [UI Guide — `getRaffles`](../../docs/ui/raffles/UIGuide_getRaffles.md).
6001
+ *
6002
+ * **Visitor mode**: supported. The same shape is returned, scoped
6003
+ * to the brand's public raffles. Per-user fields (`my_tickets_count`,
6004
+ * `my_last_tickets`, `user_opted_in`) are not meaningful for
6005
+ * visitors. The `onUpdate` callback is accepted but never fires
6006
+ * because the mutation methods that trigger it
6007
+ * ({@link claimRafflePrize}, {@link requestRaffleOptin}) are not
6008
+ * available / not effective in visitor mode.
6009
+ *
6010
+ * @param params Optional. Omit to fetch without subscribing.
6011
+ * @param params.onUpdate Callback invoked with the full refreshed
6012
+ * raffles list after every
6013
+ * {@link claimRafflePrize} or
6014
+ * {@link requestRaffleOptin} round-trip
6015
+ * on this connection.
6016
+ * @returns Promise resolving to the raffles list.
6017
+ *
6018
+ * @example
6019
+ * ```ts
6020
+ * const raffles = await window._smartico.api.getRaffles({
6021
+ * onUpdate: (refreshed) => {
6022
+ * console.log('[smartico] raffles refreshed (after claim/optin) — re-render from this array:', refreshed);
6023
+ * },
5224
6024
  * });
5225
- * ```
5226
6025
  *
6026
+ * for (const raffle of raffles) {
6027
+ * console.log('[smartico] render raffle', raffle.id, '—', raffle.name, ':', raffle.draws.length, 'draws');
6028
+ *
6029
+ * // Bucket draws: active/upcoming first (sorted by execution_ts ASC), then executed last.
6030
+ * const sorted = [...raffle.draws].sort((a, b) => {
6031
+ * const aDone = a.current_state === 3; // Executed
6032
+ * const bDone = b.current_state === 3;
6033
+ * if (aDone !== bDone) return aDone ? 1 : -1; // executed sink to the bottom
6034
+ * return a.execution_ts - b.execution_ts;
6035
+ * });
6036
+ *
6037
+ * for (const d of sorted) {
6038
+ * if (d.requires_optin && !d.user_opted_in && d.is_active && d.current_state !== 3) {
6039
+ * console.log('[smartico] draw', d.run_id, '— show OPT IN CTA (requires_optin + not yet opted in + still active)');
6040
+ * }
6041
+ * if (d.current_state === 3) {
6042
+ * console.log('[smartico] draw', d.run_id, 'executed — fetch winner details via getRaffleDrawRun');
6043
+ * }
6044
+ * }
6045
+ * }
6046
+ *
6047
+ * // For winner detail on an executed draw, call getRaffleDrawRun separately.
6048
+ * ```
5227
6049
  */
5228
6050
  async getRaffles({
5229
6051
  onUpdate
@@ -5234,27 +6056,106 @@ class WSAPIRaffles extends WSAPIJackpots {
5234
6056
  return OCache.use(onUpdateContextKey.Raffles, ECacheContext.WSAPI, () => this.api.getRafflesT(this.userExtId), CACHE_DATA_SEC);
5235
6057
  }
5236
6058
  /**
5237
- * Returns draw run for provided raffle_id and run_id.
5238
- * You can pass winners_from and winners_to parameters to get a specific range of winners. Default is 0-20.
6059
+ * Returns the full detail of a single raffle draw run — same shape
6060
+ * as the embedded `TRaffleDraw` in {@link getRaffles}, but populated
6061
+ * with paginated `prizes[].winners[]` rows including usernames and
6062
+ * avatars. Use this to render a draw detail / winners screen, or to
6063
+ * page through the winner list of an executed draw.
5239
6064
  *
6065
+ * Each call is a fresh server round-trip — there is no client cache.
6066
+ * Re-call to refresh.
5240
6067
  *
5241
- * **Example**:
6068
+ * @remarks
6069
+ * **Preconditions**
6070
+ * Pass `raffle_id` (the parent raffle's `id`) and `run_id` (the
6071
+ * specific run instance — `TRaffleDraw.run_id` from
6072
+ * {@link getRaffles}'s embedded draws). Note that
6073
+ * `TRaffleDraw.id` is the *schedule_id* (stable across runs of a
6074
+ * recurring draw) — that's NOT what this method takes; it takes the
6075
+ * per-instance `run_id`.
5242
6076
  *
5243
- * ```javascript
5244
- * _smartico.api.getRaffleDrawRun({raffle_id: 156, run_id: 145}).then((result) => {
5245
- * console.log(result);
5246
- * });
5247
- * ```
6077
+ * **Refresh model**
6078
+ * - **No subscription.** One-shot promise.
6079
+ * - **No client cache.** Every call sends a network request.
6080
+ * - **No push event** refreshes the response. Live winner-list
6081
+ * updates require a fresh call.
6082
+ * - The default Smartico UI polls this method on a variable cadence
6083
+ * while a draw is in flight: every 3 s during `WinnerSelection`,
6084
+ * every 10 s when the countdown is under 5 minutes, every 30 s
6085
+ * otherwise.
6086
+ *
6087
+ * **Winner pagination**
6088
+ * `winners_from` / `winners_to` define a half-open range of winner
6089
+ * indices. The default window is `0–20` (20 winners). The server
6090
+ * caps the window at 50 rows per call — passing a larger range
6091
+ * silently truncates. For "load more" pagination, advance
6092
+ * `winners_from` by the prior page size and pass the same
6093
+ * `winners_to` offset. `winners_total` on the returned
6094
+ * `TRaffleDraw` is the authoritative total count for pagination.
6095
+ *
6096
+ * The current user's own winning rows (if any) are server-sorted
6097
+ * to the top of the result.
6098
+ *
6099
+ * **Throws**
6100
+ * Both `raffle_id` and `run_id` are required — the SDK throws
6101
+ * synchronously when either is missing or falsy.
6102
+ *
6103
+ * **Idempotency / Side effects**: safe. Read-only.
6104
+ *
6105
+ * **UI guidance**: see [UI Guide — `getRaffleDrawRun`](../../docs/ui/raffles/UIGuide_getRaffleDrawRun.md).
6106
+ *
6107
+ * **Visitor mode**: supported.
6108
+ *
6109
+ * @param props.raffle_id The parent raffle's `id` (from `TRaffle.id`).
6110
+ * @param props.run_id The draw run instance ID (from
6111
+ * `TRaffleDraw.run_id`, not `TRaffleDraw.id`).
6112
+ * @param props.winners_from First winner index to return (0-based).
6113
+ * Defaults to `0`.
6114
+ * @param props.winners_to Last winner index (exclusive).
6115
+ * Defaults to `20`. Server caps the
6116
+ * window at 50 rows per call.
6117
+ * @returns Promise resolving to `TRaffleDraw`
6118
+ * with `prizes[].winners[]` populated
6119
+ * and `winners_total` set.
5248
6120
  *
5249
- * **Example in the Visitor mode**:
6121
+ * @example
6122
+ * ```ts
6123
+ * const raffles = await window._smartico.api.getRaffles();
6124
+ * const raffle = raffles[0];
6125
+ * const draw = raffle.draws.find(d => d.current_state === 3); // Executed
5250
6126
  *
6127
+ * if (!draw) {
6128
+ * console.log('[smartico] no executed draw — skip detail view');
6129
+ * return;
6130
+ * }
5251
6131
  *
5252
- * ```javascript
5253
- * _smartico.vapi('EN').getRaffleDrawRun({ raffle_id: 156, run_id: 145 }).then((result) => {
5254
- * console.log(result);
6132
+ * // First page of winners.
6133
+ * const detail = await window._smartico.api.getRaffleDrawRun({
6134
+ * raffle_id: raffle.id,
6135
+ * run_id: draw.run_id, // NOTE: run_id, NOT draw.id
6136
+ * winners_from: 0,
6137
+ * winners_to: 20,
5255
6138
  * });
5256
- * ```
5257
6139
  *
6140
+ * for (const prize of detail.prizes) {
6141
+ * console.log('[smartico] prize', prize.name, '— winners on this page:', prize.winners.length);
6142
+ * const myWin = prize.winners.find(w => w.username === currentUsername); // or another match
6143
+ * if (myWin) {
6144
+ * console.log('[smartico] current user won this prize — surface a Claim CTA if not yet claimed');
6145
+ * }
6146
+ * }
6147
+ *
6148
+ * // Load more — pagination via winners_from / winners_to.
6149
+ * if ((detail.winners_total ?? 0) > 20) {
6150
+ * const next = await window._smartico.api.getRaffleDrawRun({
6151
+ * raffle_id: raffle.id,
6152
+ * run_id: draw.run_id,
6153
+ * winners_from: 20,
6154
+ * winners_to: 40,
6155
+ * });
6156
+ * console.log('[smartico] page 2 loaded — append', next.prizes.flatMap(p => p.winners).length, 'winners');
6157
+ * }
6158
+ * ```
5258
6159
  */
5259
6160
  async getRaffleDrawRun(props) {
5260
6161
  if (!props.raffle_id || !props.run_id) {
@@ -5263,25 +6164,76 @@ class WSAPIRaffles extends WSAPIJackpots {
5263
6164
  return await this.api.getRaffleDrawRunT(this.userExtId, props.raffle_id, props.run_id, props.winners_from, props.winners_to);
5264
6165
  }
5265
6166
  /**
5266
- * Returns history of draw runs for the provided raffle_id and draw_id, if the draw_id is not provided will return history of all the draws for the provided raffle_id
6167
+ * Returns the history of past-executed draw runs for a raffle —
6168
+ * optionally scoped to a single recurring draw schedule. Each row
6169
+ * is a previously-executed run with its scheduled and
6170
+ * actual-execution timestamps, the user's win status, and an
6171
+ * unclaimed-prize flag for surfacing a Claim CTA on history rows.
6172
+ *
6173
+ * @remarks
6174
+ * **Preconditions**
6175
+ * `raffle_id` is required. `draw_id` is optional — when omitted,
6176
+ * the response includes history for ALL draws of the raffle; when
6177
+ * supplied, it scopes to that one draw's recurring history. The
6178
+ * `draw_id` to pass is `TRaffleDraw.id` (the schedule_id, stable
6179
+ * across runs), not `run_id`.
5267
6180
  *
6181
+ * **Server-side filtering**
6182
+ * Draws configured by the operator to hide their history are
6183
+ * excluded from the response automatically. Cancelled runs are
6184
+ * also excluded.
6185
+ *
6186
+ * **Sort order**
6187
+ * Server returns rows by scheduled `execution_ts` descending
6188
+ * (newest first). No client-side sort required.
6189
+ *
6190
+ * **Refresh model**
6191
+ * - **No subscription.** One-shot promise.
6192
+ * - **No client cache.** Every call hits the server.
6193
+ * - **No push event** refreshes the response. Re-call manually to
6194
+ * pick up newly-executed runs or `has_unclaimed_prize` flips
6195
+ * after a {@link claimRafflePrize} call.
5268
6196
  *
5269
- * **Example**:
6197
+ * **Idempotency / Side effects**: safe. Read-only.
5270
6198
  *
5271
- * ```javascript
5272
- * _smartico.api.getRaffleDrawRunsHistory({ raffle_id: 156, draw_id: 432 }).then((result) => {
5273
- * console.log(result);
6199
+ * **UI guidance**: see [UI Guide — `getRaffleDrawRunsHistory`](../../docs/ui/raffles/UIGuide_getRaffleDrawRunsHistory.md).
6200
+ *
6201
+ * **Visitor mode**: supported. The same shape is returned with
6202
+ * `is_winner` and `has_unclaimed_prize` always `false` for visitors.
6203
+ *
6204
+ * @param props.raffle_id The parent raffle's `id` (from `TRaffle.id`).
6205
+ * Required.
6206
+ * @param props.draw_id Optional — the draw's schedule_id
6207
+ * (`TRaffleDraw.id`). When set, scopes the
6208
+ * history to that one draw's runs. When
6209
+ * omitted, includes all draws of the raffle.
6210
+ * @returns Promise resolving to a `TRaffleDrawRun[]`
6211
+ * ordered newest-first. Empty if no executed
6212
+ * runs exist.
6213
+ *
6214
+ * @example
6215
+ * ```ts
6216
+ * const raffles = await window._smartico.api.getRaffles();
6217
+ * const raffle = raffles[0];
6218
+ *
6219
+ * // History across all draws of the raffle.
6220
+ * const allHistory = await window._smartico.api.getRaffleDrawRunsHistory({
6221
+ * raffle_id: raffle.id,
5274
6222
  * });
5275
- * ```
5276
6223
  *
5277
- * **Example in the Visitor mode**:
6224
+ * // Highlight rows where the user has an unclaimed prize.
6225
+ * const unclaimed = allHistory.filter(r => r.has_unclaimed_prize);
6226
+ * if (unclaimed.length > 0) {
6227
+ * console.log('[smartico] surface a "Claim" CTA on these', unclaimed.length, 'history rows:', unclaimed.map(r => r.run_id));
6228
+ * }
5278
6229
  *
5279
- * ```javascript
5280
- * _smartico.vapi('EN').getRaffleDrawRunsHistory({ raffle_id: 156, draw_id: 432 }).then((result) => {
5281
- * console.log(result);
6230
+ * // Scoped history of one recurring draw (use draw.id, the schedule_id).
6231
+ * const oneDrawHistory = await window._smartico.api.getRaffleDrawRunsHistory({
6232
+ * raffle_id: raffle.id,
6233
+ * draw_id: raffle.draws[0].id, // schedule_id (stable across recurring runs)
5282
6234
  * });
6235
+ * console.log('[smartico] render history rows for the daily draw — newest first:', oneDrawHistory.length, 'rows');
5283
6236
  * ```
5284
- *
5285
6237
  */
5286
6238
  async getRaffleDrawRunsHistory(props) {
5287
6239
  const res = await this.api.getRaffleDrawRunsHistory(this.userExtId, props);
@@ -5291,25 +6243,113 @@ class WSAPIRaffles extends WSAPIJackpots {
5291
6243
  return drawRunHistoryTransform(res);
5292
6244
  }
5293
6245
  /**
5294
- * Returns `err_code` and `err_message` after the call; `err_code` 0 means the request succeeded.
6246
+ * Claims a raffle prize the user has won. Call this when the user
6247
+ * has a winning row with `should_claim === true` and an unclaimed
6248
+ * `won_id` (read from a `TRafflePrizeWinner` on a
6249
+ * {@link getRaffleDrawRun} result, or surfaced by `has_unclaimed_prize`
6250
+ * on a {@link getRaffleDrawRunsHistory} row that the consumer drills
6251
+ * into).
5295
6252
  *
6253
+ * On success (`errorCode === 0`), the server records the claim and
6254
+ * triggers prize delivery through the operator's integration —
6255
+ * delivery is server-side, not part of this response.
5296
6256
  *
5297
- * **Example**:
6257
+ * @remarks
6258
+ * **Result shape note**: this method returns
6259
+ * `TransformedRaffleClaimPrizeResponse` with `errorCode` /
6260
+ * `errorMessage` (camelCase full-word) — different from most other
6261
+ * SDK result types which use `err_code` / `err_message` (snake_case
6262
+ * short-word). Branch on the camelCase keys when reading this
6263
+ * method's result.
5298
6264
  *
5299
- * ```javascript
5300
- * _smartico.api.claimRafflePrize({won_id:251}).then((result) => {
5301
- * console.log(result);
5302
- * });
5303
- * ```
6265
+ * **Preconditions**
6266
+ * Pass a `won_id` that identifies an unclaimed winning row for the
6267
+ * current user. The `won_id` comes from
6268
+ * `TRafflePrizeWinner.raf_won_id` returned by
6269
+ * {@link getRaffleDrawRun}.
6270
+ *
6271
+ * **Error codes** (in `errorCode`)
6272
+ * - `0` — success; the claim is recorded and prize delivery is
6273
+ * queued server-side.
6274
+ * - `1` — generic claim failure. The `errorMessage` distinguishes
6275
+ * the specific cause (the server uses one error code for several
6276
+ * distinct conditions): the prize has already been claimed, the
6277
+ * `won_id` doesn't match a winning row for this user, or the user
6278
+ * is in a control group for this raffle. Treat `1` as a retryable
6279
+ * error if the message suggests a transient condition; treat as
6280
+ * idempotent success if the message indicates already-claimed
6281
+ * (the underlying state is what the consumer wants either way —
6282
+ * the prize is claimed).
6283
+ * - other non-zero — generic server error. Surface `errorMessage`
6284
+ * if any.
6285
+ *
6286
+ * **Claim window**
6287
+ * There is no server-enforced claim deadline. Won prizes remain
6288
+ * claimable indefinitely until the operator archives them.
6289
+ *
6290
+ * **Idempotency**: NOT idempotent. A second call on the same
6291
+ * `won_id` returns `errorCode === 1` with `errorMessage` indicating
6292
+ * the prize has already been claimed. The SDK does NOT enforce an
6293
+ * in-flight lock. Guard the call site against double-clicks (set
6294
+ * a local "claiming" flag on click, clear it on response).
5304
6295
  *
5305
- * **Example in the Visitor mode**:
6296
+ * **Refresh after success (and after failure)**
6297
+ * The SDK automatically refreshes the raffles cache on every
6298
+ * response — both success and failure paths fire the `onUpdate`
6299
+ * callback registered via {@link getRaffles}. After `errorCode === 0`,
6300
+ * the affected `TRaffleDrawRun.has_unclaimed_prize` (in history)
6301
+ * and the winner row's `claimed_date` reflect the claim on the next
6302
+ * refresh.
6303
+ *
6304
+ * **Side effects** (on `errorCode === 0`)
6305
+ * - The winner row's `claimed_date` is set server-side.
6306
+ * - Prize delivery is queued for the operator's integration to
6307
+ * process. Balance / bonus / inbox updates that result from the
6308
+ * delivery arrive through their own channels — not in this
6309
+ * response.
6310
+ *
6311
+ * **Throws**
6312
+ * `won_id` is required — the SDK throws synchronously when missing
6313
+ * or falsy.
6314
+ *
6315
+ * **UI guidance**: see [UI Guide — `claimRafflePrize`](../../docs/ui/raffles/UIGuide_claimRafflePrize.md).
6316
+ *
6317
+ * **Visitor mode**: not meaningfully supported. The SDK does not
6318
+ * block the call, but visitor sessions have no win history and the
6319
+ * server will reject it.
6320
+ *
6321
+ * @param props.won_id The winning row's `raf_won_id` (from
6322
+ * `TRafflePrizeWinner` returned by
6323
+ * {@link getRaffleDrawRun}).
6324
+ * @returns `{ errorCode, errorMessage? }`; success when
6325
+ * `errorCode === 0`. Note camelCase keys (see "Result
6326
+ * shape note" above).
5306
6327
  *
5307
- * ```javascript
5308
- * _smartico.vapi('EN').claimRafflePrize({ won_id: 251 }).then((result) => {
5309
- * console.log(result);
6328
+ * @example
6329
+ * ```ts
6330
+ * const detail = await window._smartico.api.getRaffleDrawRun({
6331
+ * raffle_id: raffleId,
6332
+ * run_id: runId,
5310
6333
  * });
5311
- * ```
6334
+ * const myWin = detail.prizes.flatMap(p => p.winners).find(w => /* user match here *\/ true);
6335
+ *
6336
+ * if (!myWin || !myWin.raf_won_id || myWin.claimed_date) {
6337
+ * console.log('[smartico] nothing to claim — keep CTA hidden');
6338
+ * return;
6339
+ * }
6340
+ *
6341
+ * console.log('[smartico] claim starting — set in-flight flag, show loading dots on the Claim button, keep the modal open');
6342
+ * const r = await window._smartico.api.claimRafflePrize({ won_id: myWin.raf_won_id });
6343
+ * console.log('[smartico] claim response received — clear in-flight flag');
5312
6344
  *
6345
+ * if (r.errorCode === 0) {
6346
+ * console.log('[smartico] claim succeeded — show a success toast; getRaffles onUpdate above will fire with refreshed state; prize delivery happens server-side');
6347
+ * } else if (r.errorCode === 1 && r.errorMessage?.toLowerCase().includes('already claimed')) {
6348
+ * console.log('[smartico] prize was already claimed (perhaps from another tab) — treat as success, hide the Claim CTA');
6349
+ * } else {
6350
+ * console.error('[smartico] claim failed — surface this error message to the user and allow retry:', r.errorMessage);
6351
+ * }
6352
+ * ```
5313
6353
  */
5314
6354
  async claimRafflePrize(props) {
5315
6355
  if (!props.won_id) {
@@ -5321,9 +6361,104 @@ class WSAPIRaffles extends WSAPIJackpots {
5321
6361
  return raffleClaimPrizeResponseTransform(res);
5322
6362
  }
5323
6363
  /**
5324
- * Requests an opt-in for the specified raffle. Returns the err_code.
6364
+ * Opts the current user into a specific raffle draw run. Required
6365
+ * for draws where `TRaffleDraw.requires_optin === true` — without
6366
+ * opt-in, the user's tickets do NOT participate in that run, even
6367
+ * if they hold tickets for the raffle.
5325
6368
  *
5326
- * **Visitor mode: not supported**
6369
+ * Opt-in is per-run: a recurring draw's next run requires a fresh
6370
+ * opt-in. The user's `TRaffleDraw.user_opted_in` flag reflects the
6371
+ * current state for the current run only.
6372
+ *
6373
+ * @remarks
6374
+ * **Preconditions**
6375
+ * Read the candidate draw from {@link getRaffles}'s embedded
6376
+ * `draws[]` and gate the call on
6377
+ * `requires_optin === true && !user_opted_in && is_active &&
6378
+ * current_state !== Executed`. Calling without these guards may
6379
+ * return a non-zero `err_code`.
6380
+ *
6381
+ * **Argument mapping** — the three IDs come from different fields:
6382
+ * - `raffle_id` — the parent raffle's `TRaffle.id`
6383
+ * - `draw_id` — `TRaffleDraw.id` (schedule_id, stable across runs)
6384
+ * - `raffle_run_id` — `TRaffleDraw.run_id` (instance ID for this
6385
+ * specific run)
6386
+ *
6387
+ * **Error codes** (in `err_code`)
6388
+ * - `0` — success; the user is opted into this run. Tickets
6389
+ * collected after the run's `ticket_start_ts` will participate.
6390
+ * - `1` — generic opt-in failure. The `err_message` distinguishes
6391
+ * the specific cause: the draw doesn't require opt-in
6392
+ * (`requires_optin === false`), `raffle_id` doesn't match the
6393
+ * `draw_id`, the user is already opted in (or the draw has
6394
+ * completed), or the user is in a control group for this raffle.
6395
+ * - other non-zero — generic server error. Surface `err_message`.
6396
+ *
6397
+ * **Idempotency**: NOT idempotent. A second call on the same
6398
+ * `raffle_run_id` returns `err_code === 1` with `err_message`
6399
+ * indicating the user is already opted in. Treat as success in
6400
+ * the UI — the underlying state is what the consumer wants.
6401
+ * Guard the call site against double-clicks.
6402
+ *
6403
+ * **Refresh after success (and after failure)**
6404
+ * The SDK automatically refreshes the raffles cache on every
6405
+ * response and fires the `onUpdate` callback registered via
6406
+ * {@link getRaffles}. After `err_code === 0`, the affected draw's
6407
+ * `user_opted_in` flips to `true` on the refreshed list.
6408
+ *
6409
+ * **Side effects** (on `err_code === 0`)
6410
+ * - The user is registered for this run. Tickets earned within
6411
+ * the run's eligibility window will participate in the draw.
6412
+ * - No balance change. No CRM-side rewards. Opt-in is a metadata
6413
+ * operation only.
6414
+ *
6415
+ * **Throws**
6416
+ * `raffle_id`, `draw_id`, and `raffle_run_id` are all required —
6417
+ * the SDK throws synchronously when any is missing or falsy.
6418
+ *
6419
+ * **UI guidance**: see [UI Guide — `requestRaffleOptin`](../../docs/ui/raffles/UIGuide_requestRaffleOptin.md).
6420
+ *
6421
+ * **Visitor mode**: not supported.
6422
+ *
6423
+ * @param props.raffle_id The parent raffle's `id`.
6424
+ * @param props.draw_id The draw's schedule_id (`TRaffleDraw.id`).
6425
+ * @param props.raffle_run_id The run instance ID (`TRaffleDraw.run_id`).
6426
+ * @returns `{ err_code, err_message? }`; success
6427
+ * when `err_code === 0` (treat
6428
+ * "already opted in" as idempotent
6429
+ * success).
6430
+ *
6431
+ * @example
6432
+ * ```ts
6433
+ * const raffles = await window._smartico.api.getRaffles({
6434
+ * onUpdate: (refreshed) => console.log('[smartico] raffles refreshed — re-render', refreshed),
6435
+ * });
6436
+ * const raffle = raffles[0];
6437
+ * const draw = raffle.draws.find(d =>
6438
+ * d.requires_optin && !d.user_opted_in && d.is_active && d.current_state !== 3
6439
+ * );
6440
+ *
6441
+ * if (!draw) {
6442
+ * console.log('[smartico] no opt-in-required draw available — keep CTA hidden');
6443
+ * return;
6444
+ * }
6445
+ *
6446
+ * console.log('[smartico] opt-in starting — set in-flight flag, show loading dots on the Opt-in button');
6447
+ * const r = await window._smartico.api.requestRaffleOptin({
6448
+ * raffle_id: raffle.id,
6449
+ * draw_id: draw.id, // schedule_id, NOT run_id
6450
+ * raffle_run_id: draw.run_id,
6451
+ * });
6452
+ * console.log('[smartico] opt-in response received — clear in-flight flag');
6453
+ *
6454
+ * if (r.err_code === 0) {
6455
+ * console.log('[smartico] opted in successfully — show "Fully opted in" state on the draw card; onUpdate above will fire with user_opted_in: true');
6456
+ * } else if (r.err_code === 1 && r.err_message?.toLowerCase().includes('already opted in')) {
6457
+ * console.log('[smartico] already opted in (perhaps from another tab) — treat as success, show "Fully opted in"');
6458
+ * } else {
6459
+ * console.error('[smartico] opt-in failed — surface this error to the user:', r.err_message);
6460
+ * }
6461
+ * ```
5327
6462
  */
5328
6463
  async requestRaffleOptin(props) {
5329
6464
  if (!props.raffle_id) {
@@ -5341,29 +6476,233 @@ class WSAPIRaffles extends WSAPIJackpots {
5341
6476
  err_message: r.errMsg
5342
6477
  };
5343
6478
  }
6479
+ /**
6480
+ * Returns every prize the current user has won within a single raffle —
6481
+ * across ALL of that raffle's draws, newest-won first — in one paginated
6482
+ * call. Use this to power a "my prizes" screen for a raffle (prize name,
6483
+ * prize image, the user's name/avatar) without walking
6484
+ * {@link getRaffleDrawRunsHistory} and then paging
6485
+ * {@link getRaffleDrawRun}'s per-draw winner lists.
6486
+ *
6487
+ * Each row carries its own `raf_won_id`, which IS the `won_id` to pass to
6488
+ * {@link claimRafflePrize}: a row with `requires_claim === true` and no
6489
+ * `claimed_date` is directly claimable.
6490
+ *
6491
+ * @remarks
6492
+ * **Authentication required**
6493
+ * Works only for an identified (logged-in) user. Visitor / anonymous
6494
+ * sessions are rejected — call this only after identify.
6495
+ *
6496
+ * **Preconditions**
6497
+ * `raffle_id` is required — the SDK throws synchronously when it is
6498
+ * missing or falsy.
6499
+ *
6500
+ * **Coverage & sort order**
6501
+ * Includes the user's wins from every draw of the raffle — recurring,
6502
+ * one-shot, and grand draws, across all historical runs. Rows are ordered
6503
+ * newest-win first; there is no "unclaimed first" ordering, so sort
6504
+ * client-side if you want claimable prizes at the top.
6505
+ *
6506
+ * **Pagination**
6507
+ * `offset` / `limit` page the `won_prizes[]` rows (the SDK defaults to
6508
+ * `0` / `20`). `total` is the authoritative full count of the user's wins
6509
+ * for this raffle across all draws — drive "load more" from it
6510
+ * (`offset + won_prizes.length < total`). An `offset` past the end returns
6511
+ * an empty `won_prizes[]` with the real `total` and `errCode 0` — not an
6512
+ * error. The server does NOT clamp `limit`, so always pass an explicit,
6513
+ * sane page size.
6514
+ *
6515
+ * **Rate limit**
6516
+ * The server caps this at roughly 30 calls per minute with a ~5-second
6517
+ * minimum gap between calls. Fetch on screen open and on explicit "load
6518
+ * more" — don't poll it on a timer.
6519
+ *
6520
+ * **Error codes** (in `errCode`)
6521
+ * - `0` — success. Also returned with an empty `won_prizes[]` and
6522
+ * `total 0` when the user has no wins, or is not eligible to see this
6523
+ * raffle.
6524
+ * - `1` — generic server error; surface `errMsg` if present.
6525
+ *
6526
+ * There are no granular codes — an unknown raffle, an ineligible user,
6527
+ * and "no wins" all surface as `errCode 0` with an empty list.
6528
+ *
6529
+ * **Reading the result**
6530
+ * - Null-check `user` before reading it — it is `null` whenever
6531
+ * `won_prizes` is empty.
6532
+ * - For the avatar, use `user.avatar_id` (already a full URL for a system
6533
+ * avatar, otherwise an avatar token to resolve against the widget's
6534
+ * avatar domain); `user.avatar_url` is always `null`.
6535
+ * - Render `public_username` as-is (it is server-masked for privacy, e.g.
6536
+ * `"32:r*****"`) and substitute a "You" label for the current user.
6537
+ * - Treat `claimed_date` as the claim flag: `null` until claimed, then
6538
+ * epoch ms.
6539
+ *
6540
+ * **Idempotency / Side effects**: safe and read-only — one-shot promise,
6541
+ * no client cache, no subscription. The server caches the response
6542
+ * briefly; a successful {@link claimRafflePrize} invalidates that cache,
6543
+ * so a follow-up call reflects the new `claimed_date`. This method does
6544
+ * NOT refresh the {@link getRaffles} list or fire its `onUpdate` — re-call
6545
+ * manually to pick up newly-won prizes or a `claimed_date` flip.
6546
+ *
6547
+ * **UI guidance**: see [UI Guide — `getRaffleWonPrizes`](../../docs/ui/raffles/UIGuide_getRaffleWonPrizes.md).
6548
+ *
6549
+ * **Visitor mode**: not supported (see "Authentication required").
6550
+ *
6551
+ * @param props.raffle_id The parent raffle's `id` (from `TRaffle.id`). Required.
6552
+ * @param props.offset Zero-based index of the first row to return. Defaults to `0`.
6553
+ * @param props.limit Page size. Defaults to `20`.
6554
+ * @returns Promise resolving to {@link GetRaffleWonPrizesResponse}
6555
+ * (`user`, this page of `won_prizes`, and `total`;
6556
+ * `errCode 0` on success).
6557
+ *
6558
+ * @example
6559
+ * ```ts
6560
+ * const raffles = await window._smartico.api.getRaffles();
6561
+ * const raffle = raffles[0];
6562
+ *
6563
+ * // First page of the user's won prizes for this raffle.
6564
+ * const page = await window._smartico.api.getRaffleWonPrizes({
6565
+ * raffle_id: raffle.id,
6566
+ * offset: 0,
6567
+ * limit: 20,
6568
+ * });
6569
+ *
6570
+ * if (page.errCode !== 0) {
6571
+ * console.error('[smartico] could not load won prizes — show a retry state:', page.errMsg);
6572
+ * return;
6573
+ * }
6574
+ * if (page.won_prizes.length === 0) {
6575
+ * console.log('[smartico] no wins in this raffle (or not eligible) — render the empty state');
6576
+ * return;
6577
+ * }
6578
+ *
6579
+ * console.log('[smartico]', page.user?.public_username, 'won', page.total, 'prizes total');
6580
+ * for (const prize of page.won_prizes) {
6581
+ * console.log('[smartico] render prize card —', prize.public_meta.name, prize.public_meta.image_url);
6582
+ * if (prize.requires_claim && !prize.claimed_date) {
6583
+ * console.log('[smartico] show a Claim CTA — on click call claimRafflePrize({ won_id:', prize.raf_won_id, '})');
6584
+ * }
6585
+ * }
6586
+ *
6587
+ * // Load more.
6588
+ * if (page.offset + page.won_prizes.length < page.total) {
6589
+ * const next = await window._smartico.api.getRaffleWonPrizes({
6590
+ * raffle_id: raffle.id,
6591
+ * offset: page.offset + page.limit,
6592
+ * limit: 20,
6593
+ * });
6594
+ * console.log('[smartico] appended', next.won_prizes.length, 'more prizes');
6595
+ * }
6596
+ * ```
6597
+ */
6598
+ async getRaffleWonPrizes(props) {
6599
+ var _props$offset, _props$limit;
6600
+ if (!props.raffle_id) {
6601
+ throw new Error('raffle_id is required');
6602
+ }
6603
+ return await this.api.getRaffleWonPrizes(this.userExtId, {
6604
+ raffle_id: props.raffle_id,
6605
+ offset: (_props$offset = props.offset) != null ? _props$offset : 0,
6606
+ limit: (_props$limit = props.limit) != null ? _props$limit : 20
6607
+ });
6608
+ }
5344
6609
  }
5345
6610
 
5346
6611
  /** @group MiniGames */
5347
6612
  class WSAPIMiniGames extends WSAPIRaffles {
5348
6613
  /**
5349
- * Returns the list of mini-games configured for the current user (not filtered by spin availability or Widget visibility).
5350
- * The returned list of mini-games is cached for 30 seconds. But you can pass the onUpdate callback as a parameter. Note that each time you call getMiniGames with a new onUpdate callback, the old one will be overwritten by the new one.
5351
- * The onUpdate callback will be called on available spin count change, if mini-game has increasing jackpot per spin or won prize is spin/jackpot and if max count of the available user spins equals one, also if the spins were issued to the user manually in the BO. Updated templates will be passed to onUpdate callback.
6614
+ * Returns all mini-game templates ("SAW" = Spin And Win — the
6615
+ * umbrella term for wheel-spin, scratch-card, lootbox, gift-box,
6616
+ * treasure-hunt, plinko, coin-flip, quiz, and several other formats)
6617
+ * configured for the label. Optionally subscribes to live updates
6618
+ * via `onUpdate`: the callback fires whenever the user's
6619
+ * `spin_count` changes, jackpot grows, a manual spin is issued, or
6620
+ * a spin response lands.
6621
+ *
6622
+ * The list is NOT filtered by spin availability — every configured
6623
+ * template is returned, including ones the user cannot currently
6624
+ * play. The `visibile_when_can_spin` flag on each template hints
6625
+ * which to hide in a default lobby view (consumer-side filter).
5352
6626
  *
5353
- * **Example**:
5354
- * ```
5355
- * _smartico.api.getMiniGames().then((result) => {
5356
- * console.log(result);
5357
- * });
5358
- * ```
6627
+ * @remarks
6628
+ * **Subscription model (`onUpdate`)**
6629
+ * The callback receives the FULL refreshed template list (never a
6630
+ * diff/patch). Each subsequent call to `getMiniGames({ onUpdate })`
6631
+ * REPLACES the prior callback. Pass `onUpdate: undefined` (or omit
6632
+ * it) to keep the prior callback in place; the callback is never
6633
+ * auto-cleared.
5359
6634
  *
5360
- * **Example in the Visitor mode**:
5361
- * ```
5362
- * _smartico.vapi('EN').getMiniGames().then((result) => {
5363
- * console.log(result);
6635
+ * **Update triggers** — the callback fires when:
6636
+ *
6637
+ * 1. The server pushes a `spin_count` change (campaign award,
6638
+ * store purchase, BO manual issuance, mission reward).
6639
+ * 2. Any {@link playMiniGame} or {@link playMiniGameBatch}
6640
+ * resolves on this connection — the cached array refreshes
6641
+ * with updated `spin_count`, `jackpot_current`, and any new
6642
+ * `next_available_spin_ts`.
6643
+ * 3. Acknowledge responses land (via auto-acknowledge or
6644
+ * {@link miniGameWinAcknowledgeRequest}).
6645
+ *
6646
+ * Jackpot growth is observable on the refreshed `jackpot_current`
6647
+ * field — the SDK inlines the live value into template `name` /
6648
+ * `promo_text` / prize `name` via a `{{jackpot}}` template
6649
+ * substitution at fetch time.
6650
+ *
6651
+ * **Game-type variants**
6652
+ * All twelve `SAWGameType` values (`SpinAWheel`, `ScratchCard`,
6653
+ * `MatchX`, `GiftBox`, `PrizeDrop`, `Quiz`, `LootboxWeekdays`,
6654
+ * `LootboxCalendarDays`, `TreasureHunt`, `Voyager`, `Plinko`,
6655
+ * `CoinFlip`) surface through the same template shape. Most are
6656
+ * played via {@link playMiniGame}; the exceptions are:
6657
+ * - `PrizeDrop` — server-push only; the consumer does NOT call
6658
+ * `playMiniGame`. Prizes arrive via the prize-drop push channel.
6659
+ * - `MatchX` / `Quiz` — predictions are submitted via a separate
6660
+ * games server (not this SDK); the SAW spin is fired
6661
+ * server-internally to deduct the buy-in.
6662
+ *
6663
+ * **Cache TTL**: the SDK caches the response for 30 seconds. Cache
6664
+ * is fully cleared on login / logout.
6665
+ *
6666
+ * **Idempotency / Side effects**: safe. Read-only.
6667
+ *
6668
+ * **UI guidance**: see [UI Guide — `getMiniGames`](../../docs/ui/minigames/UIGuide_getMiniGames.md).
6669
+ *
6670
+ * **Visitor mode**: supported. Only templates configured with
6671
+ * `is_visitor_mode = true` in the BO are returned for anonymous
6672
+ * sessions. Spins from these templates may also be played (visitor
6673
+ * mode is the one mini-game flow that does support play — see
6674
+ * {@link playMiniGame} for the visitor-stop semantics).
6675
+ *
6676
+ * @param params Optional. Omit to fetch without subscribing.
6677
+ * @param params.onUpdate Callback invoked with the full refreshed
6678
+ * template array after any spin-count
6679
+ * change, spin response, or acknowledge
6680
+ * response on this connection.
6681
+ * @returns Promise resolving to the template
6682
+ * array. Empty if no templates are
6683
+ * visible to the user.
6684
+ *
6685
+ * @example
6686
+ * ```ts
6687
+ * const games = await window._smartico.api.getMiniGames({
6688
+ * onUpdate: (refreshed) => {
6689
+ * console.log('[smartico] mini-game templates refreshed — re-render lobby from this array:', refreshed);
6690
+ * },
5364
6691
  * });
5365
- * ```
5366
6692
  *
6693
+ * // Filter the lobby view per the visibility flag.
6694
+ * const visible = games.filter(g =>
6695
+ * !g.visibile_when_can_spin || (g.spin_count && g.spin_count > 0)
6696
+ * );
6697
+ *
6698
+ * for (const g of visible) {
6699
+ * console.log('[smartico] render game card', g.id, '—', g.name,
6700
+ * '— type:', g.saw_game_type,
6701
+ * '— buy-in:', g.saw_buyin_type,
6702
+ * '— spins available:', g.spin_count ?? 0,
6703
+ * '— jackpot:', g.jackpot_current, g.jackpot_symbol);
6704
+ * }
6705
+ * ```
5367
6706
  */
5368
6707
  async getMiniGames({
5369
6708
  onUpdate
@@ -5374,19 +6713,73 @@ class WSAPIMiniGames extends WSAPIRaffles {
5374
6713
  return OCache.use(onUpdateContextKey.Saw, ECacheContext.WSAPI, () => this.api.sawGetTemplatesT(this.userExtId), CACHE_DATA_SEC);
5375
6714
  }
5376
6715
  /**
5377
- * Returns the list of mini-games based on the provided parameters. "Limit" and "offset" indicate the range of items to be fetched.
5378
- * The maximum number of items per request is limited to 20.
5379
- * You can leave this params empty and by default it will return list of mini-games ranging from 0 to 20.
5380
- * The returned list of mini-games history is cached for 30 seconds.
6716
+ * Returns a paginated, newest-first list of the user's past
6717
+ * mini-game spins — each row carries the won prize ID, the
6718
+ * client-side `request_id` used for the spin, and a
6719
+ * server-recorded `is_claimed` flag (`true` if the spin has been
6720
+ * acknowledged). Optionally filter to a single template via
6721
+ * `saw_template_id`.
6722
+ *
6723
+ * Use this to power a "My prizes" / win-history surface, or to
6724
+ * recover unacknowledged spins (rows with `is_claimed === false`
6725
+ * carry a `client_request_id` you can pass to
6726
+ * {@link miniGameWinAcknowledgeRequest}).
5381
6727
  *
5382
- * **Example**:
5383
- * ```
5384
- * _smartico.api.getMiniGamesHistory().then((result) => {
5385
- * console.log(result);
5386
- * });
5387
- * ```
6728
+ * @remarks
6729
+ * **Pagination**
6730
+ * `limit` defaults to 20, `offset` defaults to 0. Sort order is
6731
+ * `create_date` DESC (newest first) — no client-side re-sort
6732
+ * required. For "load more" pagination, advance `offset` by the
6733
+ * page size on each subsequent call.
6734
+ *
6735
+ * The underlying protocol carries a `hasMore` boolean on the
6736
+ * response, but the SDK strips it from the public surface —
6737
+ * detect end of list when the returned array length is less than
6738
+ * `limit`.
6739
+ *
6740
+ * **`is_claimed` semantics**
6741
+ * Maps directly to the server's "acknowledge_date is non-null"
6742
+ * state. A spin where the auto-acknowledge fire-and-forget
6743
+ * succeeded shows `is_claimed: true`; a spin where the
6744
+ * acknowledge was lost (network drop) or where the user is on an
6745
+ * explicit-acknowledge flow shows `is_claimed: false` with a
6746
+ * usable `client_request_id`. A server-side fallback job
6747
+ * auto-acknowledges stale rows every ~60 seconds — so even
6748
+ * "lost" prizes are eventually delivered without consumer action.
6749
+ *
6750
+ * **Cache TTL**: the SDK caches the response for 30 seconds.
6751
+ * Cache is invalidated implicitly when a new spin or acknowledge
6752
+ * response lands.
6753
+ *
6754
+ * **Idempotency / Side effects**: safe. Read-only.
6755
+ *
6756
+ * **UI guidance**: see [UI Guide — `getMiniGamesHistory`](../../docs/ui/minigames/UIGuide_getMiniGamesHistory.md).
5388
6757
  *
5389
- * **Visitor mode: not supported**
6758
+ * **Visitor mode**: not supported.
6759
+ *
6760
+ * @param params Optional pagination + filter bag.
6761
+ * @param params.limit Page size. Defaults to 20.
6762
+ * @param params.offset Number of rows to skip. Defaults to 0.
6763
+ * @param params.saw_template_id When set, scopes the history to
6764
+ * a single template's spins.
6765
+ *
6766
+ * @returns Promise resolving to `TSawHistory[]` in newest-first
6767
+ * order. Empty when the user has no history.
6768
+ *
6769
+ * @example
6770
+ * ```ts
6771
+ * const history = await window._smartico.api.getMiniGamesHistory({ limit: 20 });
6772
+ *
6773
+ * // Show unacknowledged spins with a Claim CTA.
6774
+ * const unacknowledged = history.filter(h => !h.is_claimed);
6775
+ * console.log('[smartico] surface a "Claim" CTA on these', unacknowledged.length, 'history rows:',
6776
+ * unacknowledged.map(h => h.client_request_id));
6777
+ *
6778
+ * // Load-more pagination — advance offset by the prior page size.
6779
+ * const page2 = await window._smartico.api.getMiniGamesHistory({ limit: 20, offset: 20 });
6780
+ * console.log('[smartico] page 2 loaded —', page2.length, 'more rows;',
6781
+ * page2.length < 20 ? 'end of list reached, hide "Load more"' : 'keep "Load more" visible');
6782
+ * ```
5390
6783
  */
5391
6784
  async getMiniGamesHistory({
5392
6785
  limit,
@@ -5396,18 +6789,164 @@ class WSAPIMiniGames extends WSAPIRaffles {
5396
6789
  return OCache.use(onUpdateContextKey.SAWHistory, ECacheContext.WSAPI, () => this.api.getSawWinningHistoryT(this.userExtId, limit, offset, saw_template_id), CACHE_DATA_SEC);
5397
6790
  }
5398
6791
  /**
5399
- * Plays the specified by template_id mini-game on behalf of user and returns prize_id or err_code
5400
- * After playMiniGame is called, you can call getMiniGames to get the list of mini-games.The returned list of mini-games is cached for 30 seconds. But you can pass the onUpdate callback as a parameter. Note that each time you call playMiniGame with a new onUpdate callback, the old one will be overwritten by the new one.
5401
- * The onUpdate callback will be called on available spin count change, if mini-game has increasing jackpot per spin or won prize is spin/jackpot and if max count of the available user spins equals one, also if the spins were issued to the user manually in the BO. Updated templates will be passed to onUpdate callback.
6792
+ * Plays one round of a mini-game template — runs the server's
6793
+ * randomised prize selection, deducts the buy-in cost (if any),
6794
+ * and returns the won `prize_id`. After the play resolves, the SDK
6795
+ * automatically fires a server-side acknowledgement on a
6796
+ * fire-and-forget basis, marking the spin as "delivered" so the
6797
+ * prize takes effect.
6798
+ *
6799
+ * Works for all `SAWGameType` values EXCEPT:
6800
+ * - `PrizeDrop` — prizes arrive server-pushed; do NOT call
6801
+ * `playMiniGame` for this type.
6802
+ * - `MatchX` / `Quiz` — prediction submission goes through a
6803
+ * separate games server; `playMiniGame` is not the entry point.
5402
6804
  *
5403
- * **Example**:
5404
- * ```
5405
- * _smartico.api.playMiniGame(55).then((result) => {
5406
- * console.log(result);
6805
+ * @remarks
6806
+ * **Preconditions**
6807
+ * Read the candidate template from {@link getMiniGames} and check:
6808
+ * - `spin_count > 0` for spin-based templates
6809
+ * (`saw_buyin_type === 'spins'`).
6810
+ * - Sufficient balance for paid templates — points / gems /
6811
+ * diamonds from {@link getUserProfile} against
6812
+ * `buyin_cost_points` / `_gems` / `_diamonds`.
6813
+ * - `next_available_spin_ts` is in the past (or unset) — the
6814
+ * server enforces a per-period max-attempts cap.
6815
+ * - Template is currently in its active period
6816
+ * (operator-configured `activeFromDate` / `activeTillDate`).
6817
+ *
6818
+ * **Error codes** (in `err_code`, typed as {@link SAWSpinErrorCode})
6819
+ * - `0` (`SAW_OK`) — success; `prize_id` identifies the won prize
6820
+ * in `template.prizes`.
6821
+ * - `40001` (`SAW_NO_SPINS`) — user has no spin attempts for a
6822
+ * spin-based template.
6823
+ * - `40002` (`SAW_PRIZE_POOL_EMPTY`) — all prize slots in the
6824
+ * template are depleted.
6825
+ * - `40003` (`SAW_NOT_ENOUGH_POINTS`) — insufficient points for a
6826
+ * points-cost template.
6827
+ * - `40004` (`SAW_FAILED_MAX_SPINS_REACHED`) — per-period
6828
+ * max-attempts cap reached. The server's
6829
+ * `first_spin_in_period` value (exposed in
6830
+ * {@link TMiniGamePlayBatchResult}; not surfaced in the single
6831
+ * `TMiniGamePlayResult`) marks the period start.
6832
+ * - `40007` (`SAW_TEMPLATE_NOT_ACTIVE`) — template outside its
6833
+ * active time window.
6834
+ * - `40009` (`SAW_NOT_IN_SEGMENT`) — user fails the template's
6835
+ * visibility segment check, OR (for lootbox variants) the day's
6836
+ * prize pool is empty.
6837
+ * - `40011` (`SAW_NO_BALANCE_GEMS`) — insufficient gems.
6838
+ * - `40012` (`SAW_NO_BALANCE_DIAMONDS`) — insufficient diamonds.
6839
+ * - `-40001` (`SAW_VISITOR_STOP_SPIN_REQUEST`) — visitor session
6840
+ * tried to play a non-visitor-mode template. The default UI
6841
+ * silently stops the game; no prize awarded.
6842
+ * - other non-zero — generic server error. Surface `err_message`.
6843
+ *
6844
+ * **Prize handling**
6845
+ * On `err_code === 0`, `prize_id` matches an entry in
6846
+ * `template.prizes`. Look up the prize's `prize_type`
6847
+ * ({@link MiniGamePrizeTypeName}) to decide UI treatment:
6848
+ * - `'no-prize'` — the user spun but won nothing (an operator-
6849
+ * configured "loss" slot). Buy-in is still deducted.
6850
+ * - `'points'` / `'gems-and-diamonds'` / `'spin'` — value
6851
+ * credited to the user's profile / spin balance; visible on
6852
+ * the next refresh.
6853
+ * - `'bonus'` — surfaces via {@link getBonuses}.
6854
+ * - `'jackpot'` — drains the template's `jackpot_current` to the
6855
+ * user; resets the accumulator.
6856
+ * - `'raffle-ticket'` — adds tickets to the relevant raffle.
6857
+ * - `'mission'` / `'change-level'` — visible via the
6858
+ * corresponding domain methods.
6859
+ * - `'manual'` — operator delivers offline.
6860
+ *
6861
+ * **Auto-acknowledge**
6862
+ * The SDK calls the server's acknowledge endpoint immediately
6863
+ * after the spin response, on a fire-and-forget basis (not
6864
+ * awaited). This marks the spin as "delivered" so prize effects
6865
+ * take hold. If the auto-acknowledge fails (network drop), the
6866
+ * spin appears as `is_claimed: false` in
6867
+ * {@link getMiniGamesHistory}; the consumer can recover it by
6868
+ * passing the `client_request_id` to
6869
+ * {@link miniGameWinAcknowledgeRequest}. A server-side fallback
6870
+ * job auto-acknowledges stale spins every ~60 seconds, so prizes
6871
+ * are eventually delivered even without consumer action.
6872
+ *
6873
+ * **Idempotency**: NOT idempotent. A double-click sends two
6874
+ * spins and deducts the buy-in twice. The SDK does NOT guard
6875
+ * against rapid clicks at the public API level — guard the call
6876
+ * site with an in-flight flag. The server enforces a per-user
6877
+ * per-template lock that serialises concurrent requests, so two
6878
+ * simultaneous calls won't be processed in parallel, but they
6879
+ * will both be processed sequentially (i.e. both deduct).
6880
+ *
6881
+ * **Refresh after success (and after failure)**
6882
+ * The SDK automatically refreshes the templates cache on every
6883
+ * spin response and fires any `onUpdate` callback registered via
6884
+ * {@link getMiniGames} or this method. After `err_code === 0`, the
6885
+ * affected template's `spin_count` / `jackpot_current` /
6886
+ * `next_available_spin_ts` reflect the new state on the
6887
+ * refreshed array.
6888
+ *
6889
+ * **Side effects** (on `err_code === 0`)
6890
+ * - Buy-in deducted from the user's balance (points / gems /
6891
+ * diamonds / spin tickets — depending on `saw_buyin_type`).
6892
+ * Balance updates flow via the user-properties channel
6893
+ * (subscribe to {@link getUserProfile} `props_change` to
6894
+ * observe).
6895
+ * - Prize value credited per the prize's type (see "Prize
6896
+ * handling" above).
6897
+ * - A `minigame_attempt` analytics event fires server-side with
6898
+ * the result status.
6899
+ *
6900
+ * **UI guidance**: see [UI Guide — `playMiniGame`](../../docs/ui/minigames/UIGuide_playMiniGame.md).
6901
+ *
6902
+ * **Visitor mode**: not supported. Visitor sessions trying to
6903
+ * spin receive `SAW_VISITOR_STOP_SPIN_REQUEST (-40001)`.
6904
+ *
6905
+ * @param template_id The mini-game template ID (from
6906
+ * `TMiniGameTemplate.id`).
6907
+ * @param params Optional. Pass an `onUpdate` callback to
6908
+ * subscribe to subsequent template refreshes.
6909
+ * @returns `{ err_code, err_message, prize_id }` — success when
6910
+ * `err_code === 0`. `prize_id` is always populated; look
6911
+ * it up in `template.prizes` to interpret the prize.
6912
+ *
6913
+ * @example
6914
+ * ```ts
6915
+ * const games = await window._smartico.api.getMiniGames({
6916
+ * onUpdate: (refreshed) => console.log('[smartico] templates refreshed — re-render lobby', refreshed),
5407
6917
  * });
5408
- * ```
6918
+ * const game = games.find(g => g.id === templateId);
5409
6919
  *
5410
- * **Visitor mode: not supported**
6920
+ * if (!game) {
6921
+ * console.log('[smartico] template not in current list — refresh getMiniGames');
6922
+ * return;
6923
+ * }
6924
+ * if (game.saw_buyin_type === 'spins' && (game.spin_count ?? 0) === 0) {
6925
+ * console.log('[smartico] no spins available — disable Play button, show "No spins" message');
6926
+ * return;
6927
+ * }
6928
+ *
6929
+ * console.log('[smartico] play starting — set in-flight flag, start game animation (wheel spin / scratch / lootbox open / etc.)');
6930
+ * const r = await window._smartico.api.playMiniGame(game.id);
6931
+ * console.log('[smartico] play response received');
6932
+ *
6933
+ * if (r.err_code === 0) {
6934
+ * const prize = game.prizes.find(p => p.id === r.prize_id);
6935
+ * if (prize?.prize_type === 'no-prize') {
6936
+ * console.log('[smartico] user lost — animate to the no-prize slot and show "Better luck next time" modal');
6937
+ * } else {
6938
+ * console.log('[smartico] user won — animate to prize', prize?.name,
6939
+ * '— show acknowledge modal per template.acknowledge_type;',
6940
+ * 'getMiniGames onUpdate fires shortly with refreshed spin_count / jackpot');
6941
+ * }
6942
+ * } else if (r.err_code === 40004) {
6943
+ * console.error('[smartico] max spins reached — show countdown to next available spin (use playMiniGameBatch for first_spin_in_period if needed)');
6944
+ * } else if (r.err_code === -40001) {
6945
+ * console.log('[smartico] visitor stopped — silently end the game, no prize modal');
6946
+ * } else {
6947
+ * console.error('[smartico] play failed — show generic error with this message:', r.err_message);
6948
+ * }
6949
+ * ```
5411
6950
  */
5412
6951
  async playMiniGame(template_id, {
5413
6952
  onUpdate
@@ -5425,29 +6964,171 @@ class WSAPIMiniGames extends WSAPIRaffles {
5425
6964
  return o;
5426
6965
  }
5427
6966
  /**
5428
- * Sends the acknowledge request with specific client_request_id from minigame history in order to claim prize
5429
- * **Example**:
5430
- * ```
5431
- * _smartico.api.miniGameWinAcknowledgeRequest('2a189322-31bb-4119-b943-bx7868ff8dc3').then((result) => {
5432
- * console.log(result);
5433
- * });
6967
+ * Manually acknowledges a mini-game spin — marks the spin as
6968
+ * "delivered" so the prize takes effect server-side. Most consumers
6969
+ * do NOT need to call this method directly; {@link playMiniGame}
6970
+ * and {@link playMiniGameBatch} auto-acknowledge on success. Use
6971
+ * this only for recovery scenarios.
6972
+ *
6973
+ * @remarks
6974
+ * **When to call**
6975
+ * - **Lost auto-acknowledge** — when the WebSocket dropped between
6976
+ * the spin response and the auto-acknowledge fire-and-forget,
6977
+ * the spin shows `is_claimed: false` in
6978
+ * {@link getMiniGamesHistory}. Pass that row's
6979
+ * `client_request_id` here to deliver the prize.
6980
+ * - **Explicit-acknowledge UI** — for templates configured with
6981
+ * the `ExplicitAcknowledge` acknowledge type (operator
6982
+ * decision), the consumer's UI shows a "Claim" CTA after the
6983
+ * prize animation. Click handler calls this method with the
6984
+ * spin's `client_request_id` rather than auto-acknowledging.
6985
+ *
6986
+ * **No-op on already-acknowledged spins**
6987
+ * The server's acknowledge SQL matches on `acknowledge_date IS
6988
+ * NULL` — a duplicate call returns successfully with no effect.
6989
+ *
6990
+ * **Server-side fallback**
6991
+ * Even without ever calling this method, a server-side job
6992
+ * auto-acknowledges stale spins every ~60 seconds — so prizes are
6993
+ * eventually delivered. The `is_claimed` flag flips on the next
6994
+ * `getMiniGamesHistory` call after the job runs.
6995
+ *
6996
+ * **Idempotency**: safe. Repeated calls have no effect after the
6997
+ * first.
6998
+ *
6999
+ * **Side effects** (on first successful call for an unacknowledged
7000
+ * spin)
7001
+ * - Prize value credited per the prize's type (see
7002
+ * {@link playMiniGame} "Prize handling").
7003
+ * - The spin's `is_claimed` flag flips to `true` on the next
7004
+ * {@link getMiniGamesHistory} call.
7005
+ *
7006
+ * **Visitor mode**: not supported.
7007
+ *
7008
+ * @param request_id The `client_request_id` from a `TSawHistory`
7009
+ * row returned by {@link getMiniGamesHistory}.
7010
+ *
7011
+ * @example
7012
+ * ```ts
7013
+ * const history = await window._smartico.api.getMiniGamesHistory({ limit: 20 });
7014
+ * const unacknowledged = history.filter(h => !h.is_claimed);
7015
+ *
7016
+ * for (const row of unacknowledged) {
7017
+ * console.log('[smartico] re-acknowledging stale spin', row.client_request_id);
7018
+ * await window._smartico.api.miniGameWinAcknowledgeRequest(row.client_request_id);
7019
+ * }
7020
+ *
7021
+ * // Re-fetch history to see the updated is_claimed flags.
7022
+ * const fresh = await window._smartico.api.getMiniGamesHistory({ limit: 20 });
7023
+ * console.log('[smartico] history after recovery —', fresh.filter(h => !h.is_claimed).length, 'still unacknowledged');
5434
7024
  * ```
5435
7025
  */
5436
7026
  async miniGameWinAcknowledgeRequest(request_id) {
5437
7027
  return this.api.doAcknowledgeRequest(this.userExtId, request_id);
5438
7028
  }
5439
7029
  /**
5440
- * Plays the specified by template_id mini-game on behalf of user spin_count times and returns array of the prizes
5441
- * After playMiniGameBatch is called, you can call getMiniGames to get the list of mini-games. The returned list of mini-games is cached for 30 seconds. But you can pass the onUpdate callback as a parameter. Note that each time you call playMiniGameBatch with a new onUpdate callback, the old one will be overwritten by the new one.
5442
- * The onUpdate callback will be called on available spin count change, if mini-game has increasing jackpot per spin or won prize is spin/jackpot and if max count of the available user spins equals one, also if the spins were issued to the user manually in the BO. Updated templates will be passed to onUpdate callback.
7030
+ * Plays a mini-game template `spin_count` times in one round-trip.
7031
+ * Returns an array of per-spin results — each independent, each
7032
+ * with its own error code. Like {@link playMiniGame}, the SDK
7033
+ * auto-acknowledges every result on a fire-and-forget basis.
5443
7034
  *
5444
- * **Example**:
5445
- * ```
5446
- * _smartico.api.playMiniGameBatch(55, 10).then((result) => {
5447
- * console.log(result);
5448
- * });
7035
+ * Use this when a UI offers "spin N times" affordances
7036
+ * (multi-spin buttons, lootbox open-N flows). The server processes
7037
+ * each spin independently — there is no atomicity / rollback if
7038
+ * one fails.
7039
+ *
7040
+ * @remarks
7041
+ * **Result shape note** — `TMiniGamePlayBatchResult` uses
7042
+ * `errCode` / `errMessage` (camelCase) — DIFFERENT from
7043
+ * {@link TMiniGamePlayResult} which uses `err_code` / `err_message`
7044
+ * (snake_case). Branch on the camelCase keys when reading batch
7045
+ * results.
7046
+ *
7047
+ * **Per-spin independence**
7048
+ * The server iterates the requested spins and calls the prize
7049
+ * engine once per entry. If spin #2 fails with `SAW_NO_SPINS`,
7050
+ * spin #3 is still attempted — the server does NOT stop on first
7051
+ * error and does NOT roll back successful spins. Each
7052
+ * `results[i].errCode` is independent.
7053
+ *
7054
+ * Practical implication: if the user has 3 spins available and
7055
+ * requests 5, expect results 0-2 with `errCode === 0` and results
7056
+ * 3-4 with `errCode === SAW_NO_SPINS`.
7057
+ *
7058
+ * **Error codes**
7059
+ * Per-result `errCode` is from {@link SAWSpinErrorCode}. See
7060
+ * {@link playMiniGame} for the full table; the same codes apply
7061
+ * per spin.
7062
+ *
7063
+ * **`first_spin_in_period` field**
7064
+ * Populated on results where `errCode === SAW_FAILED_MAX_SPINS_REACHED`
7065
+ * (40004). The value is the epoch-ms timestamp of the user's
7066
+ * first spin in the current cooldown period. Compute the
7067
+ * countdown as:
7068
+ * `template.max_spins_period_ms - (Date.now() - first_spin_in_period)`.
7069
+ *
7070
+ * **`jackpot_amount` field**
7071
+ * Populated on results where the won prize is a jackpot
7072
+ * (`prize_type === 'jackpot'`). The amount the user just claimed
7073
+ * from the template's jackpot accumulator.
7074
+ *
7075
+ * **Auto-acknowledge**
7076
+ * The SDK fires a batch-acknowledge for all `request_id`s after
7077
+ * the batch response lands — fire-and-forget. Same recovery
7078
+ * semantics as {@link playMiniGame}: lost acknowledgements
7079
+ * become recoverable via {@link miniGameWinAcknowledgeRequest}
7080
+ * per row, and a server-side fallback job auto-acknowledges
7081
+ * stale spins every ~60 seconds.
7082
+ *
7083
+ * **Idempotency**: NOT idempotent. A double-click sends two
7084
+ * batches. Guard the call site with an in-flight flag.
7085
+ *
7086
+ * **Refresh after success (and after failure)**
7087
+ * Same as {@link playMiniGame} — the templates cache refreshes
7088
+ * and any `onUpdate` callback registered via {@link getMiniGames}
7089
+ * fires with the updated `spin_count` / `jackpot_current`.
7090
+ *
7091
+ * **Side effects** (per-spin, on `errCode === 0`)
7092
+ * Same as {@link playMiniGame} — buy-in deducted, prize value
7093
+ * credited per prize type.
7094
+ *
7095
+ * **UI guidance**: see [UI Guide — `playMiniGameBatch`](../../docs/ui/minigames/UIGuide_playMiniGameBatch.md).
7096
+ *
7097
+ * **Visitor mode**: not supported.
7098
+ *
7099
+ * @param template_id The mini-game template ID.
7100
+ * @param spin_count Number of spins to play. The server attempts
7101
+ * all `spin_count` spins — partial successes
7102
+ * are normal when the user runs out of
7103
+ * balance / spins mid-batch.
7104
+ * @param params Optional. Pass an `onUpdate` callback to
7105
+ * subscribe to subsequent template refreshes.
7106
+ * @returns `TMiniGamePlayBatchResult[]` — one entry per requested
7107
+ * spin, in request order. Note the camelCase `errCode` /
7108
+ * `errMessage` keys (see "Result shape note" above).
7109
+ *
7110
+ * @example
7111
+ * ```ts
7112
+ * const game = (await window._smartico.api.getMiniGames()).find(g => g.id === templateId);
7113
+ * if (!game) return;
7114
+ *
7115
+ * console.log('[smartico] batch play starting — set in-flight flag, animate the chosen N spins sequentially in UI');
7116
+ * const results = await window._smartico.api.playMiniGameBatch(game.id, 5);
7117
+ * console.log('[smartico] batch response received — clear in-flight flag');
7118
+ *
7119
+ * for (const [i, r] of results.entries()) {
7120
+ * if (r.errCode === 0) {
7121
+ * const prize = game.prizes.find(p => p.id === r.saw_prize_id);
7122
+ * console.log('[smartico] spin', i, 'won:', prize?.name,
7123
+ * r.jackpot_amount ? '(jackpot: ' + r.jackpot_amount + ')' : '');
7124
+ * } else if (r.errCode === 40004) {
7125
+ * const msTillNext = game.max_spins_period_ms! - (Date.now() - (r.first_spin_in_period ?? 0));
7126
+ * console.log('[smartico] spin', i, 'capped — ' + Math.ceil(msTillNext / 1000) + ' s until next allowed');
7127
+ * } else {
7128
+ * console.error('[smartico] spin', i, 'failed —', r.errCode, r.errMessage);
7129
+ * }
7130
+ * }
5449
7131
  * ```
5450
- * **Visitor mode: not supported**
5451
7132
  */
5452
7133
  async playMiniGameBatch(template_id, spin_count, {
5453
7134
  onUpdate
@@ -5482,79 +7163,297 @@ class WSAPIMiniGames extends WSAPIRaffles {
5482
7163
  /** @group Avatars */
5483
7164
  class WSAPIAvatars extends WSAPIMiniGames {
5484
7165
  /**
5485
- * Returns the list of avatars available in the catalog for the current user.
5486
- * The response includes both free avatars and earned/purchased ones.
5487
- * Avatars with `hide_until_achieved = true` and `is_given = false` should be hidden from the user.
5488
- * The result is cached for 30 seconds.
7166
+ * Returns the avatar catalog available to the current user — every pre-defined
7167
+ * avatar configured for the label, with per-user unlock flags (`is_given`, `is_in_use`)
7168
+ * applied server-side.
5489
7169
  *
5490
- * **Example**:
5491
- * ```
5492
- * _smartico.api.getAvatarsList().then((result) => {
5493
- * console.log(result);
5494
- * });
5495
- * ```
7170
+ * The returned `avatar_url` is the absolute CDN URL (the SDK prefixes relative paths
7171
+ * with the configured avatar domain). AI-customized variants are NOT returned here —
7172
+ * those live in {@link getAvatarsCustomized}.
5496
7173
  *
5497
- * **Visitor mode: not supported**
7174
+ * @remarks
7175
+ * **Preconditions**
7176
+ * - User must be authenticated. Visitor mode is not supported.
7177
+ * - No other prerequisite calls.
7178
+ *
7179
+ * **Visibility filter** (consumer-applied)
7180
+ * Avatars with `hide_until_achieved === true && is_given === false` should be filtered
7181
+ * out of the grid — they represent surprise unlocks that should remain hidden until
7182
+ * the user earns them. The default Smartico UI drops these before rendering.
7183
+ *
7184
+ * **Lock state** (consumer-applied)
7185
+ * Lock condition: `!is_given && avatar_source_type_id !== 0`. Free avatars
7186
+ * (`avatar_source_type_id === 0`) are always selectable; non-free avatars are locked
7187
+ * until `is_given` flips to true (operator grants via campaign, mission reward, store
7188
+ * purchase, etc.).
7189
+ *
7190
+ * **Sort order**
7191
+ * Server does NOT pre-sort. Caller must sort by `priority` ascending (lower = first).
7192
+ *
7193
+ * **Refresh**
7194
+ * - The SDK caches results for 30 seconds.
7195
+ * - {@link setAvatar} clears this cache on completion; the next call re-fetches.
7196
+ * - No push subscription — there is no `onUpdate` callback for avatar methods.
7197
+ * - When `is_given` flips server-side (e.g. the user earns a new avatar), the change
7198
+ * surfaces only on the next call after the 30-second cache window expires.
7199
+ *
7200
+ * **Visitor mode**: not supported.
7201
+ *
7202
+ * **UI guidance**: see [UI Guide — `getAvatarsList`](../../docs/ui/avatars/UIGuide_getAvatarsList.md).
7203
+ *
7204
+ * @returns Array of {@link TAvatarDefinition} — empty array if the feature is disabled
7205
+ * on the label.
7206
+ *
7207
+ * @example
7208
+ * ```ts
7209
+ * const avatars = await window._smartico.api.getAvatarsList();
7210
+ *
7211
+ * // Apply the standard visibility filter and sort.
7212
+ * const visible = avatars
7213
+ * .filter((a) => !a.hide_until_achieved || a.is_given)
7214
+ * .sort((a, b) => a.priority - b.priority);
7215
+ *
7216
+ * console.log('[smartico] visible avatars:', visible.length, '— render as a grid');
7217
+ *
7218
+ * const active = visible.find((a) => a.is_in_use);
7219
+ * if (active) {
7220
+ * console.log('[smartico] currently in use — overlay a checkmark on this card:', active.avatar_real_id);
7221
+ * }
7222
+ * ```
5498
7223
  */
5499
7224
  async getAvatarsList() {
5500
7225
  return OCache.use(onUpdateContextKey.AvatarsList, ECacheContext.WSAPI, () => this.api.avatarsGetListT(this.userExtId), CACHE_DATA_SEC);
5501
7226
  }
5502
7227
  /**
5503
- * Returns the list of AI-customized avatars for the current user.
5504
- * Each entry represents a previously generated AI customization for a specific base avatar.
5505
- * The result is cached for 30 seconds.
7228
+ * Returns the user's previously generated AI-customized avatar variants.
5506
7229
  *
5507
- * **Example**:
5508
- * ```
5509
- * _smartico.api.getAvatarsCustomized().then((result) => {
5510
- * console.log(result);
5511
- * });
5512
- * ```
7230
+ * Each entry is one AI-generated image derived from a base avatar in the catalog
7231
+ * (the base is identified by `avatar_real_id` and was originally a
7232
+ * {@link TAvatarDefinition} from {@link getAvatarsList}). These variants are stored
7233
+ * server-side under the user record and persist across sessions.
5513
7234
  *
5514
- * **Visitor mode: not supported**
7235
+ * @remarks
7236
+ * **Preconditions**
7237
+ * - User must be authenticated. Visitor mode is not supported.
7238
+ * - Has meaning only after at least one successful AI customization run (see below).
7239
+ * New users get an empty array.
7240
+ *
7241
+ * **Where customized avatars come from**
7242
+ * AI customization is **not** a WebSocket method on this SDK. It is a separate HTTP
7243
+ * POST to the operator-configured `avatar-customize` endpoint that takes a base avatar
7244
+ * plus a style prompt from {@link getAvatarPrompts} and returns a generated CDN URL.
7245
+ * Once that POST resolves successfully, the new variant is persisted and surfaces in
7246
+ * the next `getAvatarsCustomized()` call. The variant URL can then be passed to
7247
+ * {@link setAvatar} to activate it.
7248
+ *
7249
+ * **Relation to `getAvatarsList`**
7250
+ * AI variants do NOT appear in {@link getAvatarsList}. Catalog and variants are two
7251
+ * separate collections — combine them client-side if the UI needs a unified view per
7252
+ * base avatar.
7253
+ *
7254
+ * **Sort order**
7255
+ * Server does NOT pre-sort. Caller typically sorts by `dt_created` descending to show
7256
+ * the newest variant first.
7257
+ *
7258
+ * **Refresh**
7259
+ * - The SDK caches results for 30 seconds.
7260
+ * - {@link setAvatar} clears this cache on completion.
7261
+ * - After a successful AI customization POST, the consumer should re-call to surface
7262
+ * the new variant — the customization endpoint is outside the SDK so the SDK cannot
7263
+ * auto-invalidate.
7264
+ * - No push subscription.
7265
+ *
7266
+ * **Visitor mode**: not supported.
7267
+ *
7268
+ * **UI guidance**: see [UI Guide — `getAvatarsCustomized`](../../docs/ui/avatars/UIGuide_getAvatarsCustomized.md).
7269
+ *
7270
+ * @returns Array of {@link TAvatarCustomized} — empty if the user has never run AI
7271
+ * customization, or if the feature is disabled on the label.
7272
+ *
7273
+ * @example
7274
+ * ```ts
7275
+ * const variants = await window._smartico.api.getAvatarsCustomized();
7276
+ *
7277
+ * // Group by the base avatar so the UI can show "this base has 3 AI variants".
7278
+ * const byBase = new Map<number, typeof variants>();
7279
+ * for (const v of variants) {
7280
+ * const list = byBase.get(v.avatar_real_id) ?? [];
7281
+ * list.push(v);
7282
+ * byBase.set(v.avatar_real_id, list);
7283
+ * }
7284
+ *
7285
+ * // Newest-first within each base.
7286
+ * for (const list of byBase.values()) {
7287
+ * list.sort((a, b) => b.dt_created.localeCompare(a.dt_created));
7288
+ * }
7289
+ *
7290
+ * console.log('[smartico] AI variants by base avatar — render as a carousel under each base');
7291
+ * ```
5515
7292
  */
5516
7293
  async getAvatarsCustomized() {
5517
7294
  return OCache.use(onUpdateContextKey.AvatarsCustomized, ECacheContext.WSAPI, () => this.api.avatarsGetCustomizedT(this.userExtId), CACHE_DATA_SEC);
5518
7295
  }
5519
7296
  /**
5520
- * Returns the list of AI customization prompts (styles) available for avatar customization.
5521
- * Each prompt represents a visual style that can be applied to a base avatar.
5522
- * The result is cached for 30 seconds.
7297
+ * Returns the catalog of AI style prompts available for avatar customization.
5523
7298
  *
5524
- * **Example**:
5525
- * ```
5526
- * _smartico.api.getAvatarPrompts().then((result) => {
5527
- * console.log(result);
5528
- * });
5529
- * ```
7299
+ * Each entry is a "style" the user can apply to a base avatar (e.g. "Cartoon",
7300
+ * "Watercolor", "Cyberpunk"). Prompts carry a cost expressed in points, gems, or
7301
+ * diamonds — applying a prompt deducts the cost server-side as part of the AI
7302
+ * generation request.
5530
7303
  *
5531
- * **Visitor mode: not supported**
7304
+ * @remarks
7305
+ * **Preconditions**
7306
+ * - User must be authenticated. Visitor mode is not supported.
7307
+ * - No other prerequisite calls. The customization UI typically calls this on demand
7308
+ * when the user opens the style picker, not on app boot.
7309
+ *
7310
+ * **Cost handling**
7311
+ * Each prompt declares `cost_currency_type_id` (0 = points, 1 = gems, 2 = diamonds)
7312
+ * and `cost_value` (amount). The deduction happens server-side inside the
7313
+ * `avatar-customize` HTTP POST (the AI generation request, which is NOT part of this
7314
+ * SDK). The client should compare `cost_value` against the live balance from
7315
+ * {@link getUserProfile} to disable prompts the user cannot afford, but the server is
7316
+ * the source of truth and will reject under-funded requests authoritatively.
7317
+ *
7318
+ * **Refresh**
7319
+ * - The SDK caches results for 30 seconds.
7320
+ * - Not invalidated by {@link setAvatar} (prompts are catalog metadata, independent
7321
+ * of the user's active avatar).
7322
+ * - No push subscription.
7323
+ *
7324
+ * **Visitor mode**: not supported.
7325
+ *
7326
+ * **UI guidance**: see [UI Guide — `getAvatarPrompts`](../../docs/ui/avatars/UIGuide_getAvatarPrompts.md).
7327
+ *
7328
+ * @returns Array of {@link TAvatarPrompt} — empty if the AI customization feature is
7329
+ * disabled on the label.
7330
+ *
7331
+ * @example
7332
+ * ```ts
7333
+ * const [prompts, profile] = await Promise.all([
7334
+ * window._smartico.api.getAvatarPrompts(),
7335
+ * window._smartico.api.getUserProfile(),
7336
+ * ]);
7337
+ *
7338
+ * for (const p of prompts) {
7339
+ * // The consumer reads the matching balance from profile based on
7340
+ * // p.cost_currency_type_id (0=points, 1=gems, 2=diamonds).
7341
+ * console.log('[smartico] prompt', p.name, '— cost', p.cost_value, 'currency_type', p.cost_currency_type_id);
7342
+ * }
7343
+ * ```
5532
7344
  */
5533
7345
  async getAvatarPrompts() {
5534
7346
  return OCache.use(onUpdateContextKey.AvatarPrompts, ECacheContext.WSAPI, () => this.api.avatarsGetPromptsT(this.userExtId), CACHE_DATA_SEC);
5535
7347
  }
5536
7348
  /**
5537
- * Sets the specified avatar as the active avatar for the current user.
5538
- * Pass `avatar_url` (the `TAvatarDefinition.url` path or a CDN URL for AI-customized avatars)
5539
- * and `avatar_real_id` from the avatar catalog.
5540
- * After a successful call, the avatar list cache is cleared so the next `getAvatarsList()` call reflects `is_in_use`.
7349
+ * Activates the specified avatar for the current user. The chosen image becomes the
7350
+ * profile avatar — any subsequent {@link getUserProfile} call will reflect the new
7351
+ * `avatar_url`.
5541
7352
  *
5542
- * **Example**:
5543
- * ```
5544
- * _smartico.api.getAvatarsList().then((avatars) => {
5545
- * const avatar = avatars.find((a) => !a.hide_until_achieved || a.is_given);
5546
- * if (avatar) {
5547
- * _smartico.api.setAvatar({
5548
- * avatar_url: avatar.url,
5549
- * avatar_real_id: avatar.avatar_real_id,
5550
- * }).then((result) => {
5551
- * console.log(result);
5552
- * });
5553
- * }
7353
+ * Pass `avatar_url` (a {@link TAvatarDefinition.url} from the catalog or a
7354
+ * {@link TAvatarCustomized.url} from the AI-variant list) plus `avatar_real_id` of the
7355
+ * base avatar. The server validates ownership before applying — locked avatars are
7356
+ * rejected.
7357
+ *
7358
+ * @remarks
7359
+ * **Preconditions**
7360
+ * - User must be authenticated. Visitor mode is not supported.
7361
+ * - `avatar_url` must be non-empty and `avatar_real_id` must be truthy — the SDK
7362
+ * throws synchronously otherwise (the request never reaches the server).
7363
+ * - The avatar must exist in the catalog from {@link getAvatarsList} (or be an AI
7364
+ * variant of a base avatar the user already owns). Free avatars
7365
+ * (`avatar_source_type_id === 0`) and the user's currently active avatar bypass the
7366
+ * ownership check; all others require `is_given === true`.
7367
+ *
7368
+ * **Error codes** (in `err_code`)
7369
+ * - `0` — success; cache is cleared and the profile avatar updates immediately.
7370
+ * - `1` — avatar not found in the catalog. The `avatar_real_id` doesn't exist for
7371
+ * this label.
7372
+ * - `2` — avatar inactive (deactivated by the operator).
7373
+ * - `3` — outside the avatar's active time window
7374
+ * (`active_from_date` / `active_till_date`).
7375
+ * - `4` — user does not own this avatar (it isn't in their `is_given` set and isn't
7376
+ * a free / default avatar).
7377
+ * - other non-zero — generic server error; surface `err_message` if present.
7378
+ *
7379
+ * The default Smartico UI shows a hardcoded "Failed to apply avatar" toast on any
7380
+ * non-zero `err_code` and does not surface `err_message`. Custom UIs SHOULD branch
7381
+ * on the code and prefer `err_message` when present (it is the localized server
7382
+ * string).
7383
+ *
7384
+ * **Refresh after success**
7385
+ * - The SDK clears both the `getAvatarsList` and `getAvatarsCustomized` caches —
7386
+ * the next call to either returns fresh data with the new `is_in_use` flag set on
7387
+ * the chosen avatar.
7388
+ * - The server persists the new avatar URL on the user record; {@link getUserProfile}
7389
+ * reflects it on the next read in this session.
7390
+ * - **No push to other open sessions.** A second device / tab the user is logged in
7391
+ * on will not see the new avatar until it next calls {@link getUserProfile} or
7392
+ * re-identifies. Plan UI accordingly.
7393
+ *
7394
+ * **Idempotency**: safe to retry. Re-applying the same `avatar_real_id` returns
7395
+ * `err_code: 0` and produces no double side effects (no double-deduction — this
7396
+ * method does not charge any currency — and no duplicate log entries).
7397
+ *
7398
+ * **Side effects**: updates the user's stored `avatar_id` / `avatar_real_id`. Does
7399
+ * NOT cost points, gems, or diamonds — the cost lives on the separate AI
7400
+ * customization HTTP flow ({@link getAvatarPrompts}). Does NOT write a user-visible
7401
+ * entry to {@link getActivityLog}.
7402
+ *
7403
+ * **UI guidance**: see [UI Guide — `setAvatar`](../../docs/ui/avatars/UIGuide_setAvatar.md).
7404
+ *
7405
+ * **Visitor mode**: not supported.
7406
+ *
7407
+ * @param props.avatar_url Absolute CDN URL of the avatar image. Read from
7408
+ * {@link TAvatarDefinition.url} for catalog avatars or
7409
+ * {@link TAvatarCustomized.url} for AI variants.
7410
+ * @param props.avatar_real_id Numeric ID of the base avatar from
7411
+ * {@link TAvatarDefinition.avatar_real_id}. For AI
7412
+ * variants, this is the `avatar_real_id` of the base
7413
+ * they were derived from.
7414
+ * @returns {@link TSetAvatarResult} — check `err_code === 0` for success.
7415
+ *
7416
+ * @example
7417
+ * ```ts
7418
+ * const avatars = await window._smartico.api.getAvatarsList();
7419
+ * const target = avatars.find((a) => a.avatar_real_id === 7);
7420
+ *
7421
+ * if (!target) {
7422
+ * console.error('[smartico] avatar 7 not in catalog — hide the picker entry');
7423
+ * return;
7424
+ * }
7425
+ *
7426
+ * const isLocked = !target.is_given && target.avatar_source_type_id !== 0;
7427
+ * if (isLocked) {
7428
+ * console.log('[smartico] avatar is locked — show "earn this first" prompt instead of calling setAvatar');
7429
+ * return;
7430
+ * }
7431
+ *
7432
+ * const r = await window._smartico.api.setAvatar({
7433
+ * avatar_url: target.avatar_url,
7434
+ * avatar_real_id: target.avatar_real_id,
5554
7435
  * });
5555
- * ```
5556
7436
  *
5557
- * **Visitor mode: not supported**
7437
+ * switch (r.err_code) {
7438
+ * case 0:
7439
+ * console.log('[smartico] avatar applied — close picker, refresh header avatar');
7440
+ * break;
7441
+ * case 1:
7442
+ * console.error('[smartico] avatar not in catalog — refresh catalog and show error');
7443
+ * break;
7444
+ * case 2:
7445
+ * console.error('[smartico] avatar inactive — operator disabled it; hide from picker');
7446
+ * break;
7447
+ * case 3:
7448
+ * console.error('[smartico] outside time window — show "available on X" message');
7449
+ * break;
7450
+ * case 4:
7451
+ * console.error('[smartico] not owned — show unlock requirements');
7452
+ * break;
7453
+ * default:
7454
+ * console.error('[smartico] avatar apply failed — show generic error toast:', r.err_message);
7455
+ * }
7456
+ * ```
5558
7457
  */
5559
7458
  async setAvatar(props) {
5560
7459
  var _r$errCode;
@@ -5577,37 +7476,65 @@ class WSAPIAvatars extends WSAPIMiniGames {
5577
7476
  /** @group GamePick */
5578
7477
  class WSAPIGamePick extends WSAPIAvatars {
5579
7478
  /**
5580
- * Returns the active rounds for the specified MatchX or Quiz game.
5581
- * Each round includes its events (matches/questions) along with the current user's selections and scores.
5582
- *
5583
- * @param props.saw_template_id - The ID of the MatchX or Quiz game template
5584
- *
5585
- * **Response** `GamesApiResponse<GamePickRound[]>`:
5586
- * - `errCode` - 0 on success
5587
- * - `data` - Array of rounds, each containing:
5588
- * - `round_id`, `round_name` - Round identifier and display name
5589
- * - `open_date`, `last_bet_date` - Timestamps (ms) for round open and betting deadline
5590
- * - `is_active_now`, `is_resolved` - Round state flags
5591
- * - `round_status_id` - Round status: -1 (active), 2 (no more bets), 3 (all events resolved), 4 (round resolved)
5592
- * - `score_full_win`, `score_part_win`, `score_lost` - Scoring rules per prediction outcome
5593
- * - `user_score` - Current user's total score in this round
5594
- * - `user_placed_bet` - Whether the user has submitted predictions
5595
- * - `has_open_for_bet_events` - Whether there are events still open for betting
5596
- * - `events[]` - Array of events with `gp_event_id`, `market_type_id`, `event_meta` (team names, images, sport), `match_date`, `is_open_for_bets`, `odds_details`, and user selection fields
5597
- *
5598
- * **Example**:
5599
- * ```
5600
- * _smartico.api.gamePickGetActiveRounds({
5601
- * saw_template_id: 1083,
5602
- * }).then((result) => {
5603
- * console.log(result.data); // GamePickRound[]
5604
- * result.data.forEach(round => {
5605
- * console.log(round.round_name, round.events.length);
5606
- * });
5607
- * });
5608
- * ```
7479
+ * Returns every open round for a MatchX or Quiz game template, with the full
7480
+ * event list (matches / questions) and the current user's selections per event.
5609
7481
  *
5610
- * **Visitor mode: not supported**
7482
+ * Use to power the rounds-lobby screen — typically a list of round cards a user
7483
+ * can tap into to make predictions. For a single round, use
7484
+ * {@link gamePickGetActiveRound} (lighter payload when only one round is shown).
7485
+ *
7486
+ * @remarks
7487
+ * **Preconditions**
7488
+ * - User must be authenticated.
7489
+ * - `saw_template_id` must reference a MatchX or Quiz game template. The SDK
7490
+ * throws synchronously if it's missing.
7491
+ *
7492
+ * **Transport**
7493
+ * Game Pick methods are HTTP REST calls to a separate Game Pick games server
7494
+ * (not the main WebSocket). One consequence: there is no SDK cache and no push
7495
+ * subscription — every call is a fresh server roundtrip.
7496
+ *
7497
+ * **Round status**
7498
+ * Each round's `round_status_id` is a {@link GPRoundStatus} value. Rounds with
7499
+ * status `NoEventsDefined` (1) are filtered out server-side and never appear
7500
+ * here. Other values: `Other` (-1, normally open), `NoMoreBetsAllowed` (2),
7501
+ * `AllEventsResolved_ButNotRound` (3), `RoundResolved` (4 — appears here only
7502
+ * briefly during transition; use {@link gamePickGetHistory} for resolved rounds).
7503
+ *
7504
+ * **Error codes** (in `errCode`)
7505
+ * - `0` — success.
7506
+ * - `100002` — template not found (the `saw_template_id` doesn't reference a
7507
+ * MatchX/Quiz game).
7508
+ * - `100003` — no open rounds available for this template right now.
7509
+ * - `100000` — auth hash invalid; SDK session needs re-initialization.
7510
+ * - `100004` — generic server error.
7511
+ *
7512
+ * **Refresh**
7513
+ * - No cache; every call is live.
7514
+ * - No push subscription. Poll while the user is on the screen if you need
7515
+ * live round-status changes (typically every 30–60 s is enough).
7516
+ *
7517
+ * **Visitor mode**: not supported. The SDK throws synchronously on visitor
7518
+ * sessions.
7519
+ *
7520
+ * **UI guidance**: see [UI Guide — `gamePickGetActiveRounds`](../../docs/ui/gamepick/UIGuide_gamePickGetActiveRounds.md).
7521
+ *
7522
+ * @param props.saw_template_id ID of the MatchX or Quiz game template.
7523
+ * @returns Wrapped {@link GamesApiResponse} of {@link GamePickRound} array.
7524
+ *
7525
+ * @example
7526
+ * ```ts
7527
+ * const r = await window._smartico.api.gamePickGetActiveRounds({ saw_template_id: 1083 });
7528
+ *
7529
+ * if (r.errCode !== 0) {
7530
+ * console.error('[smartico] active rounds fetch failed — show empty rounds lobby:', r.errMessage);
7531
+ * return;
7532
+ * }
7533
+ *
7534
+ * for (const round of r.data ?? []) {
7535
+ * console.log('[smartico] render a round card:', round.round_name, '— events:', round.events.length, 'user has placed bet:', round.user_placed_bet);
7536
+ * }
7537
+ * ```
5611
7538
  */
5612
7539
  async gamePickGetActiveRounds(props) {
5613
7540
  if (!props.saw_template_id) {
@@ -5616,29 +7543,56 @@ class WSAPIGamePick extends WSAPIAvatars {
5616
7543
  return this.api.gpGetActiveRounds(props.saw_template_id);
5617
7544
  }
5618
7545
  /**
5619
- * Returns a single active round for the specified MatchX or Quiz game.
5620
- * The round includes full event details with the current user's selections.
7546
+ * Returns a single round (events, user selections, scoring rules) for a
7547
+ * MatchX or Quiz game. Use to power the round-detail / prediction-input
7548
+ * screen the user lands on after picking a round from the lobby.
5621
7549
  *
5622
- * @param props.saw_template_id - The ID of the MatchX or Quiz game template
5623
- * @param props.round_id - The specific round to retrieve
7550
+ * Same shape as one entry of {@link gamePickGetActiveRounds}; cheaper when the
7551
+ * UI only shows one round at a time.
5624
7552
  *
5625
- * **Response** `GamesApiResponse<GamePickRound>`:
5626
- * - `errCode` - 0 on success
5627
- * - `data` - Single round object with the same structure as in `gamePickGetActiveRounds`,
5628
- * including `events[]` with full event details, user selections, and resolution info
7553
+ * @remarks
7554
+ * **Preconditions**
7555
+ * - User must be authenticated. Visitor mode not supported.
7556
+ * - `saw_template_id` and `round_id` are mandatory — the SDK throws if either
7557
+ * is missing.
7558
+ *
7559
+ * **Error codes** (in `errCode`)
7560
+ * - `0` — success.
7561
+ * - `4` — round / event not found (e.g. round resolved after the last
7562
+ * `gamePickGetActiveRounds` poll); non-fatal — retry after a fresh active-
7563
+ * rounds fetch.
7564
+ * - `100002` — template not found.
7565
+ * - `100000` — auth hash invalid.
7566
+ * - `100004` — generic server error.
7567
+ *
7568
+ * **Refresh**
7569
+ * - No cache; every call is live.
7570
+ * - Re-call after {@link gamePickSubmitSelection} or
7571
+ * {@link gamePickSubmitSelectionQuiz} to surface the persisted predictions.
7572
+ * - Poll while the user is on the prediction screen if event resolution
7573
+ * updates are needed (typically every 30–60 s).
5629
7574
  *
5630
- * **Example**:
5631
- * ```
5632
- * _smartico.api.gamePickGetActiveRound({
5633
- * saw_template_id: 1083,
5634
- * round_id: 31652,
5635
- * }).then((result) => {
5636
- * console.log(result.data.round_name, result.data.events.length);
5637
- * console.log(result.data.user_score, result.data.user_placed_bet);
7575
+ * **Visitor mode**: not supported.
7576
+ *
7577
+ * **UI guidance**: see [UI Guide — `gamePickGetActiveRound`](../../docs/ui/gamepick/UIGuide_gamePickGetActiveRound.md).
7578
+ *
7579
+ * @param props.saw_template_id ID of the MatchX or Quiz game template.
7580
+ * @param props.round_id ID of the round to fetch.
7581
+ * @returns Wrapped {@link GamesApiResponse} of a single {@link GamePickRound}.
7582
+ *
7583
+ * @example
7584
+ * ```ts
7585
+ * const r = await window._smartico.api.gamePickGetActiveRound({
7586
+ * saw_template_id: 1083,
7587
+ * round_id: 31652,
5638
7588
  * });
5639
- * ```
5640
7589
  *
5641
- * **Visitor mode: not supported**
7590
+ * if (r.errCode === 0 && r.data) {
7591
+ * console.log('[smartico] render round detail page —', r.data.round_name, '— user has placed bet:', r.data.user_placed_bet, 'score so far:', r.data.user_score);
7592
+ * } else {
7593
+ * console.error('[smartico] round fetch failed — bounce back to the lobby and refresh:', r.errMessage);
7594
+ * }
7595
+ * ```
5642
7596
  */
5643
7597
  async gamePickGetActiveRound(props) {
5644
7598
  if (!props.saw_template_id) {
@@ -5650,29 +7604,52 @@ class WSAPIGamePick extends WSAPIAvatars {
5650
7604
  return this.api.gpGetActiveRound(props.saw_template_id, props.round_id);
5651
7605
  }
5652
7606
  /**
5653
- * Returns the history of all rounds (including resolved ones) for the specified MatchX or Quiz game.
5654
- * Each round contains full event details with results and the current user's predictions.
7607
+ * Returns the full round history (active + resolved) for a MatchX or Quiz game
7608
+ * template, newest-first. Each round carries the user's predictions plus the
7609
+ * server-resolved outcome on each event.
5655
7610
  *
5656
- * @param props.saw_template_id - The ID of the MatchX or Quiz game template
7611
+ * Use to power a "Past predictions" / history tab. For just the open rounds,
7612
+ * use {@link gamePickGetActiveRounds} (faster — skips resolved data).
5657
7613
  *
5658
- * **Response** `GamesApiResponse<GamePickRound[]>`:
5659
- * - `errCode` - 0 on success
5660
- * - `data` - Array of rounds ordered by `round_row_id` descending (newest first).
5661
- * Each round has the same structure as in `gamePickGetActiveRounds`, including resolved events
5662
- * with `resolution_type_id` (0=None, 2=Lost, 3=PartialWin, 4=FullWin) and `resolution_score`
7614
+ * @remarks
7615
+ * **Preconditions**
7616
+ * - User must be authenticated. Visitor mode not supported.
5663
7617
  *
5664
- * **Example**:
5665
- * ```
5666
- * _smartico.api.gamePickGetHistory({
5667
- * saw_template_id: 1083,
5668
- * }).then((result) => {
5669
- * result.data.forEach(round => {
5670
- * console.log(round.round_name, 'Score:', round.user_score, 'Resolved:', round.is_resolved);
5671
- * });
5672
- * });
5673
- * ```
7618
+ * **Sort order**
7619
+ * Server returns rounds newest-first. No client-side sort needed.
5674
7620
  *
5675
- * **Visitor mode: not supported**
7621
+ * **Per-event resolution**
7622
+ * Each event in the rounds carries `resolution_type_id` — values are from
7623
+ * {@link GamePickResolutionType}: `None` (0, not yet resolved), `Lost` (2),
7624
+ * `PartialWin` (3), `FullWin` (4). Use these to render result badges on
7625
+ * each event row.
7626
+ *
7627
+ * **Error codes** (in `errCode`)
7628
+ * - `0` — success.
7629
+ * - `100002` — template not found.
7630
+ * - `100000` — auth hash invalid.
7631
+ * - `100004` — generic server error.
7632
+ *
7633
+ * **Refresh**
7634
+ * - No cache.
7635
+ * - No push subscription. Re-call when the user opens the history tab.
7636
+ *
7637
+ * **Visitor mode**: not supported.
7638
+ *
7639
+ * **UI guidance**: see [UI Guide — `gamePickGetHistory`](../../docs/ui/gamepick/UIGuide_gamePickGetHistory.md).
7640
+ *
7641
+ * @param props.saw_template_id ID of the MatchX or Quiz game template.
7642
+ * @returns Wrapped {@link GamesApiResponse} of {@link GamePickRound} array,
7643
+ * newest-first.
7644
+ *
7645
+ * @example
7646
+ * ```ts
7647
+ * const r = await window._smartico.api.gamePickGetHistory({ saw_template_id: 1083 });
7648
+ *
7649
+ * for (const round of r.data ?? []) {
7650
+ * console.log('[smartico] history row —', round.round_name, 'score:', round.user_score, 'resolved:', round.is_resolved);
7651
+ * }
7652
+ * ```
5676
7653
  */
5677
7654
  async gamePickGetHistory(props) {
5678
7655
  if (!props.saw_template_id) {
@@ -5681,36 +7658,66 @@ class WSAPIGamePick extends WSAPIAvatars {
5681
7658
  return this.api.gpGetGamesHistory(props.saw_template_id);
5682
7659
  }
5683
7660
  /**
5684
- * Returns the leaderboard for a specific round within a MatchX or Quiz game.
5685
- * Use `round_id = -1` (AllRoundsGameBoardID) to get the season/overall leaderboard across all rounds.
5686
- *
5687
- * @param props.saw_template_id - The ID of the MatchX or Quiz game template
5688
- * @param props.round_id - The round to get the leaderboard for. Use -1 for overall/seasonal leaderboard
5689
- *
5690
- * **Response** `GamesApiResponse<GamePickRoundBoard>`:
5691
- * - `errCode` - 0 on success
5692
- * - `data` - Leaderboard object containing:
5693
- * - Round base fields (`round_id`, `round_name`, `open_date`, `last_bet_date`, etc.)
5694
- * - `users[]` - Ranked list of players, each with:
5695
- * - `ext_user_id`, `int_user_id` - User identifiers
5696
- * - `public_username`, `avatar_url` - Display info (usernames may be masked based on label settings)
5697
- * - `gp_position` - Leaderboard rank (null if not yet ranked)
5698
- * - `resolution_score` - Total score in this round
5699
- * - `full_wins_count`, `part_wins_count`, `lost_count` - Prediction outcome counts
5700
- * - `my_user` - Current user's entry (same fields as above), or null if user hasn't participated
5701
- *
5702
- * **Example**:
5703
- * ```
5704
- * _smartico.api.gamePickGetBoard({
5705
- * saw_template_id: 1083,
5706
- * round_id: 31652,
5707
- * }).then((result) => {
5708
- * console.log('Top players:', result.data.users);
5709
- * console.log('My position:', result.data.my_user?.gp_position);
7661
+ * Returns the leaderboard for a MatchX or Quiz game round — ranked list of
7662
+ * players plus the current user's entry (`my_user`). Pass `round_id: -1` for
7663
+ * the season / overall leaderboard across every round in the template.
7664
+ *
7665
+ * @remarks
7666
+ * **Preconditions**
7667
+ * - User must be authenticated. Visitor mode not supported.
7668
+ *
7669
+ * **`round_id` semantics**
7670
+ * - Positive `round_id` — per-round leaderboard scoped to that round's events.
7671
+ * - `-1` — season / overall leaderboard aggregating scores across every round
7672
+ * of the template.
7673
+ *
7674
+ * **`my_user`**
7675
+ * The current user's entry is returned separately from the `users[]` array
7676
+ * — `my_user` may be `null` if the user has never made a prediction on this
7677
+ * round / template. Use it to render the user's rank in a sticky footer or
7678
+ * "you are here" highlight regardless of pagination.
7679
+ *
7680
+ * **Username masking**
7681
+ * `public_username` on other players may be masked / anonymized by operator
7682
+ * label settings. Always pair with `avatar_url` so the user can recognize
7683
+ * themselves in the ranking by avatar even when usernames are hidden.
7684
+ *
7685
+ * **Error codes** (in `errCode`)
7686
+ * - `0` — success.
7687
+ * - `100002` — template not found.
7688
+ * - `100000` — auth hash invalid.
7689
+ * - `100004` — generic server error.
7690
+ *
7691
+ * **Refresh**
7692
+ * - No cache.
7693
+ * - No push subscription. Poll while the leaderboard is visible
7694
+ * (every 30–60 s) if you want live rank updates after match
7695
+ * resolution.
7696
+ *
7697
+ * **Visitor mode**: not supported.
7698
+ *
7699
+ * **UI guidance**: see [UI Guide — `gamePickGetBoard`](../../docs/ui/gamepick/UIGuide_gamePickGetBoard.md).
7700
+ *
7701
+ * @param props.saw_template_id ID of the MatchX or Quiz game template.
7702
+ * @param props.round_id Round ID, or `-1` for the season leaderboard.
7703
+ * @returns Wrapped {@link GamesApiResponse} of {@link GamePickRoundBoard}.
7704
+ *
7705
+ * @example
7706
+ * ```ts
7707
+ * const r = await window._smartico.api.gamePickGetBoard({
7708
+ * saw_template_id: 1083,
7709
+ * round_id: -1, // season leaderboard
5710
7710
  * });
5711
- * ```
5712
7711
  *
5713
- * **Visitor mode: not supported**
7712
+ * if (r.errCode === 0 && r.data) {
7713
+ * console.log('[smartico] render leaderboard rows for top', r.data.users.length, 'players');
7714
+ * if (r.data.my_user) {
7715
+ * console.log('[smartico] pin "you are rank #' + r.data.my_user.gp_position + '" footer to bottom');
7716
+ * } else {
7717
+ * console.log('[smartico] user not ranked — render "Play a round to join the leaderboard" CTA');
7718
+ * }
7719
+ * }
7720
+ * ```
5714
7721
  */
5715
7722
  async gamePickGetBoard(props) {
5716
7723
  if (!props.saw_template_id) {
@@ -5722,43 +7729,107 @@ class WSAPIGamePick extends WSAPIAvatars {
5722
7729
  return this.api.gpGetGameBoard(props.saw_template_id, props.round_id);
5723
7730
  }
5724
7731
  /**
5725
- * Submits score predictions for a round in a MatchX game.
5726
- * Sends the round object with user selections for all events at once.
5727
- * Each event must include `team1_user_selection` and `team2_user_selection` representing predicted scores.
5728
- * If the user hasn't placed bets before, one game attempt (spin) will be consumed.
5729
- * Predictions can be edited until each match starts (if `allow_edit_answers` is enabled on the round).
5730
- *
5731
- * @param props.saw_template_id - The ID of the MatchX game template
5732
- * @param props.round - Round object containing `round_id` and `events[]`. Typically obtained from `gamePickGetActiveRound`
5733
- * and modified with user predictions. Each event needs: `gp_event_id`, `team1_user_selection`, `team2_user_selection`
5734
- *
5735
- * **Response** `GamesApiResponse<GamePickRound>`:
5736
- * - `errCode` - 0 on success. Non-zero codes indicate errors (e.g. not enough points/attempts)
5737
- * - `data` - Updated round with all events reflecting the submitted selections.
5738
- * `user_placed_bet` will be `true`, `has_not_submitted_changes` will be `false`
5739
- *
5740
- * **Example**:
5741
- * ```
5742
- * _smartico.api.gamePickGetActiveRound({
5743
- * saw_template_id: 1190,
5744
- * round_id: 38665,
5745
- * }).then((roundData) => {
5746
- * const round = roundData.data;
5747
- * round.events = round.events.map(e => ({
5748
- * gp_event_id: e.gp_event_id,
5749
- * team1_user_selection: 1,
5750
- * team2_user_selection: 0,
5751
- * }));
5752
- * _smartico.api.gamePickSubmitSelection({
5753
- * saw_template_id: 1190,
5754
- * round: round,
5755
- * }).then((result) => {
5756
- * console.log(result.data.user_placed_bet); // true
5757
- * });
7732
+ * Submits all score predictions for a round in a MatchX game in one shot.
7733
+ * Each event in the round must carry `team1_user_selection` and
7734
+ * `team2_user_selection` (the predicted scores).
7735
+ *
7736
+ * On the user's FIRST submit for a round, the server charges one buy-in (one
7737
+ * "spin" / one currency deduction based on the template's `saw_buyin_type_id`).
7738
+ * Subsequent edits within the same round are free and only persist changed
7739
+ * selections.
7740
+ *
7741
+ * @remarks
7742
+ * **Preconditions**
7743
+ * - User must be authenticated. Visitor mode not supported.
7744
+ * - `saw_template_id` and `props.round.round_id` are mandatory — SDK throws if
7745
+ * either is missing.
7746
+ * - Each event passed must include `gp_event_id` and the two
7747
+ * `team{1,2}_user_selection` fields. Missing events default to "no change".
7748
+ *
7749
+ * **Error codes** (in `errCode`)
7750
+ * - `0` — success; the response `data` is the updated round with
7751
+ * `user_placed_bet === true` and `has_not_submitted_changes === false`.
7752
+ * - `3` (`NoBetsUpdatedOnSubmit`) — submit produced no changes, OR every event
7753
+ * is past its `last_bet_date` deadline. The buy-in is NOT consumed; the
7754
+ * current round is still returned in `data`.
7755
+ * - `4` — round / event not found; refresh state and retry.
7756
+ * - `100002` — template not found.
7757
+ * - `100000` — auth hash invalid.
7758
+ * - `100004` — generic server error.
7759
+ *
7760
+ * Buy-in failures pass through the {@link SAWSpinErrorCode} values on first
7761
+ * submit:
7762
+ * - `40001` — no spins left (when `saw_buyin_type_id` is Spins).
7763
+ * - `40003` — insufficient points.
7764
+ * - `40004` — global max-spins cap hit.
7765
+ * - `40007` — template outside its active date window.
7766
+ * - `40009` — user excluded by segment rule.
7767
+ * - `40011` — insufficient gems.
7768
+ * - `40012` — insufficient diamonds.
7769
+ *
7770
+ * **Idempotency**
7771
+ * Re-submitting identical selections returns `errCode: 3` (no changes); the
7772
+ * buy-in is consumed exactly once per round-per-user. Re-submitting after
7773
+ * `last_bet_date` also returns `errCode: 3` without consuming the buy-in.
7774
+ *
7775
+ * **Side effects**
7776
+ * - First successful submit deducts the buy-in (Points / Gems / Diamonds /
7777
+ * one Spin) per `saw_buyin_type_id`.
7778
+ * - Reports a `minigame_matchx_prediction` event to the operator's event log.
7779
+ * - Updates the round's participant counter (`users_cnt`) and per-event
7780
+ * counters (`num_users`).
7781
+ * - Score is awarded after the operator resolves the round
7782
+ * ({@link GPRoundStatus} = `RoundResolved`).
7783
+ *
7784
+ * **Visitor mode**: not supported.
7785
+ *
7786
+ * **UI guidance**: see [UI Guide — `gamePickSubmitSelection`](../../docs/ui/gamepick/UIGuide_gamePickSubmitSelection.md).
7787
+ *
7788
+ * @param props.saw_template_id ID of the MatchX game template.
7789
+ * @param props.round Round object with `round_id` + `events[]`
7790
+ * carrying the user's score predictions.
7791
+ * @returns Wrapped {@link GamesApiResponse} with the updated round.
7792
+ *
7793
+ * @example
7794
+ * ```ts
7795
+ * const ar = await window._smartico.api.gamePickGetActiveRound({
7796
+ * saw_template_id: 1190,
7797
+ * round_id: 38665,
5758
7798
  * });
5759
- * ```
7799
+ * if (ar.errCode !== 0 || !ar.data) {
7800
+ * console.error('[smartico] could not fetch round to submit — show error and bounce back');
7801
+ * return;
7802
+ * }
5760
7803
  *
5761
- * **Visitor mode: not supported**
7804
+ * const round = ar.data;
7805
+ * round.events = round.events.map(e => ({
7806
+ * gp_event_id: e.gp_event_id,
7807
+ * team1_user_selection: 1,
7808
+ * team2_user_selection: 0,
7809
+ * }));
7810
+ *
7811
+ * const r = await window._smartico.api.gamePickSubmitSelection({
7812
+ * saw_template_id: 1190,
7813
+ * round,
7814
+ * });
7815
+ *
7816
+ * switch (r.errCode) {
7817
+ * case 0:
7818
+ * console.log('[smartico] predictions saved — show confirmation, refresh leaderboard');
7819
+ * break;
7820
+ * case 3:
7821
+ * console.log('[smartico] nothing changed (or all events closed) — silent no-op');
7822
+ * break;
7823
+ * case 40001:
7824
+ * case 40003:
7825
+ * case 40011:
7826
+ * case 40012:
7827
+ * console.error('[smartico] insufficient balance / spins — show top-up CTA:', r.errMessage);
7828
+ * break;
7829
+ * default:
7830
+ * console.error('[smartico] submit failed — show generic error toast:', r.errMessage);
7831
+ * }
7832
+ * ```
5762
7833
  */
5763
7834
  async gamePickSubmitSelection(props) {
5764
7835
  var _props$round;
@@ -5771,42 +7842,83 @@ class WSAPIGamePick extends WSAPIAvatars {
5771
7842
  return this.api.gpSubmitSelection(props.saw_template_id, props.round, false);
5772
7843
  }
5773
7844
  /**
5774
- * Submits answers for a round in a Quiz game.
5775
- * Sends the round object with user answers for all events at once.
5776
- * Each event must include `user_selection` with the answer value (e.g. '1', '2', 'x', 'yes', 'no' — depending on the market type).
5777
- * If the user hasn't placed bets before, one game attempt (spin) will be consumed.
5778
- * Answers can be edited until each match starts (if `allow_edit_answers` is enabled on the round).
5779
- *
5780
- * @param props.saw_template_id - The ID of the Quiz game template
5781
- * @param props.round - Round object containing `round_id` and `events[]`. Typically obtained from `gamePickGetActiveRound`
5782
- * and modified with user answers. Each event needs: `gp_event_id`, `user_selection`
5783
- *
5784
- * **Response** `GamesApiResponse<GamePickRound>`:
5785
- * - `errCode` - 0 on success. Non-zero codes indicate errors (e.g. not enough points/attempts)
5786
- * - `data` - Updated round with all events reflecting the submitted answers.
5787
- * `user_placed_bet` will be `true`, `has_not_submitted_changes` will be `false`
5788
- *
5789
- * **Example**:
5790
- * ```
5791
- * _smartico.api.gamePickGetActiveRound({
5792
- * saw_template_id: 1183,
5793
- * round_id: 37974,
5794
- * }).then((roundData) => {
5795
- * const round = roundData.data;
5796
- * round.events = round.events.map(e => ({
5797
- * gp_event_id: e.gp_event_id,
5798
- * user_selection: 'x',
5799
- * }));
5800
- * _smartico.api.gamePickSubmitSelectionQuiz({
5801
- * saw_template_id: 1183,
5802
- * round: round,
5803
- * }).then((result) => {
5804
- * console.log(result.data.user_placed_bet); // true
5805
- * });
7845
+ * Submits all quiz answers for a round in one shot. Each event in the round
7846
+ * must carry `user_selection` (the answer value — typically `"1"` / `"2"` /
7847
+ * `"x"` for 1×2 markets, `"yes"` / `"no"` for binary markets, or any
7848
+ * operator-defined string for custom markets).
7849
+ *
7850
+ * Same first-submit buy-in behavior as {@link gamePickSubmitSelection} — one
7851
+ * buy-in is charged on the first successful submit; subsequent edits within
7852
+ * the same round are free.
7853
+ *
7854
+ * @remarks
7855
+ * **Preconditions**
7856
+ * - User must be authenticated. Visitor mode not supported.
7857
+ * - `saw_template_id` and `props.round.round_id` are mandatory — SDK throws if
7858
+ * either is missing.
7859
+ * - Each event must include `gp_event_id` and `user_selection`. Missing
7860
+ * events default to "no change".
7861
+ *
7862
+ * **Error codes** (in `errCode`)
7863
+ * - `0` — success.
7864
+ * - `3` (`NoBetsUpdatedOnSubmit`) — no changes, OR every event past
7865
+ * `last_bet_date`. Buy-in NOT consumed; current round returned in `data`.
7866
+ * - `4` — round / event not found.
7867
+ * - `100002` — template not found.
7868
+ * - `100000` — auth hash invalid.
7869
+ * - `100004` — generic server error.
7870
+ *
7871
+ * Buy-in failures use the same {@link SAWSpinErrorCode} pass-through values
7872
+ * as {@link gamePickSubmitSelection} — see that method's TSDoc for the
7873
+ * `40001` / `40003` / `40004` / `40007` / `40009` / `40011` / `40012`
7874
+ * semantics.
7875
+ *
7876
+ * **Idempotency**: identical to {@link gamePickSubmitSelection} — re-submit
7877
+ * returns `errCode: 3` and never double-charges the buy-in.
7878
+ *
7879
+ * **Side effects**: identical to {@link gamePickSubmitSelection}, with the
7880
+ * reported event being `minigame_quiz_prediction` instead of
7881
+ * `minigame_matchx_prediction`.
7882
+ *
7883
+ * **Visitor mode**: not supported.
7884
+ *
7885
+ * **UI guidance**: see [UI Guide — `gamePickSubmitSelectionQuiz`](../../docs/ui/gamepick/UIGuide_gamePickSubmitSelectionQuiz.md).
7886
+ *
7887
+ * @param props.saw_template_id ID of the Quiz game template.
7888
+ * @param props.round Round object with `round_id` + `events[]`
7889
+ * carrying the user's answer selections.
7890
+ * @returns Wrapped {@link GamesApiResponse} with the updated round.
7891
+ *
7892
+ * @example
7893
+ * ```ts
7894
+ * const ar = await window._smartico.api.gamePickGetActiveRound({
7895
+ * saw_template_id: 1183,
7896
+ * round_id: 37974,
7897
+ * });
7898
+ * if (ar.errCode !== 0 || !ar.data) {
7899
+ * console.error('[smartico] could not fetch round to submit answers — show error');
7900
+ * return;
7901
+ * }
7902
+ *
7903
+ * const round = ar.data;
7904
+ * round.events = round.events.map(e => ({
7905
+ * gp_event_id: e.gp_event_id,
7906
+ * user_selection: 'x',
7907
+ * }));
7908
+ *
7909
+ * const r = await window._smartico.api.gamePickSubmitSelectionQuiz({
7910
+ * saw_template_id: 1183,
7911
+ * round,
5806
7912
  * });
5807
- * ```
5808
7913
  *
5809
- * **Visitor mode: not supported**
7914
+ * if (r.errCode === 0) {
7915
+ * console.log('[smartico] quiz answers saved — show confirmation');
7916
+ * } else if (r.errCode === 3) {
7917
+ * console.log('[smartico] nothing changed — silent no-op');
7918
+ * } else {
7919
+ * console.error('[smartico] quiz submit failed:', r.errMessage);
7920
+ * }
7921
+ * ```
5810
7922
  */
5811
7923
  async gamePickSubmitSelectionQuiz(props) {
5812
7924
  var _props$round2;
@@ -5819,35 +7931,51 @@ class WSAPIGamePick extends WSAPIAvatars {
5819
7931
  return this.api.gpSubmitSelection(props.saw_template_id, props.round, true);
5820
7932
  }
5821
7933
  /**
5822
- * Returns the current user's profile information within the specified MatchX or Quiz game.
5823
- * The user record is synced from the Smartico platform into the games DB (synced every 1 minute).
5824
- * If the user doesn't exist in the games DB yet, it will be created automatically on first call.
5825
- *
5826
- * @param props.saw_template_id - The ID of the MatchX or Quiz game template
5827
- *
5828
- * **Response** `GamesApiResponse<GamePickUserInfo>`:
5829
- * - `errCode` - 0 on success
5830
- * - `data`:
5831
- * - `ext_user_id` - External user ID (Smartico internal numeric ID)
5832
- * - `int_user_id` - Internal user ID within the games system
5833
- * - `public_username` - Display name
5834
- * - `avatar_url` - User's avatar image URL
5835
- * - `last_wallet_sync_time` - Last time the user's balance was synced from Smartico
5836
- * - `ach_points_balance` - User's current points balance
5837
- * - `ach_gems_balance` - User's current gems balance
5838
- * - `ach_diamonds_balance` - User's current diamonds balance
5839
- * - `pubic_username_set` - Whether the user has set a custom username
5840
- *
5841
- * **Example**:
5842
- * ```
5843
- * _smartico.api.gamePickGetUserInfo({
5844
- * saw_template_id: 1083,
5845
- * }).then((result) => {
5846
- * console.log(result.data.public_username, result.data.ach_points_balance);
5847
- * });
5848
- * ```
7934
+ * Returns the current user's profile within the Game Pick system — display
7935
+ * name, avatar, balances, and last wallet-sync timestamp. The Game Pick server
7936
+ * maintains its own user record that mirrors the user's Smartico balances; the
7937
+ * mirror is updated at most once per minute, so `last_wallet_sync_time` tells
7938
+ * the consumer how stale the balances on the response are.
5849
7939
  *
5850
- * **Visitor mode: not supported**
7940
+ * On the user's first call for a given game, the Game Pick server lazily
7941
+ * provisions a user record — so this method doubles as a "register the user
7942
+ * with this game" call.
7943
+ *
7944
+ * @remarks
7945
+ * **Preconditions**
7946
+ * - User must be authenticated. Visitor mode not supported.
7947
+ *
7948
+ * **Balance freshness**
7949
+ * The balances returned here are the cached mirror — at most ~1 minute stale.
7950
+ * For an up-to-the-second view of Points / Gems / Diamonds, read from
7951
+ * {@link getUserProfile} instead. Use this method's balances only for
7952
+ * in-Game-Pick display.
7953
+ *
7954
+ * **Error codes** (in `errCode`)
7955
+ * - `0` — success.
7956
+ * - `100002` — template not found.
7957
+ * - `100000` — auth hash invalid.
7958
+ * - `100004` — generic server error.
7959
+ *
7960
+ * **Refresh**
7961
+ * - No cache. Server-side mirror refreshes at most every 60 s.
7962
+ * - No push subscription.
7963
+ *
7964
+ * **Visitor mode**: not supported.
7965
+ *
7966
+ * **UI guidance**: see [UI Guide — `gamePickGetUserInfo`](../../docs/ui/gamepick/UIGuide_gamePickGetUserInfo.md).
7967
+ *
7968
+ * @param props.saw_template_id ID of the MatchX or Quiz game template.
7969
+ * @returns Wrapped {@link GamesApiResponse} of {@link GamePickUserInfo}.
7970
+ *
7971
+ * @example
7972
+ * ```ts
7973
+ * const r = await window._smartico.api.gamePickGetUserInfo({ saw_template_id: 1083 });
7974
+ *
7975
+ * if (r.errCode === 0 && r.data) {
7976
+ * console.log('[smartico] render Game Pick header — name:', r.data.public_username, '— points:', r.data.ach_points_balance, '— synced:', r.data.last_wallet_sync_time);
7977
+ * }
7978
+ * ```
5851
7979
  */
5852
7980
  async gamePickGetUserInfo(props) {
5853
7981
  if (!props.saw_template_id) {
@@ -5856,38 +7984,56 @@ class WSAPIGamePick extends WSAPIAvatars {
5856
7984
  return this.api.gpGetUserInfo(props.saw_template_id);
5857
7985
  }
5858
7986
  /**
5859
- * Returns the game configuration and the list of all rounds for the specified MatchX or Quiz game.
5860
- * Includes the SAW template definition, label settings, and round metadata (without events).
5861
- *
5862
- * @param props.saw_template_id - The ID of the MatchX or Quiz game template
5863
- *
5864
- * **Response** `GamesApiResponse<GamePickGameInfo>`:
5865
- * - `errCode` - 0 on success
5866
- * - `data`:
5867
- * - `sawTemplate` - Game template configuration including:
5868
- * - `saw_template_id`, `saw_game_type_id` (6 = MatchX/Quiz)
5869
- * - `saw_template_ui_definition` - UI settings (name, ranking options, ask_for_username, etc.)
5870
- * - `saw_buyin_type_id` - Cost type (1=Free, 2=Points, 3=Gems, 4=Diamonds, 5=Spins)
5871
- * - `buyin_cost_points` - Cost per attempt
5872
- * - `spin_count` - Available attempts for the current user
5873
- * - `allRounds[]` - List of all rounds (metadata only, no events), each with:
5874
- * - `round_id`, `round_name`, `round_description`
5875
- * - `open_date`, `last_bet_date` - Timestamps (ms)
5876
- * - `is_active_now`, `is_resolved`, `round_status_id`
5877
- * - `labelInfo` - Label/brand configuration and settings
5878
- *
5879
- * **Example**:
5880
- * ```
5881
- * _smartico.api.gamePickGetGameInfo({
5882
- * saw_template_id: 1189,
5883
- * }).then((result) => {
5884
- * console.log(result.data.sawTemplate.saw_template_ui_definition.name);
5885
- * console.log('Rounds:', result.data.allRounds.length);
5886
- * console.log('Buy-in type:', result.data.sawTemplate.saw_buyin_type_id);
5887
- * });
5888
- * ```
7987
+ * Returns the game template configuration plus a metadata listing of every
7988
+ * round (without events). Use to power the game's welcome / lobby screen —
7989
+ * shows the template name, buy-in cost, the user's remaining attempts, and
7990
+ * the full round list.
5889
7991
  *
5890
- * **Visitor mode: not supported**
7992
+ * The `allRounds[]` here is a lightweight metadata-only view — to render
7993
+ * predictions or leaderboards, call {@link gamePickGetActiveRound} /
7994
+ * {@link gamePickGetActiveRounds} / {@link gamePickGetBoard} for the rounds
7995
+ * the user actually opens.
7996
+ *
7997
+ * @remarks
7998
+ * **Preconditions**
7999
+ * - User must be authenticated. Visitor mode not supported.
8000
+ *
8001
+ * **Buy-in semantics**
8002
+ * The `sawTemplate.saw_buyin_type_id` value determines which currency is
8003
+ * deducted on the user's first prediction in any round:
8004
+ * - Free — no deduction.
8005
+ * - Points — `buyin_cost_points` deducted from the user's points balance.
8006
+ * - Gems — deducted from the user's gems balance.
8007
+ * - Diamonds — deducted from the user's diamonds balance.
8008
+ * - Spins — one spin counted from `spin_count`.
8009
+ *
8010
+ * **Error codes** (in `errCode`)
8011
+ * - `0` — success.
8012
+ * - `100002` — template not found.
8013
+ * - `100000` — auth hash invalid.
8014
+ * - `100004` — generic server error.
8015
+ *
8016
+ * **Refresh**
8017
+ * - No cache.
8018
+ * - No push subscription. Re-call after a successful
8019
+ * {@link gamePickSubmitSelection} to refresh `spin_count`.
8020
+ *
8021
+ * **Visitor mode**: not supported.
8022
+ *
8023
+ * **UI guidance**: see [UI Guide — `gamePickGetGameInfo`](../../docs/ui/gamepick/UIGuide_gamePickGetGameInfo.md).
8024
+ *
8025
+ * @param props.saw_template_id ID of the MatchX or Quiz game template.
8026
+ * @returns Wrapped {@link GamesApiResponse} of {@link GamePickGameInfo}.
8027
+ *
8028
+ * @example
8029
+ * ```ts
8030
+ * const r = await window._smartico.api.gamePickGetGameInfo({ saw_template_id: 1189 });
8031
+ *
8032
+ * if (r.errCode === 0 && r.data) {
8033
+ * console.log('[smartico] render Game Pick lobby — title:', r.data.sawTemplate.saw_template_ui_definition.name);
8034
+ * console.log('[smartico] rounds available:', r.data.allRounds.length, '— buy-in type:', r.data.sawTemplate.saw_buyin_type_id, 'cost:', r.data.sawTemplate.buyin_cost_points);
8035
+ * }
8036
+ * ```
5891
8037
  */
5892
8038
  async gamePickGetGameInfo(props) {
5893
8039
  if (!props.saw_template_id) {
@@ -5896,35 +8042,73 @@ class WSAPIGamePick extends WSAPIAvatars {
5896
8042
  return this.api.gpGetGameInfo(props.saw_template_id);
5897
8043
  }
5898
8044
  /**
5899
- * Returns round data with events and picks for a specific user (identified by their internal user ID).
5900
- * Useful for viewing another user's predictions from the leaderboard.
5901
- * The `int_user_id` can be obtained from the `gamePickGetBoard` response (`users[].int_user_id`).
5902
- *
5903
- * @param props.saw_template_id - The ID of the MatchX or Quiz game template
5904
- * @param props.round_id - The round to get info for
5905
- * @param props.int_user_id - Internal user ID of the player whose predictions to view (from leaderboard data)
5906
- *
5907
- * **Response** `GamesApiResponse<GamePickRound>`:
5908
- * - `errCode` - 0 on success
5909
- * - `data` - Round object with the target user's selections.
5910
- * Same structure as `gamePickGetActiveRound`, but `user_selection`/`team1_user_selection`/`team2_user_selection`
5911
- * fields on events reflect the specified user's picks instead of the current user's.
5912
- * Events also include `resolution_type_id` (0=None, 2=Lost, 3=PartialWin, 4=FullWin) showing how each prediction was scored
5913
- *
5914
- * **Example**:
5915
- * ```
5916
- * _smartico.api.gamePickGetRoundInfoForUser({
5917
- * saw_template_id: 1083,
5918
- * round_id: 31652,
5919
- * int_user_id: 65653810,
5920
- * }).then((result) => {
5921
- * result.data.events.forEach(e => {
5922
- * console.log(e.event_meta.team1_name, 'vs', e.event_meta.team2_name, '→', e.user_selection);
5923
- * });
8045
+ * Returns a round with another player's predictions instead of the current
8046
+ * user's. The target player is identified by `int_user_id` (the Game Pick
8047
+ * system's internal user ID, obtained from {@link gamePickGetBoard}'s
8048
+ * `users[].int_user_id`).
8049
+ *
8050
+ * Use to power "view this player's picks" affordances opened from leaderboard
8051
+ * rows — community / social inspection of how a top-ranked user predicted.
8052
+ *
8053
+ * @remarks
8054
+ * **Preconditions**
8055
+ * - User must be authenticated. Visitor mode not supported.
8056
+ * - `saw_template_id`, `round_id`, and `int_user_id` are all mandatory — SDK
8057
+ * throws synchronously if any is missing.
8058
+ * - `int_user_id` must come from a leaderboard fetch in the same game
8059
+ * template; it is stable within a template but not portable across
8060
+ * templates.
8061
+ *
8062
+ * **Shape vs `gamePickGetActiveRound`**
8063
+ * Same {@link GamePickRound} structure, but `user_selection` /
8064
+ * `team1_user_selection` / `team2_user_selection` reflect the TARGET user's
8065
+ * predictions instead of the caller's. Each event still carries its own
8066
+ * `resolution_type_id` (see {@link GamePickResolutionType}) so the UI can
8067
+ * render result badges per pick.
8068
+ *
8069
+ * **Error codes** (in `errCode`)
8070
+ * - `0` — success.
8071
+ * - `4` — round / event / user not found.
8072
+ * - `100002` — template not found.
8073
+ * - `100000` — auth hash invalid.
8074
+ * - `100004` — generic server error.
8075
+ *
8076
+ * **Refresh**
8077
+ * - No cache. Re-call when opening a different leaderboard row's
8078
+ * "view picks" modal.
8079
+ * - No push subscription.
8080
+ *
8081
+ * **Visitor mode**: not supported.
8082
+ *
8083
+ * **UI guidance**: see [UI Guide — `gamePickGetRoundInfoForUser`](../../docs/ui/gamepick/UIGuide_gamePickGetRoundInfoForUser.md).
8084
+ *
8085
+ * @param props.saw_template_id ID of the MatchX or Quiz game template.
8086
+ * @param props.round_id ID of the round to inspect.
8087
+ * @param props.int_user_id Internal user ID from
8088
+ * `gamePickGetBoard.users[].int_user_id`.
8089
+ * @returns Wrapped {@link GamesApiResponse} with the target user's
8090
+ * {@link GamePickRound}.
8091
+ *
8092
+ * @example
8093
+ * ```ts
8094
+ * const board = await window._smartico.api.gamePickGetBoard({
8095
+ * saw_template_id: 1083, round_id: 31652,
5924
8096
  * });
5925
- * ```
8097
+ * const topPlayer = board.data?.users?.[0];
8098
+ * if (!topPlayer) return;
5926
8099
  *
5927
- * **Visitor mode: not supported**
8100
+ * const r = await window._smartico.api.gamePickGetRoundInfoForUser({
8101
+ * saw_template_id: 1083,
8102
+ * round_id: 31652,
8103
+ * int_user_id: topPlayer.int_user_id,
8104
+ * });
8105
+ *
8106
+ * if (r.errCode === 0 && r.data) {
8107
+ * for (const ev of r.data.events) {
8108
+ * console.log('[smartico] render row —', ev.event_meta.team1_name, 'vs', ev.event_meta.team2_name, '— picked:', ev.user_selection, '— resolution:', ev.resolution_type_id);
8109
+ * }
8110
+ * }
8111
+ * ```
5928
8112
  */
5929
8113
  async gamePickGetRoundInfoForUser(props) {
5930
8114
  if (!props.saw_template_id) {
@@ -5943,21 +8127,135 @@ class WSAPIGamePick extends WSAPIAvatars {
5943
8127
  /** @group LeaderBoard */
5944
8128
  class WSAPILeaderBoard extends WSAPIGamePick {
5945
8129
  /**
5946
- * Returns the leaderboard for the current type (default is Daily). If getPreviousPeriod is passed as true, a leaderboard for the previous period for the current type will be returned.
5947
- * For example, if the type is Weekly and getPreviousPeriod is true, a leaderboard for the previous week will be returned.
8130
+ * Returns the operator-configured standalone leaderboard for the
8131
+ * given period type — top-20 ranked entries, the current user's
8132
+ * own entry (when authenticated), and the configured prize table.
8133
+ * Use this to power a leaderboard page with daily / weekly /
8134
+ * monthly tabs.
5948
8135
  *
5949
- * **Example**:
5950
- * ```
5951
- * _smartico.api.getLeaderBoard(1).then((result) => {
5952
- * console.log(result);
5953
- * });
5954
- * ```
8136
+ * The leaderboard for each period type is configured separately by
8137
+ * the operator. A label may have any combination of DAILY, WEEKLY,
8138
+ * and MONTHLY boards configured (or none).
5955
8139
  *
5956
- * **Example in the Visitor mode**:
5957
- * ```
5958
- * _smartico.vapi('EN').getLeaderBoard(1).then((result) => {
5959
- * console.log(result);
5960
- * });
8140
+ * @remarks
8141
+ * **Return value note** — the runtime may return `undefined` if no
8142
+ * board is configured for the requested `periodType`, despite the
8143
+ * static return type being `Promise<LeaderBoardDetailsT>`. Defend
8144
+ * against that case in consumer code.
8145
+ *
8146
+ * **Preconditions**
8147
+ * Pass a `periodType` from {@link LeaderBoardPeriodType}
8148
+ * (`DAILY = 1`, `WEEKLY = 2`, `MONTHLY = 3`). Pass
8149
+ * `getPreviousPeriod: true` to fetch the most-recently-ended
8150
+ * period's snapshot (yesterday's results, last week's results,
8151
+ * last month's results) instead of the current live period. The
8152
+ * SDK only exposes one period back — older snapshots are not
8153
+ * reachable through this method.
8154
+ *
8155
+ * **Top-20 cap**
8156
+ * The server hard-caps `users[]` at 20 entries, ordered by
8157
+ * `position` ASC. Position values are server-computed (DENSE_RANK
8158
+ * over all participants); the truncation at 20 is for transport
8159
+ * size only — `position` may exceed 20 elsewhere.
8160
+ *
8161
+ * **The `me` field**
8162
+ * Always populated for authenticated users (even when they have
8163
+ * zero points in the period). `me.position === -1` is the signal
8164
+ * that the user is active but ranked outside the top 20 (or has no
8165
+ * activity yet). `me` is `undefined` for visitor-mode sessions.
8166
+ *
8167
+ * **Rewards are points only**
8168
+ * `rewards[].points` are gamification points credited to the user's
8169
+ * balance when the period finalizes. The leaderboard never awards
8170
+ * gems, diamonds, store items, or bonuses. The first element of
8171
+ * the array is the prize for place 1, the second for place 2, and
8172
+ * so on. The array length is the number of paid places.
8173
+ *
8174
+ * **Cache TTL — single shared key (known limitation)**
8175
+ * The SDK caches the response for 30 seconds under a SINGLE shared
8176
+ * key — calling `getLeaderBoard(DAILY)` followed by
8177
+ * `getLeaderBoard(WEEKLY)` within the same 30 s window returns the
8178
+ * cached DAILY result for the WEEKLY call. Same applies to
8179
+ * switching `getPreviousPeriod`. To force a fresh fetch when
8180
+ * switching periods or current/previous, call
8181
+ * `_smartico.api.clearCaches()` before the second call, or simply
8182
+ * wait out the TTL.
8183
+ *
8184
+ * **Period boundaries**
8185
+ * The server finalizes each period on a server-configurable
8186
+ * schedule: DAILY at midnight + operator-defined offset; WEEKLY
8187
+ * at the start of Monday; MONTHLY on the 1st of each month. At
8188
+ * finalization, the server distributes the configured `rewards[]`
8189
+ * points to the top placeholders and resets the live board. After
8190
+ * finalization, `getLeaderBoard(periodType, false)` returns the
8191
+ * NEW (empty) period's board; call with
8192
+ * `getPreviousPeriod: true` to see the just-ended standings.
8193
+ *
8194
+ * **Refresh model**
8195
+ * - One-shot fetch (no subscription).
8196
+ * - No push event refreshes the cache; finalization at the period
8197
+ * boundary is NOT pushed to the client.
8198
+ * - Poll manually if the consumer needs near-live state during an
8199
+ * in-progress period.
8200
+ *
8201
+ * **Idempotency / Side effects**: safe. Read-only.
8202
+ *
8203
+ * **UI guidance**: see [UI Guide — `getLeaderBoard`](../../docs/ui/leaderboard/UIGuide_getLeaderBoard.md).
8204
+ *
8205
+ * **Visitor mode**: supported. The same shape is returned, scoped
8206
+ * to the brand's public leaderboard. `me` is `undefined` for
8207
+ * visitors.
8208
+ *
8209
+ * @param periodType The board's period type
8210
+ * ({@link LeaderBoardPeriodType}).
8211
+ * Selects which pre-configured board to
8212
+ * fetch; the board itself is bound to one
8213
+ * period by the operator.
8214
+ * @param getPreviousPeriod When `true`, returns the most recent
8215
+ * finalized snapshot for that period type
8216
+ * (e.g. last week's results). Defaults to
8217
+ * `false` (current in-progress period).
8218
+ * @returns Promise resolving to `LeaderBoardDetailsT`.
8219
+ * **At runtime, may resolve to `undefined`**
8220
+ * when no board is configured for the
8221
+ * requested period type — guard against it.
8222
+ *
8223
+ * @example
8224
+ * ```ts
8225
+ * import { LeaderBoardPeriodType } from '@smartico/public-api';
8226
+ *
8227
+ * const board = await window._smartico.api.getLeaderBoard(LeaderBoardPeriodType.WEEKLY);
8228
+ *
8229
+ * if (!board) {
8230
+ * console.log('[smartico] no weekly board configured for this label — hide the leaderboard surface');
8231
+ * return;
8232
+ * }
8233
+ *
8234
+ * console.log('[smartico] render leaderboard tab —', board.name, '—', board.users.length, 'ranked entries (top 20 max)');
8235
+ *
8236
+ * // Render prize table.
8237
+ * for (const reward of board.rewards) {
8238
+ * console.log('[smartico] prize row — place', reward.place, '→', reward.points, 'points');
8239
+ * }
8240
+ *
8241
+ * // "Me" panel — sticky row showing the current user's rank.
8242
+ * if (board.me) {
8243
+ * if (board.me.position === -1) {
8244
+ * console.log('[smartico] user is unranked — show "You are unranked, earn points to enter the leaderboard"');
8245
+ * } else if (board.me.position > 20) {
8246
+ * console.log('[smartico] user is ranked', board.me.position, 'but outside the visible top-20 — show sticky "me" row with', board.me.points, 'points');
8247
+ * } else {
8248
+ * console.log('[smartico] user is in the top 20 at position', board.me.position, '— highlight their row in the main list');
8249
+ * }
8250
+ * }
8251
+ *
8252
+ * // Switch to previous period view — but bust the cache first to avoid the shared-key collision.
8253
+ * await window._smartico.api.clearCaches();
8254
+ * const prev = await window._smartico.api.getLeaderBoard(LeaderBoardPeriodType.WEEKLY, true);
8255
+ * console.log('[smartico] previous-week standings — render with greyed-out "ended" treatment:', prev?.users.length, 'finalists');
8256
+ *
8257
+ * // Visitor-mode equivalent — me is always undefined.
8258
+ * // const visitorBoard = await window._smartico.vapi('EN').getLeaderBoard(LeaderBoardPeriodType.DAILY);
5961
8259
  * ```
5962
8260
  */
5963
8261
  async getLeaderBoard(periodType, getPreviousPeriod) {
@@ -5968,23 +8266,49 @@ class WSAPILeaderBoard extends WSAPIGamePick {
5968
8266
  /** @group Inbox */
5969
8267
  class WSAPIInbox extends WSAPILeaderBoard {
5970
8268
  /**
5971
- * Reports an impression event for an engagement (when engagement content is displayed to the user).
5972
- * Use this method to track when users view engagement content such as inbox messages, popups.
5973
- * When using for Inbox cases, you need to use message guid as engagement_uid, and pass 31 as activityType.
8269
+ * Records that an engagement (inbox message, popup) was displayed to
8270
+ * the user. Use this to drive engagement analytics — every visible
8271
+ * inbox message should fire one impression event per render
8272
+ * occurrence, and every popup display should fire one.
5974
8273
  *
5975
- * **Example**:
5976
- * ```
5977
- * _smartico.api.reportImpressionEvent({
5978
- * engagement_uid: 'abc123-def456',
5979
- * activityType: 31 // Inbox
5980
- * });
5981
- * ```
8274
+ * **Fire-and-forget**: the call is asynchronous and one-way — there
8275
+ * is no response promise to await and no error reporting. The SDK
8276
+ * swallows transport failures silently. Returns `void`.
5982
8277
  *
5983
- * **Visitor mode: not supported**
8278
+ * @remarks
8279
+ * **When to call**
8280
+ * - Inbox: when a message becomes visible (the user opens / scrolls
8281
+ * it into view). The default Smartico UI fires this when the
8282
+ * message detail expands, alongside
8283
+ * {@link markInboxMessageAsRead}.
8284
+ * - Popup: when the popup content is shown to the user.
8285
+ *
8286
+ * **Activity types**
8287
+ * Pass {@link ActivityTypeLimited.Inbox} (`31`) for inbox messages,
8288
+ * {@link ActivityTypeLimited.Popup} (`30`) for popups. Other values
8289
+ * are not used by the engagement-impression channel.
8290
+ *
8291
+ * **Idempotency**: the server records every impression event —
8292
+ * repeated calls add to the impression count. Don't fire it on
8293
+ * every re-render; fire it once per logical "display" event
8294
+ * (modal open, list-row scroll-into-view, popup show).
8295
+ *
8296
+ * **Side effects**: server-side increments the engagement's
8297
+ * `impression_count` and updates `last_impression_date`. The
8298
+ * counter is observable via operator analytics; the SDK consumer
8299
+ * receives no signal back.
8300
+ *
8301
+ * **Visitor mode**: not supported.
5984
8302
  *
5985
- * @param params - Event parameters
5986
- * @param params.engagement_uid - Unique identifier for the engagement
5987
- * @param params.activityType - Type of engagement activity (Popup=30, Inbox=31)
8303
+ * @example
8304
+ * ```ts
8305
+ * // When the user opens an inbox message in the detail view.
8306
+ * window._smartico.api.reportImpressionEvent({
8307
+ * engagement_uid: message.message_guid,
8308
+ * activityType: 31, // ActivityTypeLimited.Inbox
8309
+ * });
8310
+ * console.log('[smartico] inbox impression reported for', message.message_guid, '— no response to await, fire-and-forget');
8311
+ * ```
5988
8312
  */
5989
8313
  reportImpressionEvent({
5990
8314
  engagement_uid,
@@ -5993,25 +8317,54 @@ class WSAPIInbox extends WSAPILeaderBoard {
5993
8317
  this.api.reportEngagementImpression(this.userExtId, engagement_uid, activityType);
5994
8318
  }
5995
8319
  /**
5996
- * Reports a click/action event for an engagement (when user interacts with engagement content).
5997
- * Use this method to track when users click on or interact with engagement content such as inbox messages, popups.
5998
- * When using for Inbox cases, you need to use message guid as engagement_uid, and pass 31 as activityType, and pass the action/deeplink that was triggered by the user interaction as action.
8320
+ * Records that the user interacted with an engagement — typically
8321
+ * tapping a CTA button, following a deep-link, or clicking a popup
8322
+ * action. Use this to drive engagement analytics on what users
8323
+ * actually act on (vs what they merely see —
8324
+ * {@link reportImpressionEvent}).
5999
8325
  *
6000
- * **Example**:
6001
- * ```
6002
- * _smartico.api.reportClickEvent({
6003
- * engagement_uid: 'abc123-def456',
6004
- * activityType: 31 // Inbox
6005
- * action: 'dp:gf_missions'
6006
- * });
6007
- * ```
8326
+ * **Fire-and-forget**: the call is asynchronous and one-way — there
8327
+ * is no response promise to await and no error reporting. The SDK
8328
+ * swallows transport failures silently. Returns `void`.
6008
8329
  *
6009
- * **Visitor mode: not supported**
8330
+ * @remarks
8331
+ * **When to call**
8332
+ * - Inbox: when the user taps a CTA button on a message or follows
8333
+ * a deep-link inside the message body. Pass the deep-link or URL
8334
+ * as `action`.
8335
+ * - Popup: when the user clicks a popup action button.
8336
+ *
8337
+ * **Activity types**
8338
+ * Pass {@link ActivityTypeLimited.Inbox} (`31`) for inbox,
8339
+ * {@link ActivityTypeLimited.Popup} (`30`) for popups.
8340
+ *
8341
+ * **`action` payload**
8342
+ * Pass the deep-link or URL that the user triggered (e.g.
8343
+ * `'dp:gf_missions'`, `'https://example.com/promo'`). The SDK
8344
+ * forwards the string to the server for analytics — it does not
8345
+ * execute the deep-link. To execute deep-links safely, call
8346
+ * `_smartico.dp(action)` separately.
8347
+ *
8348
+ * **Idempotency**: the server records every click event —
8349
+ * repeated calls add to the count. Fire once per logical user
8350
+ * action.
8351
+ *
8352
+ * **Side effects**: server-side updates the engagement's
8353
+ * `last_action` field; observable via operator analytics. The SDK
8354
+ * consumer receives no signal back.
6010
8355
  *
6011
- * @param params - Event parameters
6012
- * @param params.engagement_uid - Unique identifier for the engagement
6013
- * @param params.activityType - Type of engagement activity (Popup=30, Inbox=31)
6014
- * @param params.action - Optional action/deeplink that was triggered by the user interaction
8356
+ * **Visitor mode**: not supported.
8357
+ *
8358
+ * @example
8359
+ * ```ts
8360
+ * // When the user taps a CTA button in an inbox message.
8361
+ * window._smartico.api.reportClickEvent({
8362
+ * engagement_uid: message.message_guid,
8363
+ * activityType: 31, // ActivityTypeLimited.Inbox
8364
+ * action: 'dp:gf_missions',
8365
+ * });
8366
+ * window._smartico.dp('dp:gf_missions'); // separately execute the deep-link
8367
+ * ```
6015
8368
  */
6016
8369
  reportClickEvent({
6017
8370
  engagement_uid,
@@ -6020,19 +8373,118 @@ class WSAPIInbox extends WSAPILeaderBoard {
6020
8373
  }) {
6021
8374
  this.api.reportEngagementAction(this.userExtId, engagement_uid, activityType, action);
6022
8375
  }
6023
- /** Returns inbox messages based on the provided parameters. "From" and "to" indicate the range of messages to be fetched.
6024
- * The maximum number of messages per request is limited to 20.
6025
- * An indicator "onlyFavorite" can be passed to get only messages marked as favorites.
6026
- * An indicator "read_status" can be passed to get only messages marked as read or unread.
6027
- * You can leave this params empty and by default it will return list of messages ranging from 0 to 20.
6028
- * This function returns a list of messages without the body of each message.
6029
- * To get the body of the message you need to call getInboxMessageBody function and pass the message guid contained in each message of this request.
6030
- * All other action like mark as read, favorite, delete, etc. can be done using this message GUID.
6031
- * The "onUpdate" callback will be triggered when the user receives a new message. It will provide an updated list of messages, ranging from 0 to 20, to the onUpdate callback function.
8376
+ /**
8377
+ * Returns the user's inbox messages — newest first — with optional
8378
+ * filtering by category, favorite status, and read state. Each
8379
+ * `TInboxMessage` is a lightweight envelope (`message_guid`,
8380
+ * `sent_date`, `read`, `favorite`, `category_id`, `expire_on_dt`);
8381
+ * fetch the rich body (title, preview, icon, html_body, buttons)
8382
+ * separately via {@link getInboxMessageBody}.
6032
8383
  *
6033
- * **Visitor mode: not supported**
8384
+ * Use this to power an inbox list screen. Subscribe via `onUpdate`
8385
+ * to react to newly-arrived messages pushed by the server (from
8386
+ * campaigns, automation rules, manual operator sends).
8387
+ *
8388
+ * @remarks
8389
+ * **Subscription model (`onUpdate`)**
8390
+ * The callback receives the FULL refreshed message list (never a
8391
+ * diff/patch). Each subsequent call to `getInboxMessages({ onUpdate })`
8392
+ * REPLACES the prior callback. Pass `onUpdate: undefined` (or omit
8393
+ * it) to keep the prior callback in place; the callback is never
8394
+ * auto-cleared.
8395
+ *
8396
+ * **Update triggers** — the callback fires ONLY when:
8397
+ *
8398
+ * 1. A new message is pushed by the server (campaign / automation /
8399
+ * operator send). The refreshed list includes the new message
8400
+ * at the top.
8401
+ *
8402
+ * Does NOT fire for: `markInboxMessageAsRead`,
8403
+ * `markAllInboxMessagesAsRead`,
8404
+ * `markUnmarkInboxMessageAsFavorite`, `deleteInboxMessage`,
8405
+ * `deleteAllInboxMessages`. After a mutation, re-call
8406
+ * `getInboxMessages` manually if your UI needs the refreshed list
8407
+ * — or maintain optimistic state locally.
8408
+ *
8409
+ * **Pagination**
8410
+ * `from` (defaults to `0`) and `to` (defaults to `20`) define a
8411
+ * half-open range of message indices. The server caps the page at
8412
+ * **20 messages per request** — passing `to - from > 20` silently
8413
+ * truncates to 20. For "load more" pagination, advance `from` by
8414
+ * the prior page size on each subsequent call.
8415
+ *
8416
+ * **Filters**
8417
+ * All filters are ANDed server-side. Omitting a filter means "no
8418
+ * constraint" on that dimension:
8419
+ * - `categoryId` ({@link InboxCategories}): `General` (0),
8420
+ * `Platform` (1), `Personal` (2). Omit to get all categories.
8421
+ * - `onlyFavorite: true` returns only starred messages.
8422
+ * Omit / `false` returns all (starred and not).
8423
+ * - `read_status` ({@link InboxReadStatus}): `UnreadOnly` (1) or
8424
+ * `ReadOnly` (2). Omit to get both.
8425
+ *
8426
+ * **Server-side filtering** (always applied)
8427
+ * Expired messages (`expire_on_dt` in the past) and
8428
+ * soft-deleted messages are excluded server-side. Consumers do not
8429
+ * need to filter for these.
8430
+ *
8431
+ * **No client cache**: every call sends a fresh server round-trip.
8432
+ * To avoid redundant fetches in your UI, hold the result in
8433
+ * application state and re-fetch on `onUpdate` fires or explicit
8434
+ * user-driven refresh.
8435
+ *
8436
+ * **Idempotency / Side effects**: safe. Read-only.
8437
+ *
8438
+ * **UI guidance**: see [UI Guide — `getInboxMessages`](../../docs/ui/inbox/UIGuide_getInboxMessages.md).
8439
+ *
8440
+ * **Visitor mode**: not supported.
8441
+ *
8442
+ * @param params Optional filters + subscription.
8443
+ * @param params.from First message index (0-based). Defaults
8444
+ * to `0`.
8445
+ * @param params.to Last message index (exclusive).
8446
+ * Defaults to `20`. Server caps the page
8447
+ * at 20.
8448
+ * @param params.onlyFavorite When `true`, returns only favorite
8449
+ * (starred) messages.
8450
+ * @param params.categoryId When set, returns only messages in this
8451
+ * category ({@link InboxCategories}).
8452
+ * @param params.read_status When set, scopes to read or unread
8453
+ * only ({@link InboxReadStatus}).
8454
+ * @param params.onUpdate Callback invoked with the full
8455
+ * refreshed list when a new message
8456
+ * arrives.
8457
+ *
8458
+ * @returns Promise resolving to up to 20 `TInboxMessage` envelopes.
8459
+ * Empty array when the user has no messages matching the
8460
+ * filter.
8461
+ *
8462
+ * @example
8463
+ * ```ts
8464
+ * const messages = await window._smartico.api.getInboxMessages({
8465
+ * onUpdate: (refreshed) => {
8466
+ * console.log('[smartico] new inbox message arrived — re-render the list from this array, new message is at index 0:', refreshed);
8467
+ * },
8468
+ * });
6034
8469
  *
6035
- * @param params
8470
+ * // For each message, fetch its body for the list view (title, preview, icon).
8471
+ * for (const msg of messages) {
8472
+ * const body = await window._smartico.api.getInboxMessageBody(msg.message_guid);
8473
+ * console.log('[smartico] render message', msg.message_guid,
8474
+ * '— title:', body.title,
8475
+ * '— preview:', body.preview_body,
8476
+ * '— read:', msg.read,
8477
+ * '— favorite:', msg.favorite);
8478
+ * }
8479
+ *
8480
+ * // "Load more" pagination — advance from by the prior page size.
8481
+ * const page2 = await window._smartico.api.getInboxMessages({ from: 20, to: 40 });
8482
+ * console.log('[smartico] page 2:', page2.length, 'messages');
8483
+ *
8484
+ * // Filter examples.
8485
+ * const onlyStarred = await window._smartico.api.getInboxMessages({ onlyFavorite: true });
8486
+ * const onlyUnread = await window._smartico.api.getInboxMessages({ read_status: 1 }); // InboxReadStatus.UnreadOnly
8487
+ * ```
6036
8488
  */
6037
8489
  async getInboxMessages({
6038
8490
  from,
@@ -6048,10 +8500,53 @@ class WSAPIInbox extends WSAPILeaderBoard {
6048
8500
  return await this.api.getInboxMessagesT(this.userExtId, from, to, onlyFavorite, categoryId, read_status);
6049
8501
  }
6050
8502
  /**
6051
- * Returns inbox unread count.
8503
+ * Returns the user's current unread inbox message count — a single
8504
+ * integer suitable for driving a header badge. Optionally subscribe
8505
+ * to live updates via `onUpdate`.
6052
8506
  *
6053
- * **Visitor mode: not supported**
6054
- * @param params
8507
+ * For most UIs, prefer `core_inbox_unread_count` from
8508
+ * {@link getUserProfile} instead: it carries the same value, is
8509
+ * push-updated in real time via the user-properties channel, and
8510
+ * doesn't require a separate cache. This method is useful when you
8511
+ * want a dedicated subscription tied to the inbox surface rather
8512
+ * than the broader user-properties channel.
8513
+ *
8514
+ * @remarks
8515
+ * **Subscription model**
8516
+ * `onUpdate` fires whenever the cached value changes — either on a
8517
+ * push from the user-properties channel (new message arrival,
8518
+ * mark-as-read, delete) or after an inbox-list fetch that returns
8519
+ * a fresh count.
8520
+ *
8521
+ * **Cache TTL**: 30 seconds. The cached value is patched in place
8522
+ * by push events (no full re-fetch on every push), so the
8523
+ * `onUpdate` callback reflects server state with sub-second latency
8524
+ * regardless of the TTL.
8525
+ *
8526
+ * **Idempotency / Side effects**: safe. Read-only.
8527
+ *
8528
+ * **Visitor mode**: not supported.
8529
+ *
8530
+ * @param params Optional subscription bag.
8531
+ * @param params.onUpdate Callback invoked with the new unread
8532
+ * count whenever it changes (push-driven).
8533
+ * @returns Promise resolving to the current unread
8534
+ * count (integer, includes `0`).
8535
+ *
8536
+ * @example
8537
+ * ```ts
8538
+ * // Static read for an initial badge render.
8539
+ * const count = await window._smartico.api.getInboxUnreadCount();
8540
+ * console.log('[smartico] inbox badge initial count:', count);
8541
+ *
8542
+ * // Live subscription for badge updates.
8543
+ * await window._smartico.api.getInboxUnreadCount({
8544
+ * onUpdate: (newCount) => {
8545
+ * console.log('[smartico] inbox count changed — update the badge to:', newCount,
8546
+ * '— hide badge entirely when 0, show raw integer otherwise (no "99+" cap in the default UI)');
8547
+ * },
8548
+ * });
8549
+ * ```
6055
8550
  */
6056
8551
  async getInboxUnreadCount({
6057
8552
  onUpdate
@@ -6062,17 +8557,113 @@ class WSAPIInbox extends WSAPILeaderBoard {
6062
8557
  return OCache.use(onUpdateContextKey.InboxUnreadCount, ECacheContext.WSAPI, () => this.api.getInboxUnreadCountT(this.userExtId), CACHE_DATA_SEC);
6063
8558
  }
6064
8559
  /**
6065
- * Returns the message body of the specified message guid.
8560
+ * Returns the rich body of a single inbox message — title, preview
8561
+ * text, icon, click action, optional rich HTML body, and optional
8562
+ * buttons. Call this after fetching the envelope list via
8563
+ * {@link getInboxMessages} to render either a list-item preview or
8564
+ * a full detail view.
8565
+ *
8566
+ * The message body is served from a CDN — not over the WebSocket —
8567
+ * so latency depends on CDN proximity rather than WS round-trip
8568
+ * time. There is NO client-side cache; every call is a fresh
8569
+ * HTTP fetch. Browser HTTP caching applies (Cache-Control headers
8570
+ * from the CDN), but the SDK does not memoize the result.
6066
8571
  *
6067
- * **Visitor mode: not supported**
8572
+ * @remarks
8573
+ * **Rich vs simple messages**
8574
+ * The shape of the returned body branches on the `action` field:
8575
+ * - When `action === 'dp:inbox'`, the message has a rich HTML body
8576
+ * (`html_body`) and may have up to 2 action `buttons`. Use these
8577
+ * for a full-detail rendering.
8578
+ * - For any other `action` (deep-link like `'dp:deposit'` or a URL),
8579
+ * the message is a simple notification — `html_body` and
8580
+ * `buttons` will be `undefined` regardless of what's in the
8581
+ * underlying CDN payload. Render `title` + `preview_body` + a
8582
+ * single CTA driving the `action`.
8583
+ *
8584
+ * **The `action` field**
8585
+ * For both shapes, `action` carries either a deep-link
8586
+ * (`'dp:gf_missions'`) or a plain URL. Pass it to `_smartico.dp()`
8587
+ * for safe execution — that helper handles URL vs deep-link
8588
+ * routing.
8589
+ *
8590
+ * **Idempotency / Side effects**: safe. Read-only HTTP fetch.
8591
+ *
8592
+ * **UI guidance**: see [UI Guide — `getInboxMessageBody`](../../docs/ui/inbox/UIGuide_getInboxMessageBody.md).
8593
+ *
8594
+ * **Visitor mode**: not supported.
8595
+ *
8596
+ * @param messageGuid The `message_guid` from a `TInboxMessage`
8597
+ * returned by {@link getInboxMessages}.
8598
+ * @returns Promise resolving to `TInboxMessageBody`.
8599
+ *
8600
+ * @example
8601
+ * ```ts
8602
+ * const body = await window._smartico.api.getInboxMessageBody(messageGuid);
8603
+ *
8604
+ * console.log('[smartico] render message — title:', body.title,
8605
+ * '— preview:', body.preview_body,
8606
+ * '— icon:', body.icon);
8607
+ *
8608
+ * if (body.action === 'dp:inbox' && body.html_body) {
8609
+ * console.log('[smartico] rich message — render the html_body in a sandboxed iframe; render up to 2 action buttons:',
8610
+ * body.buttons?.length ?? 0);
8611
+ * } else {
8612
+ * console.log('[smartico] simple message — show preview_body + single CTA wired to:', body.action,
8613
+ * '— execute via _smartico.dp(body.action)');
8614
+ * }
8615
+ * ```
6068
8616
  */
6069
8617
  async getInboxMessageBody(messageGuid) {
6070
8618
  return await this.api.getInboxMessageBodyT(messageGuid);
6071
8619
  }
6072
8620
  /**
6073
- * Requests to mark inbox message with specified guid as read
8621
+ * Marks a single inbox message as read. Server-side this flips the
8622
+ * message's `is_read` flag and decrements the user's unread count
8623
+ * (the change propagates back via the user-properties channel —
8624
+ * subscribers to {@link getInboxUnreadCount}'s `onUpdate` and
8625
+ * {@link getUserProfile}'s `core_inbox_unread_count` see it within
8626
+ * ~1 second).
6074
8627
  *
6075
- * **Visitor mode: not supported**
8628
+ * @remarks
8629
+ * **Error codes** (in `err_code`)
8630
+ * - `0` — success; the message is now marked read. Idempotent — a
8631
+ * second call on the same `messageGuid` also returns `0`.
8632
+ * - other non-zero — server error. Surface `err_message` if any.
8633
+ *
8634
+ * **Refresh after success**
8635
+ * The SDK does NOT auto-refresh {@link getInboxMessages} (no
8636
+ * `onUpdate` callback fires after this mutation). The unread-count
8637
+ * channel ({@link getInboxUnreadCount}, `core_inbox_unread_count`)
8638
+ * DOES auto-update — subscribers to it see the new count
8639
+ * automatically. If your inbox list UI shows the `read` field, you
8640
+ * have two options: re-call `getInboxMessages` after the mutation,
8641
+ * or update the local copy optimistically.
8642
+ *
8643
+ * **Idempotency**: safe. Repeated calls return `err_code === 0`.
8644
+ *
8645
+ * **Side effects** (on success)
8646
+ * - Server-side `is_read` flag set to true.
8647
+ * - Server-side `core_inbox_unread_count` decremented (if the
8648
+ * message was previously unread).
8649
+ *
8650
+ * **UI guidance**: see [UI Guide — `markInboxMessageAsRead`](../../docs/ui/inbox/UIGuide_markInboxMessageAsRead.md).
8651
+ *
8652
+ * **Visitor mode**: not supported.
8653
+ *
8654
+ * @param messageGuid The `message_guid` from a `TInboxMessage`.
8655
+ * @returns `{ err_code, err_message }`; success when
8656
+ * `err_code === 0`.
8657
+ *
8658
+ * @example
8659
+ * ```ts
8660
+ * const r = await window._smartico.api.markInboxMessageAsRead(message.message_guid);
8661
+ * if (r.err_code === 0) {
8662
+ * console.log('[smartico] marked as read — optimistically flip the local read indicator; the inbox badge auto-decrements via the user-properties channel');
8663
+ * } else {
8664
+ * console.error('[smartico] mark-as-read failed — keep the local state as-is and show a non-blocking error if appropriate:', r.err_message);
8665
+ * }
8666
+ * ```
6076
8667
  */
6077
8668
  async markInboxMessageAsRead(messageGuid) {
6078
8669
  const r = await this.api.markInboxMessageRead(this.userExtId, messageGuid);
@@ -6082,9 +8673,57 @@ class WSAPIInbox extends WSAPILeaderBoard {
6082
8673
  };
6083
8674
  }
6084
8675
  /**
6085
- * Requests to mark all inbox messages as read
8676
+ * Marks ALL of the user's inbox messages as read in one server
8677
+ * round-trip — typically wired to a "Mark all as read" CTA in the
8678
+ * inbox header.
6086
8679
  *
6087
- * **Visitor mode: not supported**
8680
+ * The operation is global to the user — NOT filtered by the
8681
+ * current view (active category tab, favorite filter, etc.). Every
8682
+ * unread message in the user's inbox flips to read.
8683
+ *
8684
+ * @remarks
8685
+ * **Error codes** (in `err_code`)
8686
+ * - `0` — success; all currently-unread messages flipped to read.
8687
+ * - other non-zero — server error. Surface `err_message` if any.
8688
+ *
8689
+ * **Race condition note**: a new message arriving server-side
8690
+ * between the call and its processing remains unread (the
8691
+ * server's mark-all operation is a snapshot in time). Subsequent
8692
+ * `getInboxMessages` calls show the new message as unread.
8693
+ *
8694
+ * **Refresh after success**
8695
+ * Same as {@link markInboxMessageAsRead} — `getInboxMessages`
8696
+ * `onUpdate` does NOT fire automatically, but the unread-count
8697
+ * channel does (the badge will drop to 0, or to the count of any
8698
+ * new messages that arrived during processing).
8699
+ *
8700
+ * **Idempotency**: safe. A second call on an already-all-read
8701
+ * inbox returns `err_code === 0` with no-op effect.
8702
+ *
8703
+ * **Side effects** (on success)
8704
+ * - Every unread message flipped to read.
8705
+ * - `core_inbox_unread_count` drops to 0 (or near 0 if races).
8706
+ *
8707
+ * **UI guidance**: included in the
8708
+ * [UI Guide — `markInboxMessageAsRead`](../../docs/ui/inbox/UIGuide_markInboxMessageAsRead.md)
8709
+ * (Bulk variant section).
8710
+ *
8711
+ * **Visitor mode**: not supported.
8712
+ *
8713
+ * @returns `{ err_code, err_message }`; success when
8714
+ * `err_code === 0`.
8715
+ *
8716
+ * @example
8717
+ * ```ts
8718
+ * console.log('[smartico] mark-all-read starting — set in-flight flag on the "Mark all read" button, show loading dots');
8719
+ * const r = await window._smartico.api.markAllInboxMessagesAsRead();
8720
+ * console.log('[smartico] mark-all-read response — clear in-flight flag');
8721
+ * if (r.err_code === 0) {
8722
+ * console.log('[smartico] all messages marked read — optimistically flip every local read indicator; badge auto-drops via user-properties channel');
8723
+ * } else {
8724
+ * console.error('[smartico] mark-all failed — surface a non-blocking error:', r.err_message);
8725
+ * }
8726
+ * ```
6088
8727
  */
6089
8728
  async markAllInboxMessagesAsRead() {
6090
8729
  const r = await this.api.markAllInboxMessageRead(this.userExtId);
@@ -6094,9 +8733,57 @@ class WSAPIInbox extends WSAPILeaderBoard {
6094
8733
  };
6095
8734
  }
6096
8735
  /**
6097
- * Requests to mark inbox message with specified guid as favorite. Pass mark true to add message to favorite and false to remove.
8736
+ * Toggles a message's favorite (starred) state — pass `mark: true`
8737
+ * to favorite, `mark: false` to unfavorite. Use to power a star
8738
+ * icon click handler on the inbox list.
6098
8739
  *
6099
- * **Visitor mode: not supported**
8740
+ * @remarks
8741
+ * **Error codes** (in `err_code`)
8742
+ * - `0` — success; the message's favorite state flipped to the
8743
+ * requested value.
8744
+ * - other non-zero — server error. Surface `err_message` if any.
8745
+ *
8746
+ * **Refresh after success**
8747
+ * The SDK does NOT auto-refresh {@link getInboxMessages}. To
8748
+ * reflect the new state in the UI, update the local copy of the
8749
+ * affected `TInboxMessage` (set `favorite` to the new value) or
8750
+ * re-call `getInboxMessages`.
8751
+ *
8752
+ * **Idempotency**: safe. `mark: true` on an already-favorite
8753
+ * message returns `err_code === 0` (no-op). Same for `mark: false`
8754
+ * on a non-favorite.
8755
+ *
8756
+ * **Side effects** (on success)
8757
+ * - Server-side `is_starred` flag set to the requested value.
8758
+ * - No effect on read state, unread count, or other fields.
8759
+ *
8760
+ * **UI guidance**: see [UI Guide — `markUnmarkInboxMessageAsFavorite`](../../docs/ui/inbox/UIGuide_markUnmarkInboxMessageAsFavorite.md).
8761
+ *
8762
+ * **Visitor mode**: not supported.
8763
+ *
8764
+ * @param messageGuid The `message_guid` from a `TInboxMessage`.
8765
+ * @param mark `true` to favorite, `false` to unfavorite.
8766
+ * @returns `{ err_code, err_message }`; success when
8767
+ * `err_code === 0`.
8768
+ *
8769
+ * @example
8770
+ * ```ts
8771
+ * // Toggle handler — flip the local state optimistically, then
8772
+ * // call the server, and revert if the call fails.
8773
+ * const target = !message.favorite;
8774
+ * console.log('[smartico] optimistically flip the star to', target, '— it will revert if the server rejects');
8775
+ *
8776
+ * const r = await window._smartico.api.markUnmarkInboxMessageAsFavorite(
8777
+ * message.message_guid,
8778
+ * target,
8779
+ * );
8780
+ *
8781
+ * if (r.err_code === 0) {
8782
+ * console.log('[smartico] favorite toggled — keep the local state and show a brief "Added to favorites" / "Removed from favorites" toast');
8783
+ * } else {
8784
+ * console.error('[smartico] favorite toggle failed — revert the local state and show an error toast:', r.err_message);
8785
+ * }
8786
+ * ```
6100
8787
  */
6101
8788
  async markUnmarkInboxMessageAsFavorite(messageGuid, mark) {
6102
8789
  const r = await this.api.markUnmarkInboxMessageAsFavorite(this.userExtId, messageGuid, mark);
@@ -6106,9 +8793,52 @@ class WSAPIInbox extends WSAPILeaderBoard {
6106
8793
  };
6107
8794
  }
6108
8795
  /**
6109
- * Requests to delete inbox message
8796
+ * Soft-deletes a single inbox message. After deletion, the message
8797
+ * is excluded from future {@link getInboxMessages} responses
8798
+ * server-side, but its row is retained for analytics — there is no
8799
+ * "undelete" path via the SDK.
6110
8800
  *
6111
- * **Visitor mode: not supported**
8801
+ * @remarks
8802
+ * **Error codes** (in `err_code`)
8803
+ * - `0` — success; the message is now deleted from the user's
8804
+ * view.
8805
+ * - other non-zero — server error. Surface `err_message` if any.
8806
+ *
8807
+ * **Refresh after success**
8808
+ * The SDK does NOT auto-refresh {@link getInboxMessages}. Drop the
8809
+ * deleted message from your local list, or re-call
8810
+ * `getInboxMessages` to reload. If the deleted message was unread,
8811
+ * the unread count drops automatically via the user-properties
8812
+ * channel.
8813
+ *
8814
+ * **Idempotency**: safe. A second call on an already-deleted
8815
+ * `messageGuid` returns `err_code === 0` (server treats it as a
8816
+ * no-op — already-deleted messages are filtered out).
8817
+ *
8818
+ * **Side effects** (on success)
8819
+ * - Server-side `is_deleted` flag set to true.
8820
+ * - If the message was unread, `core_inbox_unread_count`
8821
+ * decrements via the user-properties channel.
8822
+ *
8823
+ * **UI guidance**: see [UI Guide — `deleteInboxMessage`](../../docs/ui/inbox/UIGuide_deleteInboxMessage.md).
8824
+ *
8825
+ * **Visitor mode**: not supported.
8826
+ *
8827
+ * @param messageGuid The `message_guid` from a `TInboxMessage`.
8828
+ * @returns `{ err_code, err_message }`; success when
8829
+ * `err_code === 0`.
8830
+ *
8831
+ * @example
8832
+ * ```ts
8833
+ * // Swipe-to-delete on mobile; trash-icon click on desktop.
8834
+ * console.log('[smartico] delete starting — animate the row out of the list (slide left/right by 100%)');
8835
+ * const r = await window._smartico.api.deleteInboxMessage(message.message_guid);
8836
+ * if (r.err_code === 0) {
8837
+ * console.log('[smartico] deleted — drop the message from the local list, fire a brief "Message deleted" toast (no undo affordance — there is no undelete via the SDK)');
8838
+ * } else {
8839
+ * console.error('[smartico] delete failed — animate the row back into place and show a non-blocking error:', r.err_message);
8840
+ * }
8841
+ * ```
6112
8842
  */
6113
8843
  async deleteInboxMessage(messageGuid) {
6114
8844
  const r = await this.api.deleteInboxMessage(this.userExtId, messageGuid);
@@ -6118,9 +8848,61 @@ class WSAPIInbox extends WSAPILeaderBoard {
6118
8848
  };
6119
8849
  }
6120
8850
  /**
6121
- * Requests to delete all inbox messages
8851
+ * Soft-deletes ALL of the user's inbox messages in one server
8852
+ * round-trip — typically wired to a "Delete all" CTA in the inbox
8853
+ * header (usually behind a confirm dialog given the destructive
8854
+ * nature).
6122
8855
  *
6123
- * **Visitor mode: not supported**
8856
+ * The operation is global to the user — NOT filtered by the
8857
+ * current view (active category tab, favorite filter, etc.). Every
8858
+ * non-deleted message in the user's inbox flips to deleted.
8859
+ *
8860
+ * @remarks
8861
+ * **Error codes** (in `err_code`)
8862
+ * - `0` — success; the entire inbox is now empty from the user's
8863
+ * view.
8864
+ * - other non-zero — server error. Surface `err_message` if any.
8865
+ *
8866
+ * **Race condition note**: a new message arriving server-side
8867
+ * between the call and its processing is NOT deleted (the
8868
+ * server's delete-all operation is a snapshot in time). Subsequent
8869
+ * `getInboxMessages` calls show the new message.
8870
+ *
8871
+ * **Refresh after success**
8872
+ * Same as {@link deleteInboxMessage} — `getInboxMessages` `onUpdate`
8873
+ * does not fire automatically. Replace the local list with `[]`
8874
+ * (or re-call `getInboxMessages`). The unread count drops to 0
8875
+ * (or near-0 if races) via the user-properties channel.
8876
+ *
8877
+ * **Idempotency**: safe. A second call on an already-empty inbox
8878
+ * returns `err_code === 0` with no-op effect.
8879
+ *
8880
+ * **Side effects** (on success)
8881
+ * - Every message flipped to deleted server-side.
8882
+ * - `core_inbox_unread_count` drops to 0.
8883
+ * - There is no SDK undelete path — recovery requires operator
8884
+ * intervention.
8885
+ *
8886
+ * **UI guidance**: included in the
8887
+ * [UI Guide — `deleteInboxMessage`](../../docs/ui/inbox/UIGuide_deleteInboxMessage.md)
8888
+ * (Bulk variant section).
8889
+ *
8890
+ * **Visitor mode**: not supported.
8891
+ *
8892
+ * @returns `{ err_code, err_message }`; success when
8893
+ * `err_code === 0`.
8894
+ *
8895
+ * @example
8896
+ * ```ts
8897
+ * // After a confirm dialog "Are you sure you want to delete all messages?"
8898
+ * console.log('[smartico] delete-all confirmed — show loading state on the "Delete all" button');
8899
+ * const r = await window._smartico.api.deleteAllInboxMessages();
8900
+ * if (r.err_code === 0) {
8901
+ * console.log('[smartico] inbox cleared — replace the local list with [], fire a "All messages deleted" toast, badge auto-drops to 0');
8902
+ * } else {
8903
+ * console.error('[smartico] delete-all failed — show a non-blocking error toast:', r.err_message);
8904
+ * }
8905
+ * ```
6124
8906
  */
6125
8907
  async deleteAllInboxMessages() {
6126
8908
  const r = await this.api.deleteAllInboxMessages(this.userExtId);
@@ -7468,6 +10250,10 @@ class SmarticoAPI {
7468
10250
  const message = this.buildMessage(user_ext_id, ClassId.RAF_OPTIN_REQUEST, props);
7469
10251
  return await this.send(message, ClassId.RAF_OPTIN_RESPONSE);
7470
10252
  }
10253
+ async getRaffleWonPrizes(user_ext_id, props) {
10254
+ const message = this.buildMessage(user_ext_id, ClassId.RAF_GET_WON_PRIZES_REQUEST, props);
10255
+ return await this.send(message, ClassId.RAF_GET_WON_PRIZES_RESPONSE);
10256
+ }
7471
10257
  async getActivityLog(user_ext_id, startTimeSeconds, endTimeSeconds, limit, offset) {
7472
10258
  const message = this.buildMessage(user_ext_id, ClassId.GET_POINT_HISTORY_REQUEST, {
7473
10259
  startTimeSeconds: Math.floor(startTimeSeconds),