@indiegems/gem-web-sdk 14.0.0 → 14.0.2

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 (44) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +2 -2
  3. package/brand/ticket-hero-128.png +0 -0
  4. package/brand/ticket-hero-256.png +0 -0
  5. package/brand/ticket-hero-384.png +0 -0
  6. package/brand/ticket-hero-512.png +0 -0
  7. package/brand/ticket-hero-768.png +0 -0
  8. package/brand/ticket-icon-20-1.5x.png +0 -0
  9. package/brand/ticket-icon-20-1x.png +0 -0
  10. package/brand/ticket-icon-20-2x.png +0 -0
  11. package/brand/ticket-icon-20-3x.png +0 -0
  12. package/brand/ticket-icon-32-1.5x.png +0 -0
  13. package/brand/ticket-icon-32-1x.png +0 -0
  14. package/brand/ticket-icon-32-2x.png +0 -0
  15. package/brand/ticket-icon-32-3x.png +0 -0
  16. package/dist/gem-web-sdk.esm.js +1 -1
  17. package/dist/gem-web-sdk.function.js +1 -1
  18. package/dist/gem-web-sdk.game.js +2 -2
  19. package/dist/gem-web-sdk.min.js +2 -2
  20. package/dist/gem-web-sdk.sdk.js +2 -2
  21. package/dist/gem-web-sdk.server.js +1 -1
  22. package/docs/achievements.md +2 -2
  23. package/docs/api/api-reference-function.md +3 -3
  24. package/docs/api/api-reference-game.md +65 -32
  25. package/docs/api/api-reference-server.md +1 -1
  26. package/docs/api/reference.json +21 -21
  27. package/docs/arcade-avatars.md +3 -3
  28. package/docs/arcade-economy.md +37 -36
  29. package/docs/brand-art.md +31 -30
  30. package/docs/changelog.md +12 -0
  31. package/docs/daily-challenges.md +1 -1
  32. package/docs/game-economy-tutorial.md +14 -14
  33. package/docs/getting-started.md +3 -3
  34. package/docs/inventory-and-catalogs.md +29 -29
  35. package/docs/manifest.json +32 -32
  36. package/docs/platform-rules.md +2 -2
  37. package/docs/rewarded-ads.md +1 -1
  38. package/docs/saving-player-progress.md +1 -1
  39. package/docs/server-bundles.md +2 -2
  40. package/docs/serverless-functions.md +2 -2
  41. package/docs/trading-and-gifting.md +1 -1
  42. package/docs/what-a-game-must-do.md +57 -36
  43. package/package.json +1 -1
  44. package/schema/bridge.v1.json +7 -7
package/docs/changelog.md CHANGED
@@ -16,6 +16,18 @@ Its entries are grouped under up to five headings:
16
16
  - **Rebuild**: a type changed. Recompile against the new version; code that compiles today keeps compiling unless the entry says otherwise.
17
17
  - **New capability**: something a game can now do. Nothing changes unless you use it.
18
18
 
19
+ ## 14.0.2
20
+
21
+ Upgrading from 14.0.1. Impact: none.
22
+
23
+ Nothing in this release asks anything of a game.
24
+
25
+ ## 14.0.1
26
+
27
+ Upgrading from 14.0.0. Impact: none.
28
+
29
+ Nothing in this release asks anything of a game.
30
+
19
31
  ## 14.0.0
20
32
 
21
33
  Upgrading from 13.4.3. Impact: required.
@@ -321,7 +321,7 @@ Four things to know before you ship one:
321
321
  the player does to their time zone.
322
322
 
323
323
  **When a completion pays a platform currency, show it where it happened.** The arcade shows the
324
- player's Gems and Tickets in its own chrome, and without a cue a payout flies from the middle of
324
+ player's Gems and Coins in its own chrome, and without a cue a payout flies from the middle of
325
325
  your frame, not from the payout the player is looking at. At the moment your game shows the challenge complete, call
326
326
  `gem.cueReward` with the currency that paid (`GEMS` or `ARCADE_TICKETS`) and the centre of its icon
327
327
  beside the payout on your completion screen, and the payout flies from there into the Gem button:
@@ -35,8 +35,8 @@ document, because the schema is versioned and a copied list goes stale.
35
35
 
36
36
  ```yaml
37
37
  currencies:
38
- - id: coins
39
- display_name: Coins
38
+ - id: gold
39
+ display_name: Gold
40
40
  min_balance: 0
41
41
  max_balance: 999999
42
42
 
@@ -51,7 +51,7 @@ catalogs:
51
51
  - id: buy-potion
52
52
  recipe_type: purchase
53
53
  costs:
54
- - currency_id: coins
54
+ - currency_id: gold
55
55
  amount: 25
56
56
  grants:
57
57
  - item_definition_id: healing-potion
@@ -106,12 +106,12 @@ them.
106
106
 
107
107
  ```js
108
108
  const store = config.catalogs.find((c) => c.id === 'general-store');
109
- const balance = (await gem.me.currencies.get('coins')).total; // from the snapshot, not a fourth request
109
+ const balance = (await gem.me.currencies.get('gold')).total; // from the snapshot, not a fourth request
110
110
  // `null` means no record — decide at the render what that shows, never `?? 0` at the read.
111
111
 
112
112
  for (const recipe of store.recipes) {
113
113
  const cost =
114
- recipe.costs?.find((c) => 'currency_id' in c && c.currency_id === 'coins')?.amount ?? 0;
114
+ recipe.costs?.find((c) => 'currency_id' in c && c.currency_id === 'gold')?.amount ?? 0;
115
115
  const affordable = balance !== null && balance >= cost; // null is "no record", and null >= 0 is true in JS
116
116
  const used = usage.usages.find((u) => u.recipeId === recipe.id); // camelCase: SDK shape
117
117
  const onCooldown = used?.isOnCooldown ?? false;
@@ -205,10 +205,10 @@ const order = await gem.me.orders.get(result.orderId);
205
205
  This is also the answer after a timeout: you got no result back, so list recent orders and look for
206
206
  the one you attempted, rather than resending the purchase.
207
207
 
208
- ## Selling a cosmetic for Tickets
208
+ ## Selling a cosmetic for Coins
209
209
 
210
210
  Your shop can also sell for the arcade's own currencies, and the platform draws one line through
211
- them: **Tickets buy cosmetics, Gems buy anything.** Each platform currency has a spend scope — Tickets'
211
+ them: **Coins buy cosmetics, Gems buy anything.** Each platform currency has a spend scope — Coins'
212
212
  is `cosmetic_only`, Gems' is `any`. Which currencies your game may price in, and what each may buy,
213
213
  is `gemctl platform config`'s answer. Mark a cosmetic in the config, and price it in
214
214
  `ARCADE_TICKETS`:
@@ -219,7 +219,7 @@ items:
219
219
  display_name: Wizard Hat
220
220
  category: cosmetic # it changes how the player looks, never what they can do
221
221
  platform_prices:
222
- - currency: ARCADE_TICKETS # Tickets
222
+ - currency: ARCADE_TICKETS # Coins
223
223
  price: 40
224
224
  - id: dragon-skin
225
225
  display_name: Dragon Skin
@@ -265,7 +265,7 @@ if (av.locked) {
265
265
  }
266
266
  ```
267
267
 
268
- The platform checks the prerequisite **before** it takes any Tickets: a locked player who buys
268
+ The platform checks the prerequisite **before** it takes any Coins: a locked player who buys
269
269
  anyway is refused `recipe_locked` and charged nothing. See
270
270
  [`inventory-and-catalogs.md`](inventory-and-catalogs.md#16-selling-for-a-platform-currency) for the
271
271
  two purchase paths and the full rule.
@@ -285,7 +285,7 @@ const res = await gem.purchases.request({
285
285
  if (res.outcome === 'ok' || res.outcome === 'unavailable') {
286
286
  gem.me.inventory.refresh(); // `unavailable` means no answer arrived, not that nothing happened
287
287
  } else if (res.reason === 'recipe_locked' || res.reason === 'insufficient_funds') {
288
- updateShopRow(res.reason); // your shop's own words: "Locked", "Not enough Tickets"
288
+ updateShopRow(res.reason); // your shop's own words: "Locked", "Not enough Coins"
289
289
  } else if (res.reason !== 'cancelled' && res.reason !== 'busy') {
290
290
  console.warn('purchase declined:', res.reason); // unknown_currency, no_price_in_currency … are yours to fix
291
291
  }
@@ -294,13 +294,13 @@ if (res.outcome === 'ok' || res.outcome === 'unavailable') {
294
294
  With your own purchase screen (`gem.me.mutations.purchase`), the same strings arrive as `err.code`
295
295
  — see [`inventory-and-catalogs.md`](inventory-and-catalogs.md#why-a-purchase-was-refused).
296
296
 
297
- **Never draw a top-up or payment screen of your own** — no "get more Tickets", no checkout, nothing
298
- shaped like the arcade's purchase sheet. A player short of Tickets earns more by playing; your shop
299
- says "Not enough Tickets" and stops there.
297
+ **Never draw a top-up or payment screen of your own** — no "get more Coins", no checkout, nothing
298
+ shaped like the arcade's purchase sheet. A player short of Coins earns more by playing; your shop
299
+ says "Not enough Coins" and stops there.
300
300
 
301
301
  ## Rewarding a player — still a recipe
302
302
 
303
- When your game _gives_ a player something — "you earned 100 coins," a quest reward — reach for a
303
+ When your game _gives_ a player something — "you earned 100 gold," a quest reward — reach for a
304
304
  recipe too, not a direct grant. A reward flow is the same client-authored economy as a shop under a
305
305
  friendlier name, and the same rule applies: if the value matters, the server has to be the one that
306
306
  awards it. Author a reward recipe in your config and execute it exactly as above.
@@ -50,7 +50,7 @@ either way.
50
50
  ```jsonc
51
51
  // package.json
52
52
  "dependencies": {
53
- "@indiegems/gem-web-sdk": "14.0.0"
53
+ "@indiegems/gem-web-sdk": "14.0.2"
54
54
  }
55
55
  ```
56
56
 
@@ -178,8 +178,8 @@ And draw a settings button of your own — top-right, with your Back button just
178
178
  ([the top of the screen](what-a-game-must-do.md#the-top-of-the-screen)) — whose values start from
179
179
  the player's Arcade-wide defaults, `gem.prefs.recommended`, until the player chooses their own
180
180
  ([what a game must do](what-a-game-must-do.md#game-settings)). The Arcade's menu does not open
181
- your settings screen; you may also mirror a few of your settings into it with
182
- `gem.settings.declare`, but never instead of your own button. Outside the Arcade there is no Arcade menu at all, so also give players a way out — a
181
+ your settings screen; declare your settings to it with `gem.settings.declare` too, so its
182
+ Settings screen lists them all, but never instead of your own button. Outside the Arcade there is no Arcade menu at all, so also give players a way out — a
183
183
  control that calls `gem.exit()`. A game a player cannot leave is one they leave by closing the tab.
184
184
 
185
185
  ## 6. Play it
@@ -1,7 +1,7 @@
1
1
  # Inventory and catalogs
2
2
 
3
3
  Games do not have to have an economy. A game with no currency, no items and nothing to buy is a
4
- complete game. Read this if yours has any of them — a coin count, a bag of items, a shop, a loot
4
+ complete game. Read this if yours has any of them — a gold count, a bag of items, a shop, a loot
5
5
  box, a battle pass.
6
6
 
7
7
  If it does, the platform holds it, the same way it holds a save. Balances and items live on the
@@ -42,8 +42,8 @@ which is the same rule with a save slot in place of a currency.
42
42
 
43
43
  Hold the distinction, because the whole surface is built on it.
44
44
 
45
- - A **currency definition** is "coins exist, minimum 0, maximum a million." A player's **balance**
46
- is an instance: "this player has 250 coins."
45
+ - A **currency definition** is "gold exists, minimum 0, maximum a million." A player's **balance**
46
+ is an instance: "this player has 250 gold."
47
47
  - An **item definition** is "a healing potion, stackable to 99." An **item instance** is "this
48
48
  player holds 3 of them," or, for a non-stacking item, "this specific sword, rolled with these
49
49
  properties."
@@ -60,7 +60,7 @@ because it never loaded the definitions that name them.
60
60
  Everything a player owns is reached through `gem.me`, the signed-in player:
61
61
 
62
62
  ```js
63
- await gem.me.currencies.get('coins');
63
+ await gem.me.currencies.get('gold');
64
64
  await gem.me.inventory.snapshot();
65
65
  ```
66
66
 
@@ -69,7 +69,7 @@ scoped to that character:
69
69
 
70
70
  ```js
71
71
  const hero = await gem.characters.get(characterId);
72
- await hero.currencies.get('coins'); // the hero's coins, not the account's
72
+ await hero.currencies.get('gold'); // the hero's gold, not the account's
73
73
  ```
74
74
 
75
75
  Code written against `gem.me` works unchanged against a character, because it is one interface on
@@ -85,7 +85,7 @@ lookups are memory:
85
85
  ```js
86
86
  const config = await gem.gameConfig.load(); // a round trip; do it once
87
87
  const potion = config.item('healing-potion'); // synchronous, from memory, by id or slug
88
- const coin = config.currency('coins'); // synchronous
88
+ const gold = config.currency('gold'); // synchronous
89
89
  ```
90
90
 
91
91
  Two things that will bite:
@@ -162,8 +162,8 @@ A balance read is a lookup, not a list to iterate, because **a currency the play
162
162
  is absent, not present as zero**:
163
163
 
164
164
  ```js
165
- const coins = await gem.me.currencies.get('coins');
166
- coins.total; // a number, or null when the player has no record of this currency
165
+ const gold = await gem.me.currencies.get('gold');
166
+ gold.total; // a number, or null when the player has no record of this currency
167
167
  ```
168
168
 
169
169
  **`null` is not `0`.** Reach for `total ?? 0` and you have decided that "has never held any" and
@@ -175,7 +175,7 @@ is visible.
175
175
  real-money pool. They are off by default and asked for per read:
176
176
 
177
177
  ```js
178
- const c = await gem.me.currencies.get('coins', { buckets: true });
178
+ const c = await gem.me.currencies.get('gold', { buckets: true });
179
179
  c.buckets; // [{ bucket: 'earned', balance: 200 }, { bucket: 'purchased', balance: 50 }], or absent
180
180
  ```
181
181
 
@@ -409,7 +409,7 @@ await gem.me.mutations.claimProgression('season-xp'); // collect what milestones
409
409
  await gem.me.mutations.resetProgression('season-xp'); // back to the start; claimed rewards stay
410
410
  ```
411
411
 
412
- `addProgression` is for things that accumulate — experience, kills, coins collected across a
412
+ `addProgression` is for things that accumulate — experience, kills, gold collected across a
413
413
  season. `maxProgression` is for things that are a _best_ — stars on a level, a high score, a
414
414
  furthest checkpoint. It raises the value if the new one is higher and does nothing otherwise, so
415
415
  replaying a level and doing worse leaves the record alone, and replaying it and doing the same
@@ -439,7 +439,7 @@ so this page does not list them. Name one that is not permitted and the config u
439
439
  with an error that enumerates the ones that are.
440
440
 
441
441
  Gems can be your game's soft currency, awarded through a mapping. A game currency of your own suits
442
- what lives inside one play loop — coins picked up and spent within a run, energy, crafting
442
+ what lives inside one play loop — gold picked up and spent within a run, energy, crafting
443
443
  materials: you control it completely, no other game can spend it, and none of the constraints below
444
444
  apply to it.
445
445
 
@@ -551,7 +551,7 @@ nobody else can touch them. The arcade also has currencies that span every game,
551
551
  That changes what a shop can be. A player earns in someone else's game and spends in yours.
552
552
 
553
553
  There are two such currencies, and they are not interchangeable: **Gems** (`GEMS`) buy progress and
554
- **Tickets** (`ARCADE_TICKETS`) buy cosmetics only. [arcade-economy.md](arcade-economy.md) says which
554
+ **Coins** (`ARCADE_TICKETS`) buy cosmetics only. [arcade-economy.md](arcade-economy.md) says which
555
555
  to use for what.
556
556
 
557
557
  ### Awarding one
@@ -583,13 +583,13 @@ A browser-only game cannot award a platform currency without a server **or a ser
583
583
  That is the price of the platform never trusting a client. A game with no dedicated server writes
584
584
  the source progression from a function the player calls, on a progression that opts in with
585
585
  `function_may_grant`, and the platform then pays that player only, in any listed platform currency
586
- — Gems (`GEMS`) and Tickets (`ARCADE_TICKETS`) alike
586
+ — Gems (`GEMS`) and Coins (`ARCADE_TICKETS`) alike
587
587
  ([serverless-functions.md](serverless-functions.md#paying-the-player-who-called)).
588
588
 
589
589
  ### Showing the reward land in the arcade's wallet
590
590
 
591
591
  **Platform-currency display belongs to the arcade** (the creator's ruling, 2026-09-24) — Gems and
592
- Tickets alike. The arcade shows the player's platform currencies in its own chrome — in the Gem
592
+ Coins alike. The arcade shows the player's platform currencies in its own chrome — in the Gem
593
593
  Arcade, the Gem menu the player opens from the Gem button over your frame; no wallet
594
594
  is drawn over your game — and moves a total when the platform confirms a grant. Your game keeps no counter or balance
595
595
  of a platform currency of its own, draws no flight or celebration of its own, and shows no
@@ -609,8 +609,8 @@ gem.cueReward({
609
609
  ```
610
610
 
611
611
  `origin` is the **centre of the gem on screen** — measure the gem element itself, not the row or
612
- panel around it. A Tickets payout is the same call with `currencySlug: 'ARCADE_TICKETS'`, from the
613
- ticket: the slug decides what flies and which total moves. **A payout you do not cue still flies**
612
+ panel around it. A Coins payout is the same call with `currencySlug: 'ARCADE_TICKETS'`, from the
613
+ coin: the slug decides what flies and which total moves. **A payout you do not cue still flies**
614
614
  in the Gem Arcade, from the middle of your frame, once the platform confirms it — cue it so the
615
615
  flight starts where the player is looking. **If `cueReward` returns `false`, do nothing**: there is
616
616
  no fallback celebration.
@@ -829,14 +829,14 @@ that looks like the arcade's purchase sheet or wallet. A game screen shaped like
829
829
  chrome is an imitation of it. Your Buy button is yours; asking a player to add currency or pay is
830
830
  the arcade's alone.
831
831
 
832
- ### Tickets buy cosmetics; Gems buy anything
832
+ ### Coins buy cosmetics; Gems buy anything
833
833
 
834
- The arcade has two currencies a player spends: **Gems**, for progress, and **Tickets**
834
+ The arcade has two currencies a player spends: **Gems**, for progress, and **Coins**
835
835
  (`ARCADE_TICKETS`), for cosmetics. The platform holds that line with two settings: each platform
836
836
  currency's **spend scope**, which the platform sets, and each of your items' **category**, which you
837
837
  set.
838
838
 
839
- - A currency whose spend scope is **`cosmetic_only`** may price cosmetics only. Tickets' is.
839
+ - A currency whose spend scope is **`cosmetic_only`** may price cosmetics only. Coins' is.
840
840
  - A currency whose spend scope is **`any`** may price anything. Gems' is.
841
841
 
842
842
  The platform owns each currency's scope and can change it, so do not hard-code which is which: a
@@ -862,9 +862,9 @@ items:
862
862
  cosmetics: anything that changes how the player looks and never what they can do — or
863
863
  `progression`, anything that affects play. It defaults to `progression`, so an item is only ever a
864
864
  cosmetic because you said so.
865
- - **A price in a `cosmetic_only` currency, such as Tickets, is accepted only on a cosmetic.** On an
865
+ - **A price in a `cosmetic_only` currency, such as Coins, is accepted only on a cosmetic.** On an
866
866
  item, that item must be `category: cosmetic` and a plain `item_type: item` — a `bundle` or
867
- `loot_box` opens into things its own row does not describe, so it cannot be sold for Tickets.
867
+ `loot_box` opens into things its own row does not describe, so it cannot be sold for Coins.
868
868
  - **A recipe priced in a `cosmetic_only` currency must grant at least one cosmetic item and nothing
869
869
  else.** It is refused if it grants any currency, moves any progression, grants a non-cosmetic item,
870
870
  or rolls a loot table — a loot table is refused for now even when every entry is cosmetic. Taking
@@ -884,11 +884,11 @@ purchase catalog stops listing that currency's price on anything the scope does
884
884
  purchase naming such a price is refused `not_purchasable` before anything is charged.
885
885
 
886
886
  The category is a declaration the platform takes at your word, and the release review checks it.
887
- Labelling something that affects play `cosmetic` so it can be sold for Tickets breaks platform policy.
887
+ Labelling something that affects play `cosmetic` so it can be sold for Coins breaks platform policy.
888
888
 
889
889
  **To sell a cosmetic that needs game progress first, sell it through a recipe.** An item's own
890
890
  `platform_prices` cannot carry prerequisites; a recipe's can. Grant the cosmetic from a recipe that
891
- declares the prerequisite and carries the Tickets price:
891
+ declares the prerequisite and carries the Coins price:
892
892
 
893
893
  ```yaml
894
894
  catalogs:
@@ -909,7 +909,7 @@ catalogs:
909
909
  price: 120
910
910
  ```
911
911
 
912
- A player below level 10 is refused `recipe_locked` before any Tickets are taken, and
912
+ A player below level 10 is refused `recipe_locked` before any Coins are taken, and
913
913
  `recipeAvailability` tells your shop the recipe is locked and what it needs (§9) — so show the lock
914
914
  ("Reach level 10 to unlock") instead of offering the purchase.
915
915
 
@@ -922,7 +922,7 @@ buyButton.disabled = !gems || gems.balance < price; // can the player afford it?
922
922
  ```
923
923
 
924
924
  **Read it to decide, never to display.** Platform-currency display belongs to the arcade: its
925
- wallet — the Gem menu, in the Gem Arcade — shows the player's Gems and Tickets totals while your
925
+ wallet — the Gem menu, in the Gem Arcade — shows the player's Gems and Coins totals while your
926
926
  game runs, and your game keeps no
927
927
  counter, balance or wallet total of either of its own ([brand-art.md](brand-art.md) §5). Use the
928
928
  balance to enable a purchase or to say "not enough Gems" — not to draw a number.
@@ -952,7 +952,7 @@ if (gems && gems.remaining > 0) {
952
952
  }
953
953
  ```
954
954
 
955
- One entry per earned platform currency (Gems and Tickets today), for **this game and this player**:
955
+ One entry per earned platform currency (Gems and Coins today), for **this game and this player**:
956
956
  how much more your game can still grant before the platform's ceilings trim the grant. It is the
957
957
  lower of what is left of your game's own cap and what is left of the player's daily maximum across
958
958
  all games, with the player's Boost applied — one figure, never a breakdown.
@@ -971,8 +971,8 @@ all games, with the player's Boost applied — one figure, never a breakdown.
971
971
  **Only `remaining` and `resetsAt` are stable to display.** Do not build an `earned / limit` progress
972
972
  bar: the bar switches referent when the binding ceiling changes.
973
973
 
974
- A player whose Tickets are spent for today reads `remaining: 0` with a `resetsAt`: say when more
975
- arrives (from `resetsAt`), not "no Tickets". When the platform has paused a currency's grants for
974
+ A player whose Coins are spent for today reads `remaining: 0` with a `resetsAt`: say when more
975
+ arrives (from `resetsAt`), not "no Coins". When the platform has paused a currency's grants for
976
976
  your game the entry reads `limit: 0, remaining: 0` with **no** `resetsAt`.
977
977
 
978
978
  **For a one-day allowance, `resetsAt` is the player's own next midnight — do not assume 00:00 UTC.**
@@ -1054,7 +1054,7 @@ grants itself.
1054
1054
 
1055
1055
  ## What to read next
1056
1056
 
1057
- - [`arcade-economy.md`](arcade-economy.md) — Gems, Tickets and achievement points: which to use for
1057
+ - [`arcade-economy.md`](arcade-economy.md) — Gems, Coins and achievement points: which to use for
1058
1058
  what, and the rules each carries.
1059
1059
  - [`achievements.md`](achievements.md) — platform achievements on your progressions' thresholds.
1060
1060
  - [`game-economy-tutorial.md`](game-economy-tutorial.md) — build a shop, step by step, the safe way.
@@ -8,8 +8,8 @@
8
8
  "documents": [
9
9
  {
10
10
  "file": "achievements.md",
11
- "sha256": "adc8bfaa769fbb283a53de1190ed85cdf4c1c9753e8cf78335b4595e98f8fad4",
12
- "updated": "2026-10-06T22:14:30Z"
11
+ "sha256": "c1ba25c834f5679dc5ce1c7c3b51d4219b5ac77fc898bf5d6fbf23e35614e35c",
12
+ "updated": "2026-10-21T19:36:16Z"
13
13
  },
14
14
  {
15
15
  "file": "analytics.md",
@@ -18,13 +18,13 @@
18
18
  },
19
19
  {
20
20
  "file": "arcade-avatars.md",
21
- "sha256": "7269a0dbd515e2c52e63f9fad2b3028e136396522aa68aa177c44f7eceb2c0e0",
22
- "updated": "2026-10-14T19:48:50Z"
21
+ "sha256": "e02e435d91e389f9cc2025945ebc0ac11882bac85a4e6d5d74278f11e38fee91",
22
+ "updated": "2026-10-21T19:36:16Z"
23
23
  },
24
24
  {
25
25
  "file": "arcade-economy.md",
26
- "sha256": "338dcdfe4aaa90241f7132677a0135d0ec8e32336f4302413dc0f11391b37623",
27
- "updated": "2026-10-15T23:20:43Z"
26
+ "sha256": "2a73e2cb4e82de78a9d57c614da67bfd6a79537163deca551c5beeb8e9693c56",
27
+ "updated": "2026-10-21T19:36:16Z"
28
28
  },
29
29
  {
30
30
  "file": "async-opponents.md",
@@ -33,18 +33,18 @@
33
33
  },
34
34
  {
35
35
  "file": "brand-art.md",
36
- "sha256": "44ba566da97e499f80b6c4898a5546bf7fb3dfba2290da9aa3e8d4e6056a7cce",
37
- "updated": "2026-10-14T21:18:16Z"
36
+ "sha256": "151ece62736136e0acd5c4e3e2dd7e33eb6936923c89340670844452d34acf02",
37
+ "updated": "2026-10-21T19:36:16Z"
38
38
  },
39
39
  {
40
40
  "file": "changelog.md",
41
- "sha256": "3ccaf70d80a6164736920b4df6f817d600b2a80287d4a14783f72fbb470863f6",
42
- "updated": "2026-10-21T13:51:48Z"
41
+ "sha256": "22d1e4aba2cdd10d9ff33e1d5fded140194a96fabe24f95aa673e9af1f07e72c",
42
+ "updated": "2026-10-21T19:49:02Z"
43
43
  },
44
44
  {
45
45
  "file": "daily-challenges.md",
46
- "sha256": "0423168355c22724e705def15ca19a5651b7f0da3598ec5aabcc69c32e0bb401",
47
- "updated": "2026-10-11T18:48:12Z"
46
+ "sha256": "fd1c44a923b8f7b82a52b958481e82ba5d62dc27de6a196a0776b0d34563ec54",
47
+ "updated": "2026-10-21T19:36:16Z"
48
48
  },
49
49
  {
50
50
  "file": "dedicated-server-flow.md",
@@ -63,18 +63,18 @@
63
63
  },
64
64
  {
65
65
  "file": "game-economy-tutorial.md",
66
- "sha256": "08588571cd6529d81bd064f65bc3158997418aeb88ce00ae281735c8b44bff1f",
67
- "updated": "2026-10-02T19:05:02Z"
66
+ "sha256": "5f921001150ea47a92d97e943282988826ea27506107c71846e5ff79f0d26f15",
67
+ "updated": "2026-10-21T19:36:16Z"
68
68
  },
69
69
  {
70
70
  "file": "getting-started.md",
71
- "sha256": "dbda9199712f4112bfe316b7af6e63a11fcce6842e778672459e1532947cf21f",
72
- "updated": "2026-10-21T13:51:48Z"
71
+ "sha256": "f70765a705776bea4f11c8067669204db31e992515d15a76c465785e71bc6abe",
72
+ "updated": "2026-10-21T19:49:02Z"
73
73
  },
74
74
  {
75
75
  "file": "inventory-and-catalogs.md",
76
- "sha256": "fc2dbf113d2602fff90d03fc24e03d468c7b4ab3633844ed9d0df6b985e69385",
77
- "updated": "2026-10-18T00:39:23Z"
76
+ "sha256": "e3968af5f3af88122db84ac372afd82e39c04c98cf6661f83e429243837ee6ff",
77
+ "updated": "2026-10-21T19:36:16Z"
78
78
  },
79
79
  {
80
80
  "file": "leaderboards.md",
@@ -98,8 +98,8 @@
98
98
  },
99
99
  {
100
100
  "file": "platform-rules.md",
101
- "sha256": "b259bcebc6ae12b1db9c7279a6e2adb174af9d60090bf31001e23616f6423fbf",
102
- "updated": "2026-10-10T00:12:33Z"
101
+ "sha256": "ab5b971555490956ef50f6141b0f58a6a34335a7e4cc1ef0e4d339a259c9a062",
102
+ "updated": "2026-10-21T19:36:16Z"
103
103
  },
104
104
  {
105
105
  "file": "player-sign-in.md",
@@ -108,8 +108,8 @@
108
108
  },
109
109
  {
110
110
  "file": "rewarded-ads.md",
111
- "sha256": "a111ae68fb0d443f8c183c7a963668078a6d155124cf19da45391218c0716e1f",
112
- "updated": "2026-10-17T17:23:21Z"
111
+ "sha256": "b413abb899016c9a6d0c288c39d804bdabcebceec1081bdcfbb1f3feb0073406",
112
+ "updated": "2026-10-21T19:36:16Z"
113
113
  },
114
114
  {
115
115
  "file": "room-shapes.md",
@@ -123,13 +123,13 @@
123
123
  },
124
124
  {
125
125
  "file": "saving-player-progress.md",
126
- "sha256": "11ab0684247cfc43d6d8faba6dbb897192354af5f5622b16fd96abcb156ca0cd",
127
- "updated": "2026-10-12T21:21:05Z"
126
+ "sha256": "2b51c68651b8973531fb1fda4afc4442e1450443871800ca71126605bf4b39db",
127
+ "updated": "2026-10-21T19:36:16Z"
128
128
  },
129
129
  {
130
130
  "file": "server-bundles.md",
131
- "sha256": "2312a25bb11ce29f50b97b0737c708b31f6e0290f99fe2520179a9dbac67fb8c",
132
- "updated": "2026-10-21T13:51:48Z"
131
+ "sha256": "95d84093c49693576297f2a6917064a1efbb6824c82acfc9f210d64a3b437d2f",
132
+ "updated": "2026-10-21T19:49:02Z"
133
133
  },
134
134
  {
135
135
  "file": "serverless-functions-js.md",
@@ -138,13 +138,13 @@
138
138
  },
139
139
  {
140
140
  "file": "serverless-functions.md",
141
- "sha256": "eef5fa14dced0bca39b4f4b9d6688d4f81d6c4d2441ec3497380c21b7482022a",
142
- "updated": "2026-10-15T14:37:57Z"
141
+ "sha256": "9a33e93c90c963fd82e4848d403f27134ec15636428f6cf896488089fec3ede1",
142
+ "updated": "2026-10-21T19:36:16Z"
143
143
  },
144
144
  {
145
145
  "file": "trading-and-gifting.md",
146
- "sha256": "d7ee3c85540ab8aae29988c16a76bee3ba03cc66b10d2faaf5cd7210a80a0544",
147
- "updated": "2026-08-26T03:27:49Z"
146
+ "sha256": "e5a77bcae4243fbf69a34fa6440114677401255b40ee9825c15b87c6570e5030",
147
+ "updated": "2026-10-21T19:36:16Z"
148
148
  },
149
149
  {
150
150
  "file": "vibration.md",
@@ -153,8 +153,8 @@
153
153
  },
154
154
  {
155
155
  "file": "what-a-game-must-do.md",
156
- "sha256": "3a9c0e307b78cfb8b959c7b47a1ad0349699be944903a24834ee785c3823b402",
157
- "updated": "2026-10-21T13:31:36Z"
156
+ "sha256": "a0f1fd58acd28c72cca3574032c087ffbe2cb34e311086c99eceec7e2f9b7f20",
157
+ "updated": "2026-10-21T19:36:16Z"
158
158
  }
159
159
  ]
160
160
  }
@@ -329,13 +329,13 @@ longer one, are in [`server-bundles.md`](server-bundles.md#how-long-your-server-
329
329
  Decided by the platform on **2026-09-26**, and a contract rather than an implementation fact.
330
330
 
331
331
  The **Boost** is something the Arcade sells for real money, in its own store. While a player has
332
- one, they earn **Gems and Tickets** faster — every grant is multiplied — and their earning limits
332
+ one, they earn **Gems and Coins** faster — every grant is multiplied — and their earning limits
333
333
  and holdings cap are raised by the same tier. It **never** touches achievement points. The platform applies all of
334
334
  it on its own side, when it pays: a game does nothing to make a boost work, and cannot make one
335
335
  work harder.
336
336
 
337
337
  A game **may show** it. `gem.player.boost` carries each boostable currency's multiplier and the
338
- whole days left, so a reward screen can say "2× Tickets"; see
338
+ whole days left, so a reward screen can say "2× Coins"; see
339
339
  [what a game must do](what-a-game-must-do.md#the-signed-in-players-boost-show-it-never-gate-on-it).
340
340
  A dedicated server can read the same effective boost for a player in its match, and the same rule
341
341
  holds there.
@@ -39,7 +39,7 @@ config where it is not:
39
39
  - **Soulbound.** Every item or currency it grants has `transfer_policy: none`. A reward cannot be
40
40
  traded, gifted or moved.
41
41
  - **An ordinary item or a currency.** Not a bundle, not a loot box, and not a progression.
42
- - **Never a platform currency.** The upload refuses a reward that names Gems, Tickets or any
42
+ - **Never a platform currency.** The upload refuses a reward that names Gems, Coins or any
43
43
  other platform currency.
44
44
  - **Never convertible into one of those.** The upload follows every recipe and catalog entry that
45
45
  spends the reward, then everything that spends what those grant, and so on. If that chain reaches
@@ -474,7 +474,7 @@ The order matters, and two of the steps are the ones people skip:
474
474
  The reset clears this player's state **in this game, on that one channel**: saves,
475
475
  `gem.me.settings`, inventory and progressions, and the rest of what the game keeps for them.
476
476
  It does not touch your other channels, your other games, achievements, or your Gems and
477
- Tickets. Your purchases and what you paid stay on record, but an item a purchase delivered on
477
+ Coins. Your purchases and what you paid stay on record, but an item a purchase delivered on
478
478
  that channel is cleared with the rest, and the answer lists those purchases.
479
479
  `gemctl support reset --help` has the full list, and the answer names whatever it left alone.
480
480
 
@@ -80,8 +80,8 @@ To check the download yourself: the package npm serves is, byte for byte, the fi
80
80
  also publishes on its download host, with the hash beside it:
81
81
 
82
82
  ```bash
83
- curl -fsSL -O https://downloads.gemarcade.com/sdk/v14.0.0/gem-web-sdk.tgz
84
- curl -fsSL https://downloads.gemarcade.com/sdk/v14.0.0/gem-web-sdk.tgz.sri
83
+ curl -fsSL -O https://downloads.gemarcade.com/sdk/v14.0.2/gem-web-sdk.tgz
84
+ curl -fsSL https://downloads.gemarcade.com/sdk/v14.0.2/gem-web-sdk.tgz.sri
85
85
  openssl dgst -sha384 -binary gem-web-sdk.tgz | openssl base64 -A
86
86
  ```
87
87
 
@@ -18,7 +18,7 @@ change: checking that a finished run was plausible, or granting a reward whose c
18
18
  state only your code can judge.
19
19
 
20
20
  It is also how a game **with no dedicated server** pays a platform currency — Gems (`GEMS`) or
21
- Tickets (`ARCADE_TICKETS`) — or unlocks an achievement. A browser write
21
+ Coins (`ARCADE_TICKETS`) — or unlocks an achievement. A browser write
22
22
  never does either. A function a player calls may, for **that player only**, on a progression you
23
23
  opt in. Section 2's
24
24
  [Paying the player who called](#paying-the-player-who-called) is the whole of it. Opting in makes
@@ -160,7 +160,7 @@ fires them only when you opt in:
160
160
  request answers `200`, and the transfer stays `pending` for the recipient to accept or reject
161
161
  (the previous subsection).
162
162
  - **Any currency the platform lists.** A function may pay every currency `gemctl platform config`
163
- lists under `platform_grants` — Gems (`GEMS`) and Tickets (`ARCADE_TICKETS`) alike — so a daily
163
+ lists under `platform_grants` — Gems (`GEMS`) and Coins (`ARCADE_TICKETS`) alike — so a daily
164
164
  that pays Gems needs no dedicated server. (`function_gems_not_enabled` on an older receipt is from
165
165
  before functions could pay Gems.)
166
166
  - **No guests.** A guest account's call grants and unlocks nothing (`function_invoker_is_guest`).
@@ -13,7 +13,7 @@ as `gem.transfers` rather than through `gem.me`.
13
13
  await gem.transfers.initiate({
14
14
  to: { type: 'players', id: recipientId },
15
15
  items: [{ itemDefinitionId: 'iron-sword', quantity: 1 }],
16
- currencies: [{ currencyId: 'coins', amount: 50 }],
16
+ currencies: [{ currencyId: 'gold', amount: 50 }],
17
17
  message: 'for the boss fight',
18
18
  });
19
19
  ```