# Tokens

> Two-tier token model — one primary spendable, one progression accumulator. Customers pick the names.

Source: https://docs.hatched.live/docs/concepts/tokens

Tokens are the currencies attached to a buddy. Hatched ships a **two-tier
model**: each customer configures exactly two token slots.

- **Primary** — the spendable currency. Marketplace purchases, gate unlocks,
  and `buddies.spend` draw from this slot.
- **Progression** — earn-only. Never spent; it accumulates and gates things
  like evolution readiness.

The token names are yours. Fantasy buddies use `gems` + `mana`. A fitness
app might use `reps` + `streaks`. Pick whatever fits the product.

## Why two tiers

A single wallet collapses two different motivations into one number:
"what can I buy" and "how far have I come". Splitting them makes both
feelings legible — you can spend your gems without feeling like your
overall progress regressed.

Progression is deliberately **unspendable**. Attempting
`buddies.spend(buddyId, { token: '<progression_key>' })` throws
`progression_not_spendable`. That keeps long-term progress monotonic while
leaving the primary slot free for economy design.

## Example

```
gems  (primary)      — earned from lesson_completed, spent on items + gates.
                       Capped at 500 per week.

mana  (progression)  — earned from quiz_passed, feeds "stage 3 at mana ≥ 1000"
                       evolution readiness. Cannot be spent.
```

## How to set it up

1. In the dashboard **Tokens** page, pick a `token_key` and `label` for
   each slot. Use `snake_case` for keys (`gems`, `xp_coins`, `mana`).
2. Optionally set an `icon` and a `max_balance` (primary only).
3. Attach earn rules per slot. Same shape as the rule engine elsewhere —
   event type, amount, optional cap.
4. Reference `primary.token_key` in marketplace item prices and gate costs.

If you skip this step, the [onboarding plan](/docs/concepts/overview) seeds
both slots from a theme catalog — a fantasy buddy defaults to
`gems` + `mana`, fitness to `reps` + `streaks`, and so on. See
[Token economy](/docs/concepts/token-economy) for the fallback rules.

## At runtime

```ts
const summary = await hatched.buddies.tokens(buddyId);
// {
//   primary:     { key: 'gems', label: 'Gems', balance: 120, lifetimeEarned: 340, lifetimeSpent: 220 },
//   progression: { key: 'mana', label: 'Mana', balance: 480, lifetimeEarned: 480, lifetimeSpent: 0   },
// }
```

Spend against the primary by default — no `token` arg needed:

```ts
await hatched.buddies.spend(buddyId, { amount: 50, reason: 'gate:advanced_mode' });
```

Pass `token` only when you have a multi-primary setup (not supported today —
reserved for a future expansion).

## Surfacing it to end users

Drop the [tokens widget](/docs/reference/widgets/tokens) — `<div
data-hatched-mount="tokens"></div>` — to show the wallet in your product:
the primary balance up top, then the progression token with a progress bar
toward its `max_balance`. It needs the `tokens` capability and a `read`
scope; the primary number stays live off the shared `/widget/state` poll.

## Gotchas

- **Progression is monotonic.** Rule engine writes go through but spend
  attempts are rejected. If you want a "spend XP" mechanic, that currency
  belongs in the primary slot, not progression.
- **Token keys are immutable.** Once rules and ledger rows reference a key,
  renaming it in the dashboard doesn't rewrite history. Pick the key you
  can live with.
- **Tokens emit ledger webhooks.** `token.earned` and `token.spent` fire per
  slot; subscribe if you sync balances downstream.
- **The legacy 4-tuple is gone.** Pre-0.3 customers used
  `hatch_token`/`evolution_token`/`reroll_token`/`gift_token`. Migration
  024 introduced the `kind` column; existing configs default to `primary`.

## Related

- [Token economy](/docs/concepts/token-economy) — how the two tiers, gates, and pricing fit together.
- [Coins](/docs/concepts/coins) — the default primary currency.
- [Unlock gates](/docs/guides/unlock-gates) — spending the primary token to unlock features.
- [Tokens widget](/docs/reference/widgets/tokens) — showing the wallet.
