Skip to content
For LLMsView as Markdown·

For the operator's engineer building the wallet module.

wallet ​

The wallet module is your seamless wallet, exposed once in the shape of the contract. Every game studio in the Marketplace reaches your balances through it: OneHazel translates each studio's protocol into these calls, so you never build a per-studio wallet.

Who calls whom ​

OneHazel calls you. You host the operations under the base URL you register for wallet.

OperationWhat it doesIdempotency key
POST /session/startMint a single-use launch token for a player and gameyes
POST /session/exchangeExchange the launch token for a wallet-capable session tokenyes
POST /session/balanceRead the live balance for a sessionno
POST /wallet/betDebit a real-money stakeyes
POST /wallet/winCredit a winyes
POST /wallet/rollbackReverse a prior bet or win by the amount you storedyes
POST /freerounds/issue, /freerounds/cancel, /freerounds/settleRecord, cancel and lump-settle a free-round grantyes
POST /freerounds/getRead a grant's current stateno

The full request and response shapes are in the contract and the OpenAPI document at /openapi/connect-v1.yaml.

Headers and signature ​

Every call carries three headers. Check them in this order, over the raw request body, before you parse it.

HeaderValueReject with
AuthorizationBearer <callback key>, the cck_ value shown once at registration, or the callbackKey you supplied401 unauthorized
X-OneHazel-Signaturet=<unix seconds>,v1=<hex>, where the hex is HMAC-SHA256 of "<t>." + raw body keyed by your whsec_ signing secret401 signature_invalid when the MAC does not match or t is more than 300 s from your clock
X-OneHazel-Envtest or livean error when it is missing; route test to your test player or sandbox ledger only

Compare the MAC in constant time. During a secret rotation accept both the new and the previous secret for one hour.

This is deliberately a different format from webhook.receive, which keeps sha256=<hex> with no bearer and no timestamp.

Money and idempotency ​

  • Amounts are integer minor units (amount_minor, balance_minor) at the currency's exponent: 100 is 1.00 EUR but 100 JPY. A code outside the contract's table is 400 invalid_request naming the code; a listed currency that is not the player's is 422 currency_mismatch. Never round. See Currencies and exponents.
  • Every money-moving call carries the same key in the Idempotency-Key header and the idempotency_key body field. A replay returns the original response with duplicate: true; the same key with a different body is 409 idempotency_key_conflict.
  • Free-round bets never debit, so OneHazel never sends wallet/bet for them. freerounds/settle carries the lump credit itself and is never paired with a wallet/win for the same grant.

Registration ​

Register the base URL with a testPlayerId: a funded player in your test ledger. Wallet registration is refused without one, because conformance moves test money against that player and nobody else.

Conformance ​

A run sends every call signed, with X-OneHazel-Env: test, against your test player only.

ScenarioSeverity
auth.bad_bearerblocker
auth.bad_signatureblocker
auth.stale_timestampblocker
balance.okblocker
bet.okblocker
win.okblocker
bet.insufficient_fundsblocker
bet.duplicate_returns_originalblocker
rollback.betblocker
rollback.winblocker
rollback.amount_mismatchmajor
freerounds.bet_no_debitmajor
freerounds.settle_lumpmajor
env.test_header_requiredblocker
shape.empty_body_is_failureblocker

What passing looks like for each one is on Conformance. The module is conformant when the latest finished run passed.

Samples ​

StackSample
TypeScript + Expresswallet
TypeScript + Fastifywallet
Python + FastAPIwallet
PHP (no framework)wallet
PHP + Laravelwallet

See Samples for how to run one locally.