---
title: "Inventory"
description: "Currencies (credits, coins, gems) and items (unlocks, skins, potions) are owned per user (called players in the API)."
url: "/docs/guides/inventory/"
lang: en
index: "/llms.txt"
---
# Inventory

Currencies (credits, coins, gems) and items (unlocks, skins, potions) are owned per user
(called *players* in the API). The rule that keeps balances honest: **the client can read and spend, but never create.** Balances grow only on the server, through
[rewarded ads](/docs/guides/ads/index.md).

You do not have to use it. If you run your own payments (your own Stripe, for example) or keep balances in your own
[Functions](/docs/guides/functions/index.md) and database, skip this page. It is off until you turn it on:

```json kumo.config.json
{ "features": { "inventory": true } }
```

Then push it with `kumodeck config push`.

For games: the examples below use a game's coins, gems and items. An app's credits work the same way.

## Declare

```json
{
  "currencies": [
    { "key": "coins", "title": "Coins", "initial": 100, "max": 1000000 },
    { "key": "gems" }
  ],
  "items": [
    { "key": "potion", "kind": "consumable", "max": 99 },
    { "key": "red_skin", "kind": "durable", "title": "Red Skin" }
  ]
}
```

| `kind` | Meaning | Default cap |
|---|---|---|
| `consumable` | used up when spent (potions, tickets) | 1e9 |
| `durable` | owned or not (skins, unlocks) — cannot be spent | 1 |
| `collectible` | like durable, grouped separately for collection UIs | 1 |

Currencies start at `initial` (granted on the user's first inventory read) and are capped at `max`. Grants that
would exceed a cap are clamped; the response tells you what was actually added.

## Read and spend

```js
const inv = await kumo.inventory.get();          // { currencies: { coins: 100 }, items: { potion: 2 } }

await kumo.inventory.consume({ items: { potion: 1 } });
```

`consume` is **all or nothing**: if anything is short, nothing changes and you get 409 `insufficient` with
`details.shortages`. The SDK attaches an `Idempotency-Key` automatically, so a retried request never spends twice.
Durable and collectible items cannot be consumed.

Spending coins on something in your game (a continue, a hint) is `consume` followed by your own game logic:

```js
try {
  await kumo.inventory.consume({ currencies: { coins: 50 } });
  continueRun();
} catch (e) {
  if (e.code === 'insufficient') offerRewardedAd();  // e.g. watch an ad for more coins
  else throw e;
}
```

A user sees and spends only their own inventory: your publishable key and the user's login allow nothing
else. After a rewarded ad, read it again with `kumo.inventory.get()` — the
[ads](/docs/guides/ads/index.md) guide shows where.

Every change — ad reward, spend — is recorded in a ledger with the balance after it.
