@smartico/public-api 0.0.380 → 0.0.381

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 (174) 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/Raffle/GetRaffleWonPrizesRequest.d.ts +9 -0
  7. package/dist/Raffle/GetRaffleWonPrizesResponse.d.ts +52 -0
  8. package/dist/Raffle/index.d.ts +2 -0
  9. package/dist/SmarticoAPI.d.ts +6 -1
  10. package/dist/SmarticoLib/index.d.ts +5 -1
  11. package/dist/WSAPI/WSAPIAvatars.d.ts +267 -49
  12. package/dist/WSAPI/WSAPIBonuses.d.ts +224 -10
  13. package/dist/WSAPI/WSAPIGamePick.d.ts +538 -253
  14. package/dist/WSAPI/WSAPIGeneral.d.ts +156 -14
  15. package/dist/WSAPI/WSAPIInbox.d.ts +547 -58
  16. package/dist/WSAPI/WSAPIJackpots.d.ts +292 -57
  17. package/dist/WSAPI/WSAPILeaderBoard.d.ts +127 -13
  18. package/dist/WSAPI/WSAPIMiniGames.d.ts +465 -49
  19. package/dist/WSAPI/WSAPIRaffles.d.ts +576 -52
  20. package/dist/WSAPI/WSAPITournaments.d.ts +3 -3
  21. package/dist/WSAPI/WSAPITypes.d.ts +243 -132
  22. package/dist/WSAPI/WSAPIUser.d.ts +193 -43
  23. package/dist/index.js +3409 -615
  24. package/dist/index.js.map +1 -1
  25. package/dist/index.modern.mjs +3391 -608
  26. package/dist/index.modern.mjs.map +1 -1
  27. package/docs/Callbacks.md +162 -0
  28. package/docs/SmarticoObject.md +445 -0
  29. package/docs/Transforms.md +99 -0
  30. package/docs/api/README.md +26 -0
  31. package/docs/api/classes/WSAPIAvatars.md +282 -54
  32. package/docs/api/classes/WSAPIBonuses.md +233 -12
  33. package/docs/api/classes/WSAPIClans.md +380 -10
  34. package/docs/api/classes/WSAPIGamePick.md +555 -26
  35. package/docs/api/classes/WSAPIGeneral.md +165 -16
  36. package/docs/api/classes/WSAPIInbox.md +588 -64
  37. package/docs/api/classes/WSAPIJackpots.md +320 -66
  38. package/docs/api/classes/WSAPILeaderBoard.md +133 -15
  39. package/docs/api/classes/WSAPIMiniGames.md +501 -64
  40. package/docs/api/classes/WSAPIMissions.md +58 -79
  41. package/docs/api/classes/WSAPIRaffles.md +647 -69
  42. package/docs/api/classes/WSAPIStore.md +83 -108
  43. package/docs/api/classes/WSAPITournaments.md +495 -28
  44. package/docs/api/classes/WSAPIUser.md +520 -83
  45. package/docs/api/documents/Callbacks.md +162 -0
  46. package/docs/api/documents/SmarticoObject.md +445 -0
  47. package/docs/api/documents/Transforms.md +99 -0
  48. package/docs/api/enumerations/BonusStatus-1.md +11 -6
  49. package/docs/api/enumerations/JoinClanErrorCode.md +81 -0
  50. package/docs/api/enumerations/TournamentRegistrationError.md +12 -0
  51. package/docs/api/functions/clanTournamentPlayersTransform.md +13 -0
  52. package/docs/api/interfaces/AvatarCustomized.md +25 -0
  53. package/docs/api/interfaces/AvatarDefinition.md +94 -0
  54. package/docs/api/interfaces/AvatarPrompt.md +45 -0
  55. package/docs/api/interfaces/BonusMetaMap-1.md +4 -1
  56. package/docs/api/interfaces/BonusTemplateMetaMap-1.md +9 -4
  57. package/docs/api/interfaces/Clan.md +4 -0
  58. package/docs/api/interfaces/ClanInfo.md +109 -0
  59. package/docs/api/interfaces/ClanMember.md +53 -0
  60. package/docs/api/interfaces/ClanTournamentPlayerRaw.md +67 -0
  61. package/docs/api/interfaces/GemsDiamondsLog.md +43 -0
  62. package/docs/api/interfaces/GetClanInfoResponse.md +61 -0
  63. package/docs/api/interfaces/GetClanTournamentPlayersResponse.md +81 -0
  64. package/docs/api/interfaces/GetRaffleWonPrizesRequest.md +59 -0
  65. package/docs/api/interfaces/GetRaffleWonPrizesResponse.md +95 -0
  66. package/docs/api/interfaces/InboxMarkMessageAction.md +10 -2
  67. package/docs/api/interfaces/JackpotDetails.md +14 -15
  68. package/docs/api/interfaces/JackpotPot.md +9 -6
  69. package/docs/api/interfaces/LeaderBoardDetailsT.md +14 -8
  70. package/docs/api/interfaces/LeaderBoardUserT.md +8 -5
  71. package/docs/api/interfaces/LeaderBoardsRewardsT.md +7 -0
  72. package/docs/api/interfaces/PointsLog.md +43 -0
  73. package/docs/api/interfaces/RaffleWonPrize.md +59 -0
  74. package/docs/api/interfaces/RaffleWonPrizePublicMeta.md +27 -0
  75. package/docs/api/interfaces/RaffleWonPrizeUser.md +43 -0
  76. package/docs/api/interfaces/TAchCategory.md +2 -7
  77. package/docs/api/interfaces/TAvatarCustomized.md +4 -4
  78. package/docs/api/interfaces/TAvatarDefinition.md +13 -14
  79. package/docs/api/interfaces/TAvatarPrompt.md +6 -6
  80. package/docs/api/interfaces/TBonus.md +16 -8
  81. package/docs/api/interfaces/TBuyStoreItemResult.md +4 -29
  82. package/docs/api/interfaces/TClaimBonusResult.md +5 -5
  83. package/docs/api/interfaces/TClan.md +8 -4
  84. package/docs/api/interfaces/TClanInfo.md +47 -0
  85. package/docs/api/interfaces/TClanJoinResult.md +11 -1
  86. package/docs/api/interfaces/TClanTournamentPlayer.md +55 -0
  87. package/docs/api/interfaces/TClanTournamentPlayersResult.md +27 -0
  88. package/docs/api/interfaces/TClans.md +3 -1
  89. package/docs/api/interfaces/TGetTranslations.md +4 -0
  90. package/docs/api/interfaces/TInboxMessage.md +14 -6
  91. package/docs/api/interfaces/TInboxMessageBody.md +18 -12
  92. package/docs/api/interfaces/TLevel.md +17 -20
  93. package/docs/api/interfaces/TLevelCurrent.md +19 -18
  94. package/docs/api/interfaces/TMiniGamePlayBatchResult.md +14 -6
  95. package/docs/api/interfaces/TMiniGamePlayResult.md +9 -4
  96. package/docs/api/interfaces/TMissionClaimRewardResult.md +1 -9
  97. package/docs/api/interfaces/TMissionOptInResult.md +1 -5
  98. package/docs/api/interfaces/TMissionOrBadge.md +4 -7
  99. package/docs/api/interfaces/TRaffleOptinResponse.md +4 -2
  100. package/docs/api/interfaces/TSegmentCheckResult.md +10 -0
  101. package/docs/api/interfaces/TSetAvatarResult.md +3 -3
  102. package/docs/api/interfaces/TStoreItem.md +3 -13
  103. package/docs/api/interfaces/TTournament.md +1 -1
  104. package/docs/api/interfaces/TTournamentDetailed.md +1 -1
  105. package/docs/api/interfaces/TTournamentRegistrationResult.md +4 -2
  106. package/docs/api/interfaces/TUICustomSection.md +23 -19
  107. package/docs/api/interfaces/TUserProfile.md +18 -16
  108. package/docs/api/interfaces/TransformedRaffleClaimPrizeResponse.md +6 -2
  109. package/docs/api/interfaces/UserLevelExtraCountersT.md +9 -2
  110. package/docs/api/type-aliases/ActivityLogEntry.md +3 -0
  111. package/docs/native/ADDING_METHODS.md +7 -2
  112. package/docs/native/PROTOCOL.md +337 -0
  113. package/docs/ui/avatars/UIGuide_getAvatarPrompts.md +100 -0
  114. package/docs/ui/avatars/UIGuide_getAvatarsCustomized.md +90 -0
  115. package/docs/ui/avatars/UIGuide_getAvatarsList.md +105 -0
  116. package/docs/ui/avatars/UIGuide_setAvatar.md +118 -0
  117. package/docs/ui/bonuses/UIGuide_claimBonus.md +144 -0
  118. package/docs/ui/bonuses/UIGuide_getBonuses.md +176 -0
  119. package/docs/ui/gamepick/UIGuide_gamePickGetActiveRound.md +99 -0
  120. package/docs/ui/gamepick/UIGuide_gamePickGetActiveRounds.md +76 -0
  121. package/docs/ui/gamepick/UIGuide_gamePickGetBoard.md +81 -0
  122. package/docs/ui/gamepick/UIGuide_gamePickGetGameInfo.md +78 -0
  123. package/docs/ui/gamepick/UIGuide_gamePickGetHistory.md +55 -0
  124. package/docs/ui/gamepick/UIGuide_gamePickGetRoundInfoForUser.md +71 -0
  125. package/docs/ui/gamepick/UIGuide_gamePickGetUserInfo.md +64 -0
  126. package/docs/ui/gamepick/UIGuide_gamePickSubmitSelection.md +103 -0
  127. package/docs/ui/gamepick/UIGuide_gamePickSubmitSelectionQuiz.md +67 -0
  128. package/docs/ui/general/UIGuide_getCustomSections.md +137 -0
  129. package/docs/ui/general/UIGuide_getRelatedItemsForGame.md +140 -0
  130. package/docs/ui/inbox/UIGuide_deleteInboxMessage.md +135 -0
  131. package/docs/ui/inbox/UIGuide_getInboxMessageBody.md +133 -0
  132. package/docs/ui/inbox/UIGuide_getInboxMessages.md +186 -0
  133. package/docs/ui/inbox/UIGuide_markInboxMessageAsRead.md +122 -0
  134. package/docs/ui/inbox/UIGuide_markUnmarkInboxMessageAsFavorite.md +84 -0
  135. package/docs/ui/jackpots/UIGuide_getJackpotEligibleGames.md +73 -0
  136. package/docs/ui/jackpots/UIGuide_getJackpotWinners.md +83 -0
  137. package/docs/ui/jackpots/UIGuide_jackpotGet.md +138 -0
  138. package/docs/ui/jackpots/UIGuide_jackpotOptIn.md +85 -0
  139. package/docs/ui/jackpots/UIGuide_jackpotOptOut.md +65 -0
  140. package/docs/ui/leaderboard/UIGuide_getLeaderBoard.md +166 -0
  141. package/docs/ui/minigames/UIGuide_getMiniGames.md +153 -0
  142. package/docs/ui/minigames/UIGuide_getMiniGamesHistory.md +90 -0
  143. package/docs/ui/minigames/UIGuide_playMiniGame.md +150 -0
  144. package/docs/ui/minigames/UIGuide_playMiniGameBatch.md +128 -0
  145. package/docs/ui/raffles/UIGuide_claimRafflePrize.md +99 -0
  146. package/docs/ui/raffles/UIGuide_getRaffleDrawRun.md +117 -0
  147. package/docs/ui/raffles/UIGuide_getRaffleDrawRunsHistory.md +92 -0
  148. package/docs/ui/raffles/UIGuide_getRaffleWonPrizes.md +120 -0
  149. package/docs/ui/raffles/UIGuide_getRaffles.md +175 -0
  150. package/docs/ui/raffles/UIGuide_requestRaffleOptin.md +114 -0
  151. package/docs/ui/user/UIGuide_getActivityLog.md +130 -0
  152. package/docs/ui/user/UIGuide_getLevels.md +47 -42
  153. package/package.json +1 -1
  154. package/src/Base/ClassId.ts +2 -0
  155. package/src/Jackpots/JackpotDetails.ts +14 -15
  156. package/src/Jackpots/JackpotPot.ts +10 -6
  157. package/src/Raffle/GetRaffleWonPrizesRequest.ts +12 -0
  158. package/src/Raffle/GetRaffleWonPrizesResponse.ts +56 -0
  159. package/src/Raffle/index.ts +3 -1
  160. package/src/SmarticoAPI.ts +14 -1
  161. package/src/SmarticoLib/index.ts +4 -0
  162. package/src/WSAPI/WSAPIAvatars.ts +267 -49
  163. package/src/WSAPI/WSAPIBonuses.ts +224 -10
  164. package/src/WSAPI/WSAPIGamePick.ts +538 -253
  165. package/src/WSAPI/WSAPIGeneral.ts +156 -14
  166. package/src/WSAPI/WSAPIInbox.ts +545 -56
  167. package/src/WSAPI/WSAPIJackpots.ts +297 -61
  168. package/src/WSAPI/WSAPILeaderBoard.ts +127 -13
  169. package/src/WSAPI/WSAPIMiniGames.ts +465 -49
  170. package/src/WSAPI/WSAPIRaffles.ts +592 -64
  171. package/src/WSAPI/WSAPITournaments.ts +3 -3
  172. package/src/WSAPI/WSAPITypes.ts +243 -132
  173. package/src/WSAPI/WSAPIUser.ts +193 -43
  174. package/tsconfig.json +9 -1
@@ -3,35 +3,158 @@
3
3
 
4
4
  ### getClans()
5
5
 
6
- > **getClans**(`__namedParameters?`): `Promise`\<[`TClans`](../interfaces/TClans.md)\>
6
+ > **getClans**(`params?`): `Promise`\<[`TClans`](../interfaces/TClans.md)\>
7
7
 
8
- Returns clans list payload for the current user.
9
- The returned payload is cached for 30 seconds.
10
- If onUpdate is passed, it will be called when clans response is received.
8
+ Returns the active clans visible to the current user along with the
9
+ user's own membership state (`user_clan_id`, `cooldown_until`,
10
+ `join_date`). Use this to power a clan-picker / clan-browser screen.
11
11
 
12
- **Visitor mode: not supported**
12
+ The returned `clans[]` is server-sorted by `rating_position` ASC
13
+ (1 = highest-ranked). The server has already filtered out archived
14
+ clans, clans the user fails segment conditions for, and (when any
15
+ clan has a non-zero score) clans with a zero rating score.
13
16
 
14
17
  #### Parameters
15
18
 
16
- ##### \_\_namedParameters?
19
+ ##### params?
20
+
21
+ Optional. Omit to fetch without subscribing.
17
22
 
18
23
  ###### onUpdate?
19
24
 
20
25
  (`data`) => `void`
21
26
 
27
+ Callback invoked with the full refreshed
28
+ `TClans` payload whenever the 30 s cache
29
+ expires and a subsequent fetch lands.
30
+ Each call to `getClans` overwrites the
31
+ prior callback.
32
+
22
33
  #### Returns
23
34
 
24
35
  `Promise`\<[`TClans`](../interfaces/TClans.md)\>
25
36
 
37
+ Promise resolving to `TClans`. Empty
38
+ `clans` array if no clans are visible.
39
+
40
+ #### Remarks
41
+
42
+ **Subscription model (`onUpdate`)**
43
+ The callback receives the FULL refreshed `TClans` payload (never a
44
+ diff/patch). Each subsequent call to `getClans({ onUpdate })`
45
+ REPLACES the prior callback. Pass `onUpdate: undefined` (or omit
46
+ it) to keep the prior callback in place; the callback is never
47
+ auto-cleared.
48
+
49
+ **Update triggers** — note that, unlike most other subscription
50
+ methods in the SDK, this callback is **poll-driven only**. There
51
+ is NO server push that refreshes the clans list. The callback
52
+ fires when:
53
+
54
+ 1. The 30-second cache TTL expires and a subsequent `getClans()`
55
+ call (from the consumer) triggers a fresh fetch.
56
+
57
+ Consequence: clan-membership changes (other users joining your
58
+ clan, an operator-driven kick, a clan being archived) surface
59
+ only on the next consumer-driven fetch. If your UI needs
60
+ near-live state, poll `getClans()` on an interval; or re-call
61
+ after a [joinClan](#joinclan) resolves to pick up the new
62
+ `user_clan_id` immediately.
63
+
64
+ **Reading state from the returned payload**
65
+ Drive list rendering from `clans[]` (already sorted). Identify
66
+ the user's own clan via `clan.clan_id === user_clan_id`
67
+ (`null` for clanless users). Detect an active switch-cooldown
68
+ with `cooldown_until !== null` — when set, the user cannot join
69
+ any clan until the cooldown expires, even clans they could
70
+ normally join. The cooldown is **user-level**, not per-clan.
71
+
72
+ `cooldown_until` is an ISO 8601 UTC datetime string with no
73
+ timezone suffix (`"YYYY-MM-DDTHH:MM:SS"`). Parse as UTC:
74
+ `new Date(cooldown_until + 'Z')` or `moment.utc(cooldown_until)`.
75
+ Display in the user's local time. The default cooldown period
76
+ is configured per label by the operator (typically 7 days).
77
+
78
+ `rating_position` is a global rank computed server-side across
79
+ all active clans in the label (1 = highest-rated). Because some
80
+ clans may be hidden by per-user segment visibility, positions
81
+ the user sees may skip (e.g. 1, 3, 7) — don't assume the array
82
+ spans a contiguous range.
83
+
84
+ The `entry_fee_currency_type_id` enum on each clan uses values
85
+ `0` = points, `1` = gems, `2` = diamonds, `3` = free. Compare
86
+ `entry_fee_amount` against the user's matching balance (from
87
+ [getUserProfile](WSAPIUser.md#getuserprofile)) to drive Join button affordability.
88
+
89
+ **Cache TTL**: the SDK caches the response for 30 seconds. Cache
90
+ is fully cleared on login / logout.
91
+
92
+ **Idempotency**: safe. Read-only. Repeated calls within the
93
+ cache window return a deep-cloned cached payload without a
94
+ network round-trip.
95
+
96
+ **Side effects**: none — pure metadata read.
97
+
98
+ **UI guidance**: see [UI Guide — `getClans`](../_media/UIGuide_getClans.md).
99
+
100
+ **Visitor mode**: not supported.
101
+
102
+ #### Example
103
+
104
+ ```ts
105
+ const result = await window._smartico.api.getClans({
106
+ onUpdate: (refreshed) => {
107
+ console.log('[smartico] clans payload refreshed (poll-driven; ~30 s cadence) — re-render the clan list from this payload:', refreshed);
108
+ },
109
+ });
110
+
111
+ console.log('[smartico] user is', result.user_clan_id == null ? 'clanless' : 'in clan ' + result.user_clan_id);
112
+
113
+ // Cooldown handling — affects ALL clans, not a specific one.
114
+ if (result.cooldown_until) {
115
+ const cooldownEndsUtc = new Date(result.cooldown_until + 'Z');
116
+ console.log('[smartico] switch-cooldown active — disable all Join buttons until', cooldownEndsUtc.toLocaleString(),
117
+ '(local). Render the cooldown end on the disabled button label.');
118
+ }
119
+
120
+ // Render clans — already sorted by rating_position ASC.
121
+ for (const clan of result.clans) {
122
+ const isMine = clan.clan_id === result.user_clan_id;
123
+ const isFull = clan.member_count >= clan.capacity_limit;
124
+
125
+ if (isMine) {
126
+ console.log('[smartico] highlight clan', clan.clan_id, 'as "Your clan"; CTA label "Your clan", no-op');
127
+ } else if (result.cooldown_until) {
128
+ console.log('[smartico] clan', clan.clan_id, '— Join blocked by user cooldown; show disabled button with cooldown end date');
129
+ } else if (isFull) {
130
+ console.log('[smartico] clan', clan.clan_id, '— Clan full; show disabled "Clan is full" button');
131
+ } else {
132
+ const label = clan.entry_fee_amount === 0 || clan.entry_fee_currency_type_id === 3
133
+ ? 'Join free'
134
+ : `Join (${clan.entry_fee_amount} ${['Points','Gems','Diamonds'][clan.entry_fee_currency_type_id]})`;
135
+ console.log('[smartico] clan', clan.clan_id, '— render enabled Join button labelled', label);
136
+ }
137
+ }
138
+
139
+ // After a successful joinClan(), re-call getClans manually — no push refresh.
140
+ // const r = await window._smartico.api.joinClan(targetClanId);
141
+ // if (r.errCode === 0) await window._smartico.api.getClans();
142
+ ```
143
+
26
144
  ***
27
145
 
28
146
  ### getClanInfo()
29
147
 
30
148
  > **getClanInfo**(`clanId`): `Promise`\<[`TClanInfo`](../interfaces/TClanInfo.md)\>
31
149
 
32
- Returns detailed information for a specific clan including its members.
150
+ Returns the full detail of a single clan — adds the ranked
151
+ `members[]` roster and a fresh `cooldown_until` on top of the same
152
+ fields exposed on a `TClan` list entry. Use this to power a clan
153
+ detail screen / popup after the user picks an item from
154
+ [getClans](#getclans).
33
155
 
34
- **Visitor mode: not supported**
156
+ Each call is a fresh server round-trip — there is no client cache
157
+ and no push event refreshes detail state. Re-call to refresh.
35
158
 
36
159
  #### Parameters
37
160
 
@@ -39,19 +162,114 @@ Returns detailed information for a specific clan including its members.
39
162
 
40
163
  `number`
41
164
 
165
+ The clan ID from `TClans.clans[i].clan_id`.
166
+
42
167
  #### Returns
43
168
 
44
169
  `Promise`\<[`TClanInfo`](../interfaces/TClanInfo.md)\>
45
170
 
171
+ Promise resolving to `TClanInfo`. Rejects if the
172
+ clan ID is invalid or the user lacks visibility
173
+ for it.
174
+
175
+ #### Remarks
176
+
177
+ **Preconditions**
178
+ Pass a valid `clanId` (typically read from
179
+ `TClans.clans[i].clan_id` returned by [getClans](#getclans)). The
180
+ method works standalone — `getClans()` is not required first —
181
+ but is the only stable source of valid IDs.
182
+
183
+ **Refresh model**
184
+ - **No subscription.** One-shot promise.
185
+ - **No client cache.** Every call sends a network request.
186
+ - **No push event** refreshes the detail. Member-join, kick, or
187
+ contribution-score changes require a fresh `getClanInfo` call.
188
+
189
+ **Returned shape — beyond `TClan`**
190
+ `TClanInfo` adds two things on top of the same identity / capacity
191
+ / fee / rating fields you'd see on a `TClan` list entry:
192
+ 1. A ranked `members[]` array — each entry carries `user_id`,
193
+ `public_username`, `avatar_id` / `avatar_real_id`, the resolved
194
+ `avatar_url`, the member's `position` (rank within this clan),
195
+ `contribution_score`, the `is_me` flag identifying the current
196
+ user's row, and the optional `clean_ext_user_id`. The server
197
+ orders members by score DESC (i.e. `position` ASC); no
198
+ client-side re-sort is required.
199
+ 2. A fresh `cooldown_until` on the clan info itself. This is the
200
+ SAME user-level cooldown as `TClans.cooldown_until` (the
201
+ cooldown is global to the user, not per clan) — but `getClans`
202
+ returns a value cached for up to 30 s while `getClanInfo`
203
+ always returns the current value. If the list-cached cooldown
204
+ has just expired, this detail call will reflect it first.
205
+
206
+ **Username display**: in the default Smartico UI, member rows use
207
+ `public_username`. Some surfaces (e.g. the tournament clan
208
+ drill-down) prefer `clean_ext_user_id` as the primary display
209
+ with `public_username` as fallback — pick the convention that
210
+ matches your product's identity model.
211
+
212
+ **Idempotency**: safe. Read-only.
213
+
214
+ **Side effects**: none — pure metadata read.
215
+
216
+ **UI guidance**: see [UI Guide — `getClanInfo`](../_media/UIGuide_getClanInfo.md).
217
+
218
+ **Visitor mode**: not supported.
219
+
220
+ #### Example
221
+
222
+ ```ts
223
+ const result = await window._smartico.api.getClans();
224
+ const clan = result.clans[0];
225
+
226
+ console.log('[smartico] loading detail for clan', clan.clan_id);
227
+ const detail = await window._smartico.api.getClanInfo(clan.clan_id);
228
+
229
+ console.log('[smartico] render detail with', detail.members.length, 'members');
230
+
231
+ // Find current user's row to power a sticky "me" footer.
232
+ const meRow = detail.members.find(m => m.is_me);
233
+ if (meRow) {
234
+ console.log('[smartico] current user rank in this clan:', meRow.position,
235
+ 'contribution:', meRow.contribution_score, '— render sticky my-member footer');
236
+ }
237
+
238
+ // Resolve Join CTA state using the detail's cooldown (always fresh).
239
+ const isMyClan = detail.clan_id === result.user_clan_id;
240
+ const inCooldown = detail.cooldown_until != null;
241
+ const isFull = detail.member_count >= detail.capacity_limit;
242
+ if (isMyClan) {
243
+ console.log('[smartico] this is the user\'s clan — render disabled "Your clan" button');
244
+ } else if (inCooldown) {
245
+ const ends = new Date(detail.cooldown_until + 'Z');
246
+ console.log('[smartico] user is in clan-switch cooldown until', ends.toLocaleString(),
247
+ '— render disabled cooldown button');
248
+ } else if (isFull) {
249
+ console.log('[smartico] clan is full — render disabled "Clan is full" button');
250
+ } else {
251
+ console.log('[smartico] joinable — render enabled Join button');
252
+ }
253
+ ```
254
+
46
255
  ***
47
256
 
48
257
  ### joinClan()
49
258
 
50
259
  > **joinClan**(`clanId`): `Promise`\<[`TClanJoinResult`](../interfaces/TClanJoinResult.md)\>
51
260
 
52
- Joins a clan on behalf of the current user.
261
+ Joins (or switches into) a clan on behalf of the current user. For
262
+ paid clans (`entry_fee_amount > 0`), the user's balance for the
263
+ matching currency is debited synchronously before the response
264
+ returns. If the user is already in a clan, this is treated as an
265
+ atomic clan-switch (deactivate old membership + insert new + debit
266
+ fee, all in one transaction — the user is never observably
267
+ clanless).
53
268
 
54
- **Visitor mode: not supported**
269
+ A successful response (`errCode === 0`) means: membership is
270
+ persisted, the fee (if any) has been debited, the user's
271
+ switch-cooldown has restarted, and a clan-joined CRM event has
272
+ fired server-side.
55
273
 
56
274
  #### Parameters
57
275
 
@@ -59,8 +277,160 @@ Joins a clan on behalf of the current user.
59
277
 
60
278
  `number`
61
279
 
280
+ The clan ID from `TClans.clans[i].clan_id` (or
281
+ `TClanInfo.clan_id`).
282
+
62
283
  #### Returns
63
284
 
64
285
  `Promise`\<[`TClanJoinResult`](../interfaces/TClanJoinResult.md)\>
65
286
 
287
+ `{ errCode, errMsg }`; success when `errCode === 0`.
288
+ Note camelCase keys (see "Result shape note" above).
289
+
290
+ #### Remarks
291
+
292
+ **Result shape note**: `TClanJoinResult` uses `errCode` / `errMsg`
293
+ (camelCase) — different from most other SDK result types in this
294
+ library which use `err_code` / `err_message` (snake_case). Branch
295
+ on the camelCase keys when reading this method's result.
296
+
297
+ **Preconditions**
298
+ Read the candidate from [getClans](#getclans) and gate the call on
299
+ `clan.member_count < clan.capacity_limit`, `cooldown_until == null`
300
+ (on the `TClans` payload — the cooldown is user-level, not
301
+ per-clan), and the user's balance for `entry_fee_currency_type_id`
302
+ being at least `entry_fee_amount` (free clans bypass this check).
303
+ The SDK forwards the call unconditionally — calling without
304
+ satisfying these will most likely return one of the error codes
305
+ below.
306
+
307
+ **Error codes** (in `errCode`, typed as [JoinClanErrorCode](../enumerations/JoinClanErrorCode.md))
308
+ - `0` (`JOIN_CLAN_OK`) — success; membership persisted, fee
309
+ debited, cooldown restarted.
310
+ - `1000` (`JOIN_CLAN_INVALID_PARAMETERS`) — request body missing
311
+ `clan_id`. Should not occur via the SDK; treat as a programming
312
+ error.
313
+ - `1001` (`JOIN_CLAN_NOT_FOUND`) — clan doesn't exist for this
314
+ label, OR the clan is archived / in draft state. The server
315
+ merges "missing" and "archived" into this single code. Refresh
316
+ `getClans()` and remove the entry from the UI.
317
+ - `1002` (`JOIN_CLAN_FULL`) — capacity reached between the list
318
+ fetch and the join click (`member_count >= capacity_limit` at
319
+ the server's atomic check). Refresh the list — the button will
320
+ become "Clan is full".
321
+ - `1003` (`JOIN_CLAN_INSUFFICIENT_FUNDS`) — insufficient balance
322
+ for the clan's entry fee. A single code covers all three
323
+ currencies (points / gems / diamonds); read
324
+ `clan.entry_fee_currency_type_id` to identify which currency
325
+ the user is short on. Show an insufficient-balance UI naming
326
+ the deficit.
327
+ - `1004` (`JOIN_CLAN_SEGMENT_MISMATCH`) — user does not meet the
328
+ clan's entry segment or numeric conditions. There is no
329
+ client-side pre-guard for this — surface a gating message.
330
+ - `1005` (`JOIN_CLAN_USER_IS_NOT_IN_CLAN`) — this method NEVER
331
+ returns `1005`. The code is shared with the tournament
332
+ registration error space and fires only on
333
+ [registerInTournament](WSAPITournaments.md#registerintournament) responses when a clan-based
334
+ tournament requires the user to be in a clan first.
335
+ - `1006` (`JOIN_CLAN_COOLDOWN_ACTIVE`) — user is inside the
336
+ switch-cooldown window from a previous clan join (default
337
+ 7 days, configurable per label). Surface the
338
+ `TClans.cooldown_until` expiry in the disabled CTA.
339
+ - `1007` (`JOIN_CLAN_ARCHIVED`) — defined on the public enum but
340
+ the server does NOT emit this code in practice (archived clans
341
+ are reported as `1001`). Handle it as a synonym for `1001`
342
+ defensively, but do not rely on it.
343
+ - `1011` (`JOIN_CLAN_JOINED_AFTER_TOURNAMENT_START`) — this
344
+ method does NOT return `1011` directly. The code fires on
345
+ [registerInTournament](WSAPITournaments.md#registerintournament) responses when the user joined
346
+ their current clan after the tournament started AND already
347
+ has tournament scores.
348
+ - other non-zero — generic server error. Surface `errMsg` if any.
349
+
350
+ **Idempotency**: NOT safe — and notably dangerous. Calling
351
+ `joinClan` with the user's CURRENT `clanId` does NOT short-circuit
352
+ server-side; the call is treated as a clan-switch to the same
353
+ clan. If the label's cooldown is non-zero (the default), the
354
+ second call returns `1006`. If the cooldown is configured to
355
+ zero, the second call will deduct the entry fee AGAIN, deactivate
356
+ the membership, and re-insert it. The SDK does NOT enforce an
357
+ in-flight lock. Consumers MUST guard the call site against
358
+ double-clicks (set a local "joining" flag on click, clear it on
359
+ response) AND short-circuit when the user is already in the
360
+ target clan.
361
+
362
+ **Refresh after success (and after failure)**
363
+ The SDK does NOT automatically refresh the [getClans](#getclans) cache
364
+ after a join. Manually re-call `getClans()` on success to pick up
365
+ the new `user_clan_id`, refreshed `member_count`, and the new
366
+ `cooldown_until` value. Until you do, the cached `TClans` payload
367
+ is stale.
368
+
369
+ **Side effects** (consumer-observable on success)
370
+ - Entry-fee currency balance decreases by `entry_fee_amount`
371
+ (visible on the next [getUserProfile](WSAPIUser.md#getuserprofile) refresh / via the
372
+ user-properties update channel).
373
+ - The user's clan membership row is created (or replaced, on a
374
+ switch). `member_count` on the new clan increments by 1; on a
375
+ switch, the old clan's `member_count` decrements by 1 in the
376
+ same transaction.
377
+ - The user's switch-cooldown restarts at the join time (the
378
+ `TClans.cooldown_until` field on the next list fetch reflects
379
+ the new expiry).
380
+ - Server-side CRM analytics fire (clan-joined event, currency-
381
+ deducted event for paid clans). These surface only as
382
+ server-side traces; the SDK consumer observes them indirectly
383
+ via balance / membership refresh.
384
+
385
+ **UI guidance**: see [UI Guide — `joinClan`](../_media/UIGuide_joinClan.md).
386
+
387
+ **Visitor mode**: not supported. The server rejects the request
388
+ for unauthenticated sessions.
389
+
390
+ #### Example
391
+
392
+ ```ts
393
+ const result = await window._smartico.api.getClans();
394
+ const target = result.clans.find(c => c.clan_id === clanId);
395
+
396
+ if (!target) {
397
+ console.log('[smartico] clan not in current list — refresh getClans and retry');
398
+ return;
399
+ }
400
+ if (target.clan_id === result.user_clan_id) {
401
+ console.log('[smartico] user is already in this clan — short-circuit; do NOT call joinClan again, the server will treat it as a switch and may re-deduct the fee');
402
+ return;
403
+ }
404
+ if (result.cooldown_until) {
405
+ console.log('[smartico] user is in cooldown — disable Join until', new Date(result.cooldown_until + 'Z').toLocaleString());
406
+ return;
407
+ }
408
+ if (target.member_count >= target.capacity_limit) {
409
+ console.log('[smartico] clan is full — disable Join');
410
+ return;
411
+ }
412
+
413
+ console.log('[smartico] join starting — set in-flight flag, show loading dots on Join button, keep modal open');
414
+ const r = await window._smartico.api.joinClan(target.clan_id);
415
+ console.log('[smartico] join response received — clear in-flight flag');
416
+
417
+ if (r.errCode === 0) {
418
+ console.log('[smartico] joined successfully — refresh getClans to pick up the new user_clan_id, member_count, and cooldown_until');
419
+ await window._smartico.api.getClans();
420
+ } else if (r.errCode === 1002) {
421
+ console.error('[smartico] clan is full — refresh list, hide the Join CTA on the now-full clan');
422
+ } else if (r.errCode === 1003) {
423
+ const currencyLabel = ['points', 'gems', 'diamonds'][target.entry_fee_currency_type_id] ?? 'currency';
424
+ console.error('[smartico] insufficient', currencyLabel, '— need', target.entry_fee_amount);
425
+ } else if (r.errCode === 1004) {
426
+ console.error('[smartico] user does not meet segment / entry conditions — show a gating message');
427
+ } else if (r.errCode === 1006) {
428
+ console.error('[smartico] cooldown active — surface the cooldown_until from getClans:', result.cooldown_until);
429
+ } else if (r.errCode === 1001) {
430
+ console.error('[smartico] clan not found or archived — refresh list and remove from UI');
431
+ } else {
432
+ console.error('[smartico] join failed — show a generic error toast with this message:', r.errMsg);
433
+ }
434
+ ```
435
+
66
436
  ***