Wallet API reference
@moosehq/provider-sdk's ProviderClient wraps all four of these endpoints — most providers should use it rather than calling the HTTP API directly. All endpoints require a valid provider signature and are rate-limited per tenant. Errors from the endpoint itself share the body shape { "error": "<message>" }; a 401 from a bad/missing/expired signature is a plain-text body (invalid signature), not JSON — that check happens before the request reaches the endpoint. See Errors & retry for the full status code reference shared across every endpoint below.
Exceeding your tenant's rate limit returns 429 with a Retry-After header (seconds to wait) and body { "error": "rate limit exceeded" }.
POST /v1/wallet/session/verify
Request:
{ "sessionToken": "..." }Response: see VerifySessionResponse in the SDK reference.
Error responses: 400 (missing/empty sessionToken), 401 (no provider identity resolved — bad/missing signature; or the session token is invalid or expired), 403 (the session belongs to a different provider), 429 (rate limit exceeded), 500 (Central Config resolution failed).
POST /v1/wallet/transaction
Requires a session token — obtained from the session the operator's launch call created, which this endpoint resolves server-side (there's no separate operatorId field on the wire). Submits a canonical BET / WIN / ROLLBACK transaction. ADJUSTMENT is rejected on this endpoint — that's admin-only.
Status code mapping:
400— invalid request body (validation failure, e.g. malformedcurrency), or the request doesn't match the session it references (playerRef/gameId/currencymismatch)401— no provider identity resolved (bad/missing signature), or the session token is invalid or expired403— the session belongs to a different provider than the one that signed this request409— another attempt for thistransactionIdis in flight, don't retry yet429— rate limit exceeded503— game/operator not configured500— retryable platform-side failure (includes aTIMED_OUTresolution)
A 200 with status: "DECLINED" is a normal business outcome, not an error.
Request/response shapes: see TransactionRequest / TransactionResponse in the SDK reference for the exact wire schema. currency must be a 3-letter, uppercase ISO-4217 code (e.g. "USD", not "usd") — a malformed code is rejected with 400.
POST /v1/wallet/balance
Queries the player's current balance on demand, without moving any money. Requires only a session token — playerRef is resolved from the session server-side, so a provider can never query the balance of a player under a session it doesn't own. The operator's wallet remains the source of truth: this is a pass-through query, not a cached or platform-computed value.
Request:
{ "sessionToken": "..." }Response:
{ "balance": 4200 }balance is in minor currency units (e.g. cents), matching TransactionResponse.balance.
For a DEMO session, this returns the in-memory fun-play balance instead of querying a real operator.
Status code mapping:
400— missing/emptysessionToken401— no provider identity resolved (bad/missing signature), or the session token is invalid or expired403— the session belongs to a different provider than the one that signed this request429— rate limit exceeded503— operator not configured500— the operator's balance endpoint errored or timed out
Unlike POST /v1/wallet/transaction, this call is not idempotent-tracked and not retried by the platform on your behalf beyond what ProviderClient.getBalance does client-side — it's a read, so a failed attempt can simply be retried.
Bot-detection telemetry isn't on this API
There's no POST /v1/client/behavior here — bot-detection digests are posted straight from the game client's browser to a separate, dedicated behavior-ingestion endpoint, authenticated by session token rather than a provider signature (a browser can't hold your HMAC secret). See BehaviorReporter and the behavior-ingestion endpoint.