Sell something for LIX
Register a product on your world, ask the player to buy it, and read back what they own. The shell charges LIX; your world never handles money.
A world sells things through World Products: world-local currencies, passes and consumables that you register once and the player buys with LIX. The rule that shapes everything: your world names a product and reads entitlements back. The shell raises the confirmation, charges the wallet and settles the sale. A world cannot set a price or write an entitlement.
Selling a portable item is different
A World Product never mints a universal item. To sell something the player keeps across worlds,
publish it as an item and open an offer for it with
helix continuum publish-glb, then buy it from world code with
Helix.marketplace.purchaseDistributionKey, purchaseDistribution or purchaseListing.
1. Register the product
Register before you write the code that buys it. Your world must already exist and be published
(helix publish). The key is permanent, and so is the grants list, so choose both once.
helix product register my-world \
--key coin-pack \
--title "Coin Pack" \
--price 25 \
--grants '[{"kind":"currency","code":"coin","amount":500,"display":"wallet"}]' \
--dry-run--dry-run validates everything locally, works while logged out, and prints exactly what would be
sent:
✔ Dry run — nothing sent.
POST /api/v1/iwp/products (worldId of "my-world")
body: { "key": "coin-pack", "title": "Coin Pack", "priceLix": 25,
"grants": [{ "kind": "currency", "code": "coin", "amount": 500, "display": "wallet" }] }Drop --dry-run to register for real. The product is live as soon as the command returns. A
--price of 0 registers a free claim. An empty --grants '[]' makes a tip jar. The grant shapes
are currency and pass (optionally with a duration), up to 8 per product.
2. Ask to buy
const result = await Helix.marketplace.purchaseProduct('coin-pack');
if (result.completed) refresh();
else showToast(result.reason ?? result.status);purchaseProduct shows the shell's confirmation, charges the wallet and settles. Branch on
result.completed, not on a single status, so a free claim and an AlreadyOwned answer are not
mistaken for failures. A Pending status is not final: re-read it with
Helix.marketplace.getPurchase(idempotencyKey).
3. Read what the player owns
const e = await Helix.purchases.getEntitlements();
const coins = e.balances['coin'] ?? 0;
const hasPass = e.passes['vip']?.active === true;
// spend from a server-held balance
const spent = await Helix.purchases.consume('coin', 100);
if (!spent.applied) showToast('Not enough coins');Entitlements live on the server and survive across sessions. For gating inside a multiplayer room, the declarative rules can react to a settled purchase directly, so the room never trusts the client's word about what it owns.
Try it without spending anything
helix dev serves your built world under a simulated shell with no login and no backend. Purchases,
saves and entitlements are answered locally, and a debug menu forces them to fail so you can test
the unhappy paths.