# Token economy

> How the two-tier token model, gates, and marketplace pricing fit together.

Source: https://docs.hatched.live/docs/concepts/token-economy

This page is the mental model for the whole token surface — how the two
slots get seeded, where they're spent, and where they accumulate.

## The loop

```
┌────────┐  events.send()      ┌────────────┐
│ Event  ├────────────────────▶│ Rule       │
└────────┘                     │ engine     │
                               └─────┬──────┘
                    earn rules       │        readiness conditions
                  ┌──────────────────┼──────────────────┐
                  ▼                                     ▼
          ┌──────────────┐                    ┌──────────────────┐
          │ primary      │◀─── buddies.spend  │ progression      │
          │ (spendable)  │     marketplace    │ (earn-only)      │
          │              │     gates.unlock   │ evolution gate   │
          └──────────────┘                    └──────────────────┘
```

Events feed both slots via the rule engine. The primary slot is where
users *consume*; the progression slot is where the buddy *grows*.

## How slots are seeded

When you apply an onboarding plan, Hatched looks at the `token_config`
bundle:

- **If the plan already has two slots** (one `primary`, one
  `progression`) — it's used as-is.
- **If the plan is empty or partial** — Hatched picks a theme from the
  plan description / target sector / creature style, then seeds both
  slots from a catalog.

The catalog matrix:

| Theme       | Primary (spendable) | Progression (earn-only) |
|-------------|---------------------|-------------------------|
| `fantasy`   | `gems`              | `mana`                  |
| `fitness`   | `reps`              | `streaks`               |
| `corporate` | `points`            | `xp`                    |
| `education` | `stars`             | `xp`                    |
| `tech`      | `bytes`             | `commits`               |
| `default`   | `coins`             | `xp`                    |

The resolved source lands in `customers.settings.applied_sources`:

```json
{
  "tokens":      "plan" | "fallback",
  "marketplace": "plan" | "fallback" | "hybrid",
  "theme":       "fantasy",
  "applied_at":  "2026-04-22T10:30:00Z"
}
```

Check that field if you're debugging why a customer ended up with
`gems`/`mana` instead of the names in their plan.

## Where each slot gets used

**Primary** is drawn from by:

- `hatched.buddies.spend(buddyId, { amount, reason })` — direct spend.
- `hatched.gates.unlock(buddyId, gateKey)` — gate cost is always primary.
- [Marketplace](/docs/concepts/marketplace) item prices.

**Progression** is read by:

- Evolution readiness conditions (`token.<progression_key> >= N`).
- Any custom rule engine condition that references the balance.
- Dashboards, widgets, leaderboards.

Attempting `buddies.spend({ token: '<progression_key>' })` returns
`progression_not_spendable`.

## Why not one wallet

A single wallet forces an ugly trade-off: spending a coin on a hat would
also lower the "how far I've come" number. Two slots lets the product
feel consumerist (spend, trade) *and* progressive (never regress) at the
same time.

If you genuinely want a single-wallet feel, set the primary token as
your only visible balance and treat progression as a backend-only metric
that drives evolution. Nothing in the SDK forces both to surface in the
UI.

## Migrating from the legacy 4-tuple

Customers created before 0.3 had a hardcoded `hatch_token` /
`evolution_token` / `reroll_token` / `gift_token` contract. Migration 024
added the `kind` column and defaulted every existing row to `primary`.
On the next onboarding apply, the fallback seeds a progression slot if
none exists — no manual step needed.

## Related

- [Tokens](/docs/concepts/tokens) — slot contract.
- [Marketplace](/docs/concepts/marketplace) — primary-priced catalog.
- [Unlock gates](/docs/guides/unlock-gates) — primary-spent feature flags.
- [Evolution](/docs/concepts/evolution) — progression-gated stages.
