@smartico/public-api 0.0.378 → 0.0.380

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 (99) hide show
  1. package/.nvmrc +1 -1
  2. package/README.md +31 -2
  3. package/dist/Store/BuyStoreItemErrorCode.d.ts +9 -1
  4. package/dist/Tournaments/TournamentRegistrationError.d.ts +3 -1
  5. package/dist/WSAPI/WSAPI.d.ts +13 -1235
  6. package/dist/WSAPI/WSAPIAvatars.d.ts +78 -0
  7. package/dist/WSAPI/WSAPIBase.d.ts +71 -0
  8. package/dist/WSAPI/WSAPIBonuses.d.ts +26 -0
  9. package/dist/WSAPI/WSAPIClans.d.ts +388 -0
  10. package/dist/WSAPI/WSAPIGamePick.d.ts +308 -0
  11. package/dist/WSAPI/WSAPIGeneral.d.ts +30 -0
  12. package/dist/WSAPI/WSAPIInbox.d.ts +126 -0
  13. package/dist/WSAPI/WSAPIJackpots.d.ts +109 -0
  14. package/dist/WSAPI/WSAPILeaderBoard.d.ts +25 -0
  15. package/dist/WSAPI/WSAPIMiniGames.d.ts +94 -0
  16. package/dist/WSAPI/WSAPIMissions.d.ts +517 -0
  17. package/dist/WSAPI/WSAPIRaffles.d.ts +119 -0
  18. package/dist/WSAPI/WSAPIStore.d.ts +656 -0
  19. package/dist/WSAPI/WSAPITournaments.d.ts +501 -0
  20. package/dist/WSAPI/WSAPITypes.d.ts +145 -85
  21. package/dist/WSAPI/WSAPIUser.d.ts +383 -0
  22. package/dist/index.js +3818 -1528
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.modern.mjs +3180 -981
  25. package/dist/index.modern.mjs.map +1 -1
  26. package/docs/api/README.md +56 -2
  27. package/docs/api/classes/WSAPIAvatars.md +116 -0
  28. package/docs/api/classes/WSAPIBonuses.md +50 -0
  29. package/docs/api/classes/WSAPIClans.md +66 -0
  30. package/docs/api/classes/WSAPIGamePick.md +181 -0
  31. package/docs/api/classes/WSAPIGeneral.md +63 -0
  32. package/docs/api/classes/WSAPIInbox.md +277 -0
  33. package/docs/api/classes/WSAPIJackpots.md +185 -0
  34. package/docs/api/classes/WSAPILeaderBoard.md +39 -0
  35. package/docs/api/classes/WSAPIMiniGames.md +175 -0
  36. package/docs/api/classes/WSAPIMissions.md +611 -0
  37. package/docs/api/classes/WSAPIRaffles.md +195 -0
  38. package/docs/api/classes/WSAPIStore.md +744 -0
  39. package/docs/api/classes/WSAPITournaments.md +104 -0
  40. package/docs/api/classes/WSAPIUser.md +200 -0
  41. package/docs/api/enumerations/AchievementClaimPeriodTypeId.md +13 -0
  42. package/docs/api/enumerations/BuyStoreItemErrorCode.md +32 -0
  43. package/docs/api/interfaces/ClanPrizePublicMeta.md +41 -0
  44. package/docs/api/interfaces/ClanPrizeStructureEntry.md +7 -1
  45. package/docs/api/interfaces/ClanPublicMeta.md +4 -0
  46. package/docs/api/interfaces/SAWTemplateUI.md +10 -0
  47. package/docs/api/interfaces/TAchCategory.md +19 -4
  48. package/docs/api/interfaces/TBuyStoreItemResult.md +26 -8
  49. package/docs/api/interfaces/TMissionClaimRewardResult.md +14 -3
  50. package/docs/api/interfaces/TMissionOptInResult.md +10 -3
  51. package/docs/api/interfaces/TMissionOrBadge.md +6 -2
  52. package/docs/api/interfaces/TStoreItem.md +21 -1
  53. package/docs/api/interfaces/TTournamentDetailed.md +8 -0
  54. package/docs/api/interfaces/UserAchievement.md +1 -1
  55. package/docs/ui/clans/UIGuide_getClanInfo.md +153 -0
  56. package/docs/ui/clans/UIGuide_getClans.md +172 -0
  57. package/docs/ui/clans/UIGuide_joinClan.md +196 -0
  58. package/docs/ui/missions/UIGuide_getAchCategories.md +17 -0
  59. package/docs/ui/missions/UIGuide_getBadges.md +237 -0
  60. package/docs/ui/missions/UIGuide_getMissions.md +268 -0
  61. package/docs/ui/missions/UIGuide_requestMissionClaimReward.md +21 -0
  62. package/docs/ui/missions/UIGuide_requestMissionOptIn.md +16 -0
  63. package/docs/ui/store/UIGuide_buyStoreItem.md +133 -0
  64. package/docs/ui/store/UIGuide_getStoreCategories.md +19 -0
  65. package/docs/ui/store/UIGuide_getStoreItems.md +264 -0
  66. package/docs/ui/store/UIGuide_getStorePurchasedItems.md +122 -0
  67. package/docs/ui/tournaments/UIGuide_getClanTournamentPlayers.md +140 -0
  68. package/docs/ui/tournaments/UIGuide_getTournamentInstanceInfo.md +265 -0
  69. package/docs/ui/tournaments/UIGuide_getTournamentsList.md +236 -0
  70. package/docs/ui/tournaments/UIGuide_registerInTournament.md +156 -0
  71. package/docs/ui/user/UIGuide_getLevels.md +162 -0
  72. package/docs/ui/user/UIGuide_getUserProfile.md +213 -0
  73. package/package.json +3 -3
  74. package/scripts/postdoc-cleanup.js +195 -0
  75. package/src/Missions/MissionsUtils.ts +5 -2
  76. package/src/Store/BuyStoreItemErrorCode.ts +8 -0
  77. package/src/Store/StoreItem.ts +1 -0
  78. package/src/Store/StoreItemPurchased.ts +1 -0
  79. package/src/Store/StoreItemType.ts +1 -1
  80. package/src/Tournaments/TournamentRegistrationError.ts +4 -0
  81. package/src/WSAPI/WSAPI.ts +27 -2007
  82. package/src/WSAPI/WSAPIAvatars.ts +127 -0
  83. package/src/WSAPI/WSAPIBase.ts +93 -0
  84. package/src/WSAPI/WSAPIBonuses.ts +54 -0
  85. package/src/WSAPI/WSAPIClans.ts +417 -0
  86. package/src/WSAPI/WSAPIGamePick.ts +382 -0
  87. package/src/WSAPI/WSAPIGeneral.ts +57 -0
  88. package/src/WSAPI/WSAPIInbox.ts +222 -0
  89. package/src/WSAPI/WSAPIJackpots.ts +212 -0
  90. package/src/WSAPI/WSAPILeaderBoard.ts +40 -0
  91. package/src/WSAPI/WSAPIMiniGames.ts +181 -0
  92. package/src/WSAPI/WSAPIMissions.ts +572 -0
  93. package/src/WSAPI/WSAPIRaffles.ts +175 -0
  94. package/src/WSAPI/WSAPIStore.ts +732 -0
  95. package/src/WSAPI/WSAPITournaments.ts +541 -0
  96. package/src/WSAPI/WSAPITypes.ts +145 -87
  97. package/src/WSAPI/WSAPIUser.ts +456 -0
  98. package/tsconfig.json +17 -1
  99. package/docs/api/classes/WSAPI.md +0 -1927
@@ -0,0 +1,611 @@
1
+ # Class: WSAPIMissions
2
+ ## Methods
3
+
4
+ ### getMissions()
5
+
6
+ > **getMissions**(`params?`): `Promise`\<[`TMissionOrBadge`](../interfaces/TMissionOrBadge.md)[]\>
7
+
8
+ Returns all missions visible to the current user, scoped server-side
9
+ to what the user is qualified to see. Optionally subscribes to live
10
+ updates via `onUpdate`: the callback is invoked with the full, refreshed
11
+ mission array whenever the server pushes a mission-state-changing event.
12
+
13
+ The returned list is the canonical source of truth for mission state
14
+ (`is_opted_in`, `progress`, `is_completed`, `completion_count`, etc.).
15
+ Consumers should NEVER mutate it optimistically — always wait for the
16
+ `onUpdate` callback (or a fresh `getMissions()` call after the 30s cache
17
+ window expires) to observe the new state.
18
+
19
+ #### Parameters
20
+
21
+ ##### params?
22
+
23
+ Optional. Omit to fetch without subscribing.
24
+
25
+ ###### onUpdate?
26
+
27
+ (`data`) => `void`
28
+
29
+ Callback invoked with the full refreshed
30
+ mission array on every server-pushed update
31
+ (after opt-in / claim-reward calls, or when
32
+ the server pushes an asynchronous
33
+ mission-state change). Each call to
34
+ `getMissions` overwrites the prior callback.
35
+ Never fires in visitor mode.
36
+
37
+ #### Returns
38
+
39
+ `Promise`\<[`TMissionOrBadge`](../interfaces/TMissionOrBadge.md)[]\>
40
+
41
+ Promise resolving to the current list of
42
+ missions visible to the user. Empty array if
43
+ the user has no missions (or if the visitor
44
+ proxy user has none configured).
45
+
46
+ #### Remarks
47
+
48
+ **Subscription model (`onUpdate`)**
49
+ The callback registered via `onUpdate` is invoked with the FULL refreshed
50
+ mission array — never a diff/patch — whenever any of these occur:
51
+ - `requestMissionOptIn(...)` resolves (any `err_code`)
52
+ - `requestMissionClaimReward(...)` resolves (any `err_code`)
53
+ - The server pushes an asynchronous mission-state change — e.g. task
54
+ progress advances (for tasks configured to push updates), a mission
55
+ completes, or the user's level / gamification visibility tags change
56
+
57
+ Each subsequent call to `getMissions({ onUpdate })` REPLACES the prior
58
+ callback — there is only one active subscriber at a time. Pass
59
+ `onUpdate: undefined` (or omit it) to keep the prior callback in place;
60
+ the callback is never auto-cleared.
61
+
62
+ **What triggers an asynchronous mission-state refresh** (consumer-relevant)
63
+ - A user activity (deposit, bet, login, etc.) advances task progress
64
+ on a task configured to push updates, OR the activity completes a
65
+ mission.
66
+ - The user's level changes, or other gamification visibility tags change.
67
+ - Operator-side BO mission-config edits do NOT push to connected clients —
68
+ those changes surface only on the next cache miss, or after one of the
69
+ other trigger events above fires.
70
+
71
+ **What's in the returned list / what's filtered server-side**
72
+ The response is pre-filtered by the server. Missions EXCLUDED before
73
+ reaching the SDK:
74
+ - Missions in `DRAFT` or `ARCHIVED` status (see [AchievementStatus](../enumerations/AchievementStatus.md)).
75
+ - Missions where the user fails visibility conditions. Exception:
76
+ completed missions may remain visible after the user no longer
77
+ qualifies (configurable per-brand).
78
+ - `FEATURED_MANUALLY` missions that are still locked — they appear
79
+ only after the user unlocks them.
80
+ - Locked missions whose `ach_public_meta.hide_locked_mission` is
81
+ `true` or `null` — UNLESS the mission has
82
+ `expose_immediately_on_unlock = true` (then the locked mission is
83
+ included as a teaser).
84
+ - `RECURRING` missions between cycles, until the next cycle starts.
85
+ - `RECURRING_QUANTITY` missions outside their `active_from_ts` /
86
+ `active_till_ts` window.
87
+
88
+ **Field semantics** (non-obvious fields on `TMissionOrBadge`)
89
+ - `is_opted_in` does NOT flip optimistically. It flips only after the
90
+ opt-in round-trip completes and the auto-refresh lands. Render the
91
+ pending state from your own loading flag, not from this field.
92
+ - `dt_start` doubles as opt-in timestamp for opt-in missions and as
93
+ unlock timestamp for previously-locked missions; for time-limited
94
+ missions, expiration is `dt_start + time_limit_ms`.
95
+ - `availability_status` is the canonical derived state — see
96
+ [AchievementAvailabilityStatus](../enumerations/AchievementAvailabilityStatus.md) for the full enum. Use this
97
+ field to drive UI chips ("missed", "coming soon", "needs opt-in",
98
+ "in progress", etc.) rather than recomputing from raw timestamps.
99
+ - `next_recurrence_date_ts` is populated for `RECURRING` /
100
+ `RECURRING_QUANTITY` missions; ignore it when the mission has an
101
+ `active_till_ts` that has already passed.
102
+ - `only_in_custom_section: true` means the mission should be HIDDEN
103
+ from the main missions view and shown only inside its custom
104
+ section. The list still includes it — filter it out client-side
105
+ for general views.
106
+
107
+ **UI guidance**: see [UI Guide — `getMissions`](../../ui/missions/UIGuide_getMissions.md).
108
+
109
+ #### Example
110
+
111
+ ```ts
112
+ // 1. Initial fetch + subscribe to live updates
113
+ const missions = await window._smartico.api.getMissions({
114
+ onUpdate: (refreshed) => {
115
+ console.log('[smartico] mission list refreshed — re-render the entire mission list UI from this array, do not merge with prior state:', refreshed);
116
+
117
+ // 2. onUpdate fires for: opt-in/claim responses, task-progress
118
+ // pushes that complete a mission or move a "pushToClient" task,
119
+ // and user-level changes. Treat each call as authoritative.
120
+ const claimable = refreshed.filter(m =>
121
+ m.is_completed && m.requires_prize_claim && !m.prize_claimed_date_ts,
122
+ );
123
+ if (claimable.length > 0) {
124
+ console.log('[smartico] there are unclaimed mission prizes — surface a "Claim" CTA on each of these missions:', claimable.map(m => m.id));
125
+ }
126
+ },
127
+ });
128
+
129
+ // 3. Filter/render from the returned list. The server already excludes
130
+ // DRAFT/ARCHIVED missions and ones the user can't see. You may need
131
+ // client-side filters for view-specific concerns (custom sections,
132
+ // completed/missed tabs).
133
+ const generalView = missions
134
+ .filter(m => !m.only_in_custom_section)
135
+ .sort((a, b) => (a.position ?? 0) - (b.position ?? 0));
136
+
137
+ console.log('[smartico] initial mission list ready — render', generalView.length, 'missions sorted by position');
138
+
139
+ // 4. Visitor-mode equivalent: onUpdate is accepted but never fires.
140
+ // Re-poll if you need fresh data.
141
+ // const visitorMissions = await window._smartico.vapi('EN').getMissions();
142
+ // console.log('[smartico] visitor missions are the brand proxy user\'s — per-user fields are not meaningful');
143
+ ```
144
+
145
+ ***
146
+
147
+ ### getBadges()
148
+
149
+ > **getBadges**(): `Promise`\<[`TMissionOrBadge`](../interfaces/TMissionOrBadge.md)[]\>
150
+
151
+ Returns all the badges available to the current user.
152
+
153
+ Badges are completion-tracked achievements that auto-unlock once the user
154
+ meets their criteria — there is no opt-in step, no claim step, and no
155
+ task-description reveal. Contrast with [getMissions](#getmissions), which returns
156
+ the same `TMissionOrBadge[]` shape but represents explicit opt-in flows.
157
+
158
+ #### Returns
159
+
160
+ `Promise`\<[`TMissionOrBadge`](../interfaces/TMissionOrBadge.md)[]\>
161
+
162
+ Promise resolving to `TMissionOrBadge[]`. Every item has
163
+ `type === 'badge'`. Cached for 30 s under the `Badges` key.
164
+
165
+ #### Remarks
166
+
167
+ **Why no `onUpdate` callback?**
168
+ Unlike `getMissions({ onUpdate })`, this method does NOT accept an
169
+ `onUpdate` subscription. Internally the SDK never refreshes the `Badges`
170
+ cache from server pushes: the achievement-related push events
171
+ (mission opt-in / claim / reload) refresh the missions cache, NOT
172
+ the badges cache. The badges cache is flushed only by:
173
+ - its 30-second TTL
174
+ - a full SDK cache wipe at login / logout
175
+
176
+ **How to refresh the badge list**
177
+ If you need near-live badge state, poll manually after the TTL expires.
178
+ The first call inside the 30 s window returns the cached payload; the
179
+ first call after the TTL triggers a fresh server round-trip. Example:
180
+ ```ts
181
+ setInterval(async () => {
182
+ const badges = await window._smartico.api.getBadges();
183
+ console.log('[smartico] badge list polled — re-render badge grid from this array', badges);
184
+ }, 30_000);
185
+ ```
186
+ Alternatively, re-fetch on a domain event your app already handles
187
+ (e.g. after the user finishes a game round or claims a bonus).
188
+
189
+ **Field semantics — badges vs. missions** (same `TMissionOrBadge` type,
190
+ different feature semantics; mission-only fields are present on badge
191
+ objects but should be ignored by badge UI):
192
+ - `is_requires_optin` — always `false` for badges in practice; the BO
193
+ badge editor does not expose this field, so live badge rows never
194
+ have it set to `true`.
195
+ - `is_opted_in` — irrelevant for badges; do not gate badge UI on it.
196
+ - `time_limit_ms` — NOT used for badges (no opt-in time to count from).
197
+ Badges express their time window with `active_from_ts` and
198
+ `active_till_ts` (absolute calendar timestamps) instead.
199
+ - `badgeTimeLimitState` — badge-only computed field, injected by the
200
+ SDK before returning. Enum `BadgesTimeLimitStates`:
201
+ `BeforeStartDate` (0), `AfterStartDateNoProgress` (1),
202
+ `AfterStartDateNoProgressAndEndDate` (2),
203
+ `AfterStartDateWithProgressAndEndDate` (3),
204
+ `AfterEndDateNotStarted` (4), `AfterEndDateWithProgress` (5).
205
+ Use this — not `time_limit_ms` — to drive badge availability chips.
206
+ - `is_locked` — for badges this is only meaningful when
207
+ `badgeTimeLimitState === BeforeStartDate` (time window not started).
208
+ Badges have no prerequisite-mission lock concept, so
209
+ `unlock_mission_description` is not used in badge UIs.
210
+ - `tasks[]` — present, but badge UIs deliberately hide task
211
+ descriptions ("surprise mechanics"): show only `name`, not
212
+ `description`. Display stage count (`completed_tasks` / total) rather
213
+ than a percentage progress bar.
214
+ - `requires_prize_claim`, `prize_claimed_date_ts`, `claim_button_title`,
215
+ `claim_button_action`, `prize_claim_expiration_date` — defined on
216
+ the type but not used in badge flows (no claim workflow for badges).
217
+ - `max_completion_count`, `completion_count`, `next_recurrence_date_ts`,
218
+ `milliseconds_till_available` — recurring-mission fields; not used in
219
+ badge UIs.
220
+ - `category_ids` — primary navigation field for badges. Call
221
+ [getAchCategories](#getachcategories) to resolve category metadata and group
222
+ badges by category (badge UIs typically show all categories
223
+ simultaneously as collapsible sections, each labeled with
224
+ "completed / total").
225
+
226
+ **Idempotency / Side effects**: fetch-only; safe to call repeatedly.
227
+ The cache layer deduplicates concurrent calls within the TTL window.
228
+
229
+ **UI guidance**: see [UI Guide — `getBadges`](../../ui/missions/UIGuide_getBadges.md).
230
+
231
+ #### Example
232
+
233
+ ```ts
234
+ const [badges, categories] = await Promise.all([
235
+ window._smartico.api.getBadges(),
236
+ window._smartico.api.getAchCategories(),
237
+ ]);
238
+
239
+ console.log('[smartico] badges fetched — render grouped grid with', badges.length, 'badges across', categories.length, 'categories');
240
+
241
+ for (const category of categories) {
242
+ const inCategory = badges.filter(b => b.category_ids?.includes(category.id));
243
+ const completed = inCategory.filter(b => b.is_completed).length;
244
+ console.log(`[smartico] render category section "${category.name}" header showing ${completed}/${inCategory.length}`);
245
+
246
+ for (const badge of inCategory) {
247
+ // Use badgeTimeLimitState (not time_limit_ms) for availability chip
248
+ if (badge.badgeTimeLimitState === 0) { // BadgesTimeLimitStates.BeforeStartDate
249
+ console.log('[smartico] badge not yet started — render grayscaled card with "Starts on" chip for badge', badge.id);
250
+ } else if (badge.is_completed) {
251
+ console.log('[smartico] badge completed — render with completed styling and check-mark for badge', badge.id);
252
+ } else {
253
+ console.log('[smartico] badge in progress — render with stage counter (completed_tasks / total) for badge', badge.id);
254
+ }
255
+ }
256
+ }
257
+
258
+ // No onUpdate available — poll if live state matters
259
+ setInterval(async () => {
260
+ try {
261
+ const fresh = await window._smartico.api.getBadges();
262
+ console.log('[smartico] badge poll tick — diff against previous list and re-render any changed badge cards', fresh);
263
+ } catch (e) {
264
+ console.error('[smartico] badge poll failed — keep showing last known state, retry on next tick:', e);
265
+ }
266
+ }, 30_000);
267
+ ```
268
+
269
+ ***
270
+
271
+ ### getAchCategories()
272
+
273
+ > **getAchCategories**(): `Promise`\<[`TAchCategory`](../interfaces/TAchCategory.md)[]\>
274
+
275
+ Returns the list of active mission/badge categories configured for the current label.
276
+ Categories are operator-defined groupings used to organize missions and badges into
277
+ sections, tabs, or filter chips in the gamification UI. The same category list serves
278
+ BOTH missions and badges — there is no separate "mission categories" vs "badge
279
+ categories" split on the server.
280
+
281
+ #### Returns
282
+
283
+ `Promise`\<[`TAchCategory`](../interfaces/TAchCategory.md)[]\>
284
+
285
+ Array of `{ id, name, order }`. Empty array if no active categories are
286
+ configured for the label. `name` is pre-translated. Order is NOT applied — caller
287
+ must sort.
288
+
289
+ #### Remarks
290
+
291
+ **What categories are**
292
+ - Operator-defined groupings, configured per-label by the brand operator.
293
+ Only active categories are returned; inactive/deleted ones are filtered out.
294
+ - Each `TAchCategory` exposes `{ id, name, order }`. `name` is the display label;
295
+ `order` is the relative position (lower = appears first).
296
+ - The same array covers BOTH missions and badges — categories are not filtered by
297
+ entity type on the server.
298
+
299
+ **Localization / translation**
300
+ - `name` is **pre-translated server-side** to the authenticated user's stored language.
301
+ In visitor mode, the language passed to `_smartico.vapi(lang)` drives the
302
+ translation. Consumers never need to translate `name` themselves.
303
+ - Fallback if no translation exists for the user's language: the brand's EN value
304
+ is returned. The field is never null.
305
+
306
+ **Sort order**
307
+ The server does NOT pre-sort. Sort client-side before rendering:
308
+ `categories.sort((a, b) => a.order - b.order)`.
309
+
310
+ **How missions/badges join to categories**
311
+ Each `TMissionOrBadge` exposes `category_ids: number[]` (zero-or-more,
312
+ many-to-many). A single mission/badge can belong to multiple categories
313
+ simultaneously, or to none. To render a "category → items" view:
314
+ `missions.filter(m => m.category_ids.includes(cat.id))`.
315
+
316
+ **Uncategorized items**
317
+ Items with `category_ids: []` are NOT represented by a synthetic "Other" entry.
318
+ If your UI needs one, generate it client-side by checking
319
+ `item.category_ids.length === 0`.
320
+
321
+ **Empty categories**
322
+ A category may be returned even if zero missions/badges reference it. Filter
323
+ out empty categories on the consumer side if you don't want to render section
324
+ headers for them.
325
+
326
+ **Refresh cadence / cache**
327
+ - The SDK caches the response for 30 seconds. Subsequent calls within that
328
+ window return the cached array without a network round-trip.
329
+ - The cache is NOT invalidated by mission/badge push events — operator-driven
330
+ category changes propagate at the next 30 s cache miss.
331
+ - Cache is fully cleared on login / logout.
332
+ - In **visitor mode** additional server-side caching applies on top of the
333
+ SDK cache; worst-case staleness for an edited/added category is on the order
334
+ of a few minutes for anonymous viewers.
335
+
336
+ **Idempotency**: safe. Read-only metadata fetch. Cache de-duplicates concurrent calls.
337
+
338
+ **Side effects**: none — pure metadata read.
339
+
340
+ **UI guidance**: see [UI Guide — `getAchCategories`](../../ui/missions/UIGuide_getAchCategories.md).
341
+
342
+ #### Example
343
+
344
+ ```ts
345
+ const categories = await window._smartico.api.getAchCategories();
346
+
347
+ if (categories.length === 0) {
348
+ console.log('[smartico] no categories configured — render missions/badges as a flat list without section headers');
349
+ } else {
350
+ const sorted = [...categories].sort((a, b) => a.order - b.order);
351
+ console.log('[smartico] categories loaded — render one tab/section per entry in this order:', sorted.map(c => c.name));
352
+
353
+ // Join to missions: render each category as a section
354
+ const missions = await window._smartico.api.getMissions();
355
+ for (const cat of sorted) {
356
+ const items = missions.filter(m => m.category_ids.includes(cat.id));
357
+ if (items.length === 0) {
358
+ console.log('[smartico] category has no missions — skip rendering its section header:', cat.name);
359
+ continue;
360
+ }
361
+ console.log(`[smartico] render section "${cat.name}" with ${items.length} mission(s)`);
362
+ }
363
+
364
+ // Uncategorized items: surface in a caller-provided "Other" bucket if needed
365
+ const uncategorized = missions.filter(m => m.category_ids.length === 0);
366
+ if (uncategorized.length > 0) {
367
+ console.log('[smartico] uncategorized missions exist — render a caller-provided "Other" section; server does NOT return id:-1:', uncategorized.length);
368
+ }
369
+ }
370
+
371
+ // Visitor mode — same shape, language driven by vapi(lang) argument
372
+ const visitorCats = await window._smartico.vapi('DE').getAchCategories();
373
+ console.log('[smartico] visitor-mode categories (names translated to DE):', visitorCats);
374
+ ```
375
+
376
+ ***
377
+
378
+ ### requestMissionOptIn()
379
+
380
+ > **requestMissionOptIn**(`mission_id`): `Promise`\<[`TMissionOptInResult`](../interfaces/TMissionOptInResult.md)\>
381
+
382
+ Opt the current user in to a mission. The mission must have
383
+ `is_requires_optin: true` (from `getMissions()`) and must not already
384
+ be opted-in or locked. After a successful opt-in, the user's task
385
+ progress starts counting toward this mission — events fired BEFORE
386
+ opt-in do not count retroactively (except `UNLOCK_ACHIEVEMENT` tasks,
387
+ which are exempt and can fire pre-opt-in).
388
+
389
+ #### Parameters
390
+
391
+ ##### mission\_id
392
+
393
+ `number`
394
+
395
+ The mission `id` (from `getMissions()`)
396
+
397
+ #### Returns
398
+
399
+ `Promise`\<[`TMissionOptInResult`](../interfaces/TMissionOptInResult.md)\>
400
+
401
+ `{ err_code, err_message }`; success when `err_code === 0`
402
+
403
+ #### Remarks
404
+
405
+ **Preconditions**
406
+ Only call when the mission has `is_requires_optin === true`,
407
+ `is_opted_in === false`, and `is_locked === false`. Calling on a
408
+ mission that does not require opt-in is wasted; calling twice returns
409
+ `40010`.
410
+
411
+ **Error codes** (in `err_code`)
412
+ - `0` — success; task progress counting begins
413
+ - `105` — wrong mission id (different label) OR visibility conditions
414
+ not met. Same code covers both; use `err_message` to disambiguate.
415
+ - `40010` — already opted-in. Safe to treat as "no-op success"; usually
416
+ means local mission state was stale (e.g. user opted in from another
417
+ session/tab).
418
+ - `40013` — mission not opt-in-able: missing `requires_optin`, in
419
+ DRAFT/ARCHIVED state, or outside its `active_from_ts` /
420
+ `active_till_ts` window.
421
+ - `40014` — mission is locked; user has unlock-tasks remaining.
422
+ Surface the mission's `unlock_mission_description` to guide them.
423
+ - other non-zero — generic server error. Surface `err_message` if any.
424
+
425
+ **Time-limited missions**
426
+ For missions with `time_limit_ms > 0`, the countdown begins at opt-in
427
+ time, not at mission creation. Server records expiration as
428
+ `optin_date + time_limit_ms`.
429
+
430
+ **Refresh after success**
431
+ The SDK refreshes its mission cache automatically on opt-in response;
432
+ any `onUpdate` callback passed to a prior `getMissions({ onUpdate })`
433
+ fires with the new state shortly after. No manual re-fetch needed.
434
+
435
+ **Idempotency**: not idempotent. A second call returns `40010`. Guard
436
+ the call site against double-clicks.
437
+
438
+ **Side effects**: does NOT award points / badges / levels directly.
439
+ Rewards come from task completion, which is itself gated on opt-in.
440
+ May affect visibility of other missions whose visibility conditions
441
+ depend on this opt-in.
442
+
443
+ **UI guidance**: see [UI Guide — `requestMissionOptIn`](../../ui/missions/UIGuide_requestMissionOptIn.md).
444
+
445
+ **Visitor mode: not supported**
446
+
447
+ #### Example
448
+
449
+ ```ts
450
+ const missions = await window._smartico.api.getMissions({
451
+ onUpdate: (m) => console.log('[smartico] mission list updated — re-render mission UI from this new array', m),
452
+ });
453
+ const mission = missions.find(m => m.id === missionId);
454
+
455
+ if (!mission || !mission.is_requires_optin || mission.is_opted_in || mission.is_locked) {
456
+ console.log('[smartico] opt-in not applicable — keep UI as-is');
457
+ return;
458
+ }
459
+
460
+ console.log('[smartico] opt-in starting — show loading spinner on opt-in button and disable it to prevent double-clicks');
461
+ const r = await window._smartico.api.requestMissionOptIn(mission.id);
462
+ console.log('[smartico] opt-in response received — clear loading spinner');
463
+
464
+ if (r.err_code === 0 || r.err_code === 40010) {
465
+ console.log('[smartico] opt-in succeeded (or was already done) — onUpdate above will fire with refreshed mission state; no manual re-fetch needed');
466
+ } else if (r.err_code === 40014) {
467
+ console.error('[smartico] mission is locked — surface the unlock description as guidance:', mission.unlock_mission_description);
468
+ } else {
469
+ console.error('[smartico] opt-in failed — show an error toast with this message to the user:', r.err_message);
470
+ }
471
+ ```
472
+
473
+ ***
474
+
475
+ ### requestMissionClaimReward()
476
+
477
+ > **requestMissionClaimReward**(`mission_id`, `ach_completed_id`): `Promise`\<[`TMissionClaimRewardResult`](../interfaces/TMissionClaimRewardResult.md)\>
478
+
479
+ Claim the prize for a completed mission. This is the step that runs
480
+ AFTER `requestMissionOptIn()` (when applicable) and AFTER the user has
481
+ completed all of the mission's tasks. Only call when the mission has
482
+ `requires_prize_claim === true`, `is_completed === true`, and the prize
483
+ has not already been claimed.
484
+
485
+ The points / bonuses / badge grants associated with the mission are
486
+ awarded server-side synchronously inside this call (before the response
487
+ is returned), so a successful response (`err_code === 0`) means the
488
+ reward has already landed in the user's account.
489
+
490
+ #### Parameters
491
+
492
+ ##### mission\_id
493
+
494
+ `number`
495
+
496
+ The mission `ach_id` (from `getMissions()`).
497
+
498
+ ##### ach\_completed\_id
499
+
500
+ `number`
501
+
502
+ The completion-row identifier, read from
503
+ `mission.ach_completed_id` on the same mission
504
+ object fetched via `getMissions()`. Never
505
+ fabricate or cache across sessions.
506
+
507
+ #### Returns
508
+
509
+ `Promise`\<[`TMissionClaimRewardResult`](../interfaces/TMissionClaimRewardResult.md)\>
510
+
511
+ `{ err_code, err_message }`; success when `err_code === 0`
512
+ (or `40017` when treated as idempotent no-op).
513
+
514
+ #### Remarks
515
+
516
+ **Preconditions**
517
+ Only call when, on the mission object from `getMissions()`:
518
+ - `is_completed === true`
519
+ - `requires_prize_claim === true`
520
+ - `prize_claimed_date_ts == null` (i.e. not already claimed)
521
+ - `prize_claim_expiration_date` is `null` OR still in the future
522
+
523
+ The `ach_completed_id` argument must be passed straight from the same
524
+ mission object (`mission.ach_completed_id`). It is a server-side primary
525
+ key identifying the specific completion row; the SDK consumer should
526
+ never synthesise or cache it across sessions — always re-read it from a
527
+ fresh `getMissions()` result.
528
+
529
+ **Error codes** (in `err_code`)
530
+ - `0` — success; the prize was claimed and rewards have been credited.
531
+ - `1` — generic server error. Covers several distinct underlying
532
+ conditions, all collapsed into the same code: stale or wrong
533
+ `ach_completed_id` (does not match user / mission), the mission no
534
+ longer requiring a prize claim, the mission being archived/draft,
535
+ label mismatch, or the completion being too old (server enforces a
536
+ freshness window of several months). Recovery: re-fetch missions
537
+ with `getMissions()`; if the mission still appears claimable,
538
+ surface a generic error.
539
+ - `40015` — claim window expired (`prize_claim_expiration_date` has
540
+ passed). Show a "claim period ended" message; do not retry.
541
+ - `40016` — mission is not completed yet. Indicates a race or a stale
542
+ local cache. Re-fetch via `getMissions()`; if `is_completed` is still
543
+ false, the UI should hide the claim button.
544
+ - `40017` — prize already claimed. Treat as idempotent success: refresh
545
+ the mission list (auto-refresh will fire) and hide the claim button.
546
+ Usually means another tab/session already claimed it.
547
+ - other non-zero — generic server error. Surface `err_message` if any.
548
+
549
+ **Recurring missions**
550
+ The server transparently handles both one-shot and recurring mission
551
+ completions. The SDK consumer passes a single `ach_completed_id`
552
+ regardless of mission type; the server resolves the correct completion.
553
+
554
+ **Refresh after success**
555
+ On any claim response (success OR failure), the SDK automatically
556
+ re-fetches the mission list and fires any `onUpdate` callback
557
+ registered via `getMissions({ onUpdate })`. No manual re-fetch needed.
558
+ Note: points balance, badges, and level state arrive through their
559
+ own push channels — subscribe to them separately if your UI shows
560
+ those values.
561
+
562
+ **Idempotency**: not idempotent at the row level. A second call on the
563
+ same `ach_completed_id` returns `40017` (already claimed). For a stale
564
+ `ach_completed_id` the second call returns `1`. Guard the call site
565
+ against double-clicks; the SDK does not enforce its own in-flight lock.
566
+
567
+ **Side effects** (consumer-observable effects of a successful claim):
568
+ - Points credited to the user's balance.
569
+ - CRM-rule activities executed (may grant bonuses, badges; may unlock
570
+ follow-up missions, which arrive as a later mission auto-refresh).
571
+ - Analytics / automation events fired downstream; not directly observable
572
+ in the response, but visible via the mission auto-refresh and any
573
+ subsequent bonus / badge updates.
574
+
575
+ **UI guidance**: see [UI Guide — `requestMissionClaimReward`](../../ui/missions/UIGuide_requestMissionClaimReward.md).
576
+
577
+ #### Example
578
+
579
+ ```ts
580
+ const missions = await window._smartico.api.getMissions({
581
+ onUpdate: (m) => console.log('[smartico] mission list updated — re-render mission UI from this new array', m),
582
+ });
583
+ const mission = missions.find(m => m.ach_id === missionId);
584
+
585
+ if (
586
+ !mission ||
587
+ !mission.is_completed ||
588
+ !mission.requires_prize_claim ||
589
+ mission.prize_claimed_date_ts ||
590
+ (mission.prize_claim_expiration_date && Date.now() >= mission.prize_claim_expiration_date)
591
+ ) {
592
+ console.log('[smartico] claim not applicable — keep claim button hidden');
593
+ return;
594
+ }
595
+
596
+ console.log('[smartico] claim starting — show loading spinner on claim button and disable it to prevent double-clicks');
597
+ const r = await window._smartico.api.requestMissionClaimReward(mission.ach_id, mission.ach_completed_id);
598
+ console.log('[smartico] claim response received — clear loading spinner');
599
+
600
+ if (r.err_code === 0 || r.err_code === 40017) {
601
+ console.log('[smartico] claim succeeded (or was already claimed) — points/bonuses are credited; mission list auto-refresh will fire via the onUpdate above; hide the claim button');
602
+ } else if (r.err_code === 40015) {
603
+ console.error('[smartico] claim window expired — show a "claim period ended" message and remove the claim button:', mission.prize_claim_expiration_date);
604
+ } else if (r.err_code === 40016) {
605
+ console.error('[smartico] mission reported as not completed by server — local state was stale; auto-refresh will reconcile, do not retry');
606
+ } else {
607
+ console.error('[smartico] claim failed — show an error toast with this message to the user (and re-fetch missions to recover from a stale ach_completed_id):', r.err_message);
608
+ }
609
+ ```
610
+
611
+ ***