Docs/Reference/Embers, Ingots & Wallet

Embers, Ingots & Wallet

For the conceptual overview, see Currency. All endpoints are under /v1/wallet, /v1/embers, and /v1/ingots. Every route requires an authenticated player (guest or registered). The @me token resolves to the caller from the bearer token, mirroring the Users convention.

Combined snapshot

GET /v1/wallet/@me

Returns both currencies plus level info in a single payload. Used by the account dashboard card and SparkUserMenu.

  • Auth: authenticated
GET/v1/wallet/@me

Response200 OK

JSON
{
   "embers": {
      "balance": 25,
      "lifetime": 25,
      "level": { "ord": 1, "name": "Tinder", "threshold": 0 },
      "next_level": { "ord": 2, "name": "Spark", "threshold": 100 },
      "progress": 0.25
   },
   "ingots": {
      "balance": 0,
      "purchased": 0
   }
}

Embers — earned currency

GET /v1/embers/@me

Same shape as the embers slice of /v1/wallet/@me.

GET/v1/embers/@me

Response200 OK

JSON
{
   "balance": 25,
   "lifetime": 25,
   "level": { "ord": 1, "name": "Tinder", "threshold": 0 },
   "next_level": { "ord": 2, "name": "Spark", "threshold": 100 },
   "progress": 0.25
}
FieldTypeMeaning
balanceintCurrent spendable Embers.
lifetimeintTotal ever earned. Drives level. Monotonic.
levelLevelCurrent tier (ord, name, threshold).
next_levelLevel|nullNext tier; null at max.
progressfloat[0, 1] progress toward next_level. 1.0 at max.

GET /v1/embers/@me/awards

List of awards the caller has earned. Optional ?game=lextris filter.

Response200 OK

JSON
{
   "awards": [
      {
         "award_id":   "id.award.first_word",
         "game":       "lextris",
         "ember_value": 25,
         "repeatable":  false,
         "granted_at":  "2026-04-26T21:42:18+00:00"
      }
   ]
}

ember_value is the snapshot at grant time. Editing the codex YAML later doesn't rewrite this number — the audit trail preserves what each player earned.

POST /v1/embers/@me/awards

Grant an award by codex id. Idempotent for non-repeatable awards.

Request

JSON
{
   "award_id": "id.award.first_word",
   "game":     "lextris"
}
FieldTypeConstraints
award_idstringrequired, ≤ 255. Codex id like id.award.first_word.
gamestringrequired, ≤ 64. Codex game preset (lextris, gear-goblins, katforge, ...).

Response — first earn201 Created

JSON
{
   "granted":     true,
   "award_id":    "id.award.first_word",
   "game":        "lextris",
   "ember_value": 25,
   "granted_at":  "2026-04-26T21:42:18+00:00",
   "wallet": {
      "balance":    25,
      "lifetime":   25,
      "level":      { "ord": 1, "name": "Tinder", "threshold": 0 },
      "next_level": { "ord": 2, "name": "Spark", "threshold": 100 },
      "progress":   0.25
   }
}

Response — already earned200 OK

Same shape with granted: false and the original granted_at. wallet reflects the current balance, not a fresh credit.

Errors

CodeMeaning
404No award with (award_id, game) in the codex.
422Validation failed (missing fields).

GET /v1/embers/@me/ledger

Most-recent-first paginated ledger of every credit and debit.

Query

ParamTypeDefaultNotes
limitint50Clamped to [1, 200].
beforeintExclusive ledger id from the previous page.

Response200 OK

JSON
{
   "entries": [
      {
         "id":         872,
         "amount":     25,
         "source":     "award",
         "reason":     "First Word",
         "ref_type":   "award",
         "ref_id":     "id.award.first_word",
         "created_at": "2026-04-26T21:42:18+00:00"
      }
   ],
   "limit": 50
}

source for Embers entries: award, spend, bonus, admin. Negative amount is a debit.

Ingots — paid currency

GET /v1/ingots/@me

GET/v1/ingots/@me
JSON
{
   "balance":   0,
   "purchased": 0
}
FieldTypeMeaning
balanceintCurrent spendable Ingots.
purchasedintTotal ever credited via purchase / gift / admin. Never drops on spend.

GET /v1/ingots/@me/ledger

Same shape and pagination as the Embers ledger; entries have source ∈ {purchase, spend, refund, gift, admin} and ref_type typically stripe, paypal, or gift.

Ingot top-ups land in a follow-up PR alongside the Stripe / PayPal webhook integration. Until then, purchase, spend, refund, and gift are reachable only through IngotService from server-side code or a console command.