Game API

Crunchy-owned games plug into the casino's existing game flow as a first-party source (CRUNCHY_OWNED). The casino frontend never computes an outcome; it launches a session, sends bets, and renders what the server returns. All amounts are integers in minor units (cents, or 10⁻⁶ USDT).

1 · Catalog

GET/api/v1/games

Requires a bearer token. Returns every ACTIVE Crunchy-owned game in the normalized aggregator shape. Reel strips are never included.

{
  "source": "CRUNCHY_OWNED",
  "count": 20,
  "games": [{
    "id": "crunchy-fruits", "source": "CRUNCHY_OWNED", "provider": "Crunchy Time Studio", "first_party": true,
    "name": "Crunchy Fruits", "category": "SLOTS", "family": "CRUNCHY_FRUITS",
    "rtp": 0.965, "volatility": "MEDIUM", "max_win_multiplier": 5000,
    "layout": { "reels": 5, "rows": 5, "paylines": 10 },
    "math_version": "1.0.0", "real_money_enabled": false, "demo_enabled": true,
    "launch": { "method": "POST", "path": "/api/v1/games/crunchy-fruits/session" }
  }]
}

2 · Launch a session

POST/api/v1/games/:gameId/session

Requires Authorization: Bearer <player access token>. The session is bound to the exact active math version; if the game is updated, the next spin returns MATH_VERSION_CHANGED and the client must relaunch.

POST /api/v1/games/crunchy-fruits/session
{ "currency": "USD", "mode": "DEMO" }          // mode: DEMO | REAL (REAL requires real_money_enabled)

201 → {
  "session_id": "…", "expires_at": "…", "currency": "USD", "mode": "DEMO", "balance": 100000,
  "bets": { "min_bet": 20, "max_bet": 50000, "bet_levels": [20, 50, 100, …], "default_bet": 100 },
  "game": { …client-safe config: symbols, paylines, paytable, features, art palette… }
}

3 · Spin

POST/api/v1/sessions/:sessionId/spin

The server validates the session, currency, bet and funds, creates a round, draws a cryptographically secure outcome, evaluates paylines, wilds, scatters, free spins, bonus rounds and multipliers, applies the max-win cap, settles the wallet atomically and records an audit trail. Repeating the same idempotency_key returns the original settled round.

POST /api/v1/sessions/{session_id}/spin
{ "bet_amount": 100, "currency": "USD", "idempotency_key": "c7a2…" }

200 → {
  "round_id": "…", "balance": 99940, "outcome_reference": "sha256…", "duplicate": false,
  "outcome": {
    "base": { "reel_stops": [12, 3, 44, 7, 21], "grid": [["CHERRY", …], …], "line_wins": [{ "payline_id": 1, "symbol_id": "SEVEN", "count": 3, "positions": [[0,2],[1,2],[2,2]], "win": 40 }], … },
    "free_spins": { "triggered": false, … }, "bonus": { "triggered": false, … },
    "base_win": 40, "free_spin_win": 0, "bonus_win": 0, "multiplier": 1, "total_payout": 40, "capped": false
  }
}

Errors: 401 UNAUTHORIZED · 402 INSUFFICIENT_FUNDS · 403 REAL_MONEY_DISABLED · 409 SESSION_EXPIRED | MATH_VERSION_CHANGED | ROUND_IN_PROGRESS · 400 BET_* validation codes

4 · Session state & close

GET / DELETE/api/v1/sessions/:sessionId

Returns balance and the last 20 rounds; DELETE closes the session.

Wallet interface

serverWalletAdapter

The engine never touches balances directly. Demo mode uses the test wallet. Real-money mode supports USDT, USDC, BTC, and ETH through Crunchy Time's private append-only crypto ledger. settleRound(user, asset, round_id, bet, payout) performs one atomic, idempotent wager and payout per round. Spins settle internally; blockchain deposits and withdrawals remain fail-closed until custody, network, confirmation, and signing rules are configured.