Appearance
ScatterKings for Operators
Add ScatterKings (SK) slot games to your casino through OneHazel.
The integration direction: OneHazel runs the ScatterKings connector — it speaks SK's protocol, signs and verifies SK's callbacks, and routes them. Your job is to expose a wallet OneHazel can settle play against, sync the games to your storefront, and open the launch link. You never write ScatterKings-specific code, never hold SK's signing keys, and never build an SK-shaped payload.
OneHazel ⇄ you — the contract
OneHazel's connector calls a vendor-neutral wallet & session API on your side (/provider/v1/*) for every balance check, bet, win, rollback, and free-round. You implement that contract (or, on a OneHazel-supported platform, it's already built). OneHazel handles everything SK-specific — protocol, HMAC signatures, routing, currency conversion. The same contract serves every game provider, not just SK.
How a spin flows
player clicks Play
→ your storefront gets a launch link from OneHazel and opens it
→ OneHazel exchanges the link, starts the SK game, returns the game client
→ as the player spins, SK → OneHazel → YOUR wallet:
POST /provider/v1/session/balance show balance
POST /provider/v1/wallet/bet debit the stake
POST /provider/v1/wallet/win credit a win
POST /provider/v1/wallet/rollback reverse on error
→ your ledger is the source of truth; the in-game balance mirrors itScatterKings never calls you directly — OneHazel's connector does, after translating SK's protocol into the vendor-neutral contract below.
Which path are you on?
| Your platform | What you do |
|---|---|
| On a OneHazel-supported platform (the wallet contract is already built in) | Config only — mint a key (Step 1); OneHazel registers the connection and syncs the catalogue. Then Steps 3–5. |
| Your own / custom platform | Implement the receiving side of the /provider/v1/* contract (Step 2), then config. |
Reference implementation
A complete, code-verified reference implementation of this entire contract exists (SIM Casino, OPERATOR_API.md). Use it as a worked sample to build against — not a platform you adopt. An established operator implements the contract on their own wallet/ledger.
Step 1 — Get approved + mint a callback key
ScatterKings approves each operator in writing before go-live; OneHazel coordinates that approval and provisioning for you. In parallel, mint an integrator key with the provider_callback scope on your platform — the credential OneHazel presents on every wallet call:
bash
POST /api/v1/admin/integrator-keys
Authorization: Bearer <your-admin-jwt>
{ "name": "onehazel-scatterkings", "scopes": ["provider_callback"] }
→ { "key": "sim_live_<48 hex>", "last4": "…", "is_active": true } # shown onceGive OneHazel, over a secure channel (not email/chat): the key, your platform base URL, and your operator identity.
Scope matters
A key without provider_callback authenticates but is rejected on every wallet call with 403 SCOPE_FORBIDDEN (distinct from 401 AUTH_INVALID for an unknown/revoked key). If wallet calls 403 after go-live, check the scope first.
Step 2 — Implement the wallet contract
Custom platforms only — on a supported platform this is built in; skip to Step 3.
OneHazel calls these endpoints on your platform. This is the minimum surface to connect. Auth on every call: Authorization: Bearer sim_live_<your key>. All money is int64 minor units (EUR 3.56 = 356).
| Endpoint | You implement |
|---|---|
POST /provider/v1/session/start + /session/exchange | Mint a one-shot launch token, then exchange it for a wallet-capable session token. |
POST /provider/v1/session/balance | Return the wallet balance for a session token. |
POST /provider/v1/wallet/bet · /win · /rollback | Debit / credit / reverse against your ledger, keyed on idempotency_key. |
POST /provider/v1/catalog/sync | Upsert the games OneHazel pushes. |
GET /api/v1/public/v2/games | Public read of synced games — your lobby source. |
POST /provider/v1/freerounds/* | Optional — only if you run SK free-round campaigns; otherwise return 501. |
Four cross-cutting must-builds underneath those endpoints:
- Idempotency store — persist
(idempotency_key → HTTP status + response body)and replay it byte-for-byte (adding"duplicate": true) on any repeat. Globally unique across endpoints. This is what makes every mutating call safe to retry. - Wallet ledger in
int64minor units — never floats.betdebits,wincredits,rollbackreverses by the echoedmetadata.amount_minor. - Session mint/verify — a short-lived one-shot
plaunch_*(~5-min TTL) exchanged for a longer-livedpsess_*; verify on every wallet call. - Public catalogue read — serve the synced games so the storefront can launch them.
Endpoint samples
One flow throughout: player p_abc, EUR wallet at 10000 (€100.00), bet 350, win 1200.
Exchange the launch token → wallet session — POST /provider/v1/session/exchange
json
// request
{ "idempotency_key": "ik_exch_<uuid>", "launch_token": "plaunch_abcd1234…", "client_ip": "203.0.113.42" }
// response
{
"session": { "token": "psess_ef5678…", "expires_at": "2026-05-25T13:00:00Z" },
"player": { "external_id": "p_abc", "currency": "EUR", "balance_minor": 10000 },
"duplicate": false
}Balance — POST /provider/v1/session/balance
json
// request
{ "session_token": "psess_ef5678…" }
// response
{ "player_external_id": "p_abc", "currency": "EUR", "balance_minor": 10000 }Bet (debit) — POST /provider/v1/wallet/bet
json
// request
{ "idempotency_key": "ik_bet_<uuid>", "session_token": "psess_ef5678…",
"round_external_id": "round_77a2", "amount_minor": 350, "currency": "EUR",
"provider_transaction_id": "sk_tx_8841", "round_closed": false }
// response
{ "status": "accepted", "player_external_id": "p_abc", "currency": "EUR",
"balance_minor": 9650, "transaction_external_id": "tx_0a1b2c3d…", "duplicate": false }Win (credit) — POST /provider/v1/wallet/win
json
// request
{ "idempotency_key": "ik_win_<uuid>", "session_token": "psess_ef5678…",
"bet_idempotency_key": "ik_bet_<uuid>", "round_external_id": "round_77a2",
"amount_minor": 1200, "currency": "EUR", "provider_transaction_id": "sk_tx_8842",
"round_closed": true }
// response
{ "status": "accepted", "player_external_id": "p_abc", "currency": "EUR",
"balance_minor": 10850, "transaction_external_id": "tx_1b2c3d4e…", "duplicate": false }Rollback — POST /provider/v1/wallet/rollback — reverses a prior bet or win by its target_idempotency_key. You must echo the original amount in metadata.amount_minor (the original request body isn't stored).
json
// request
{ "idempotency_key": "ik_rbk_<uuid>", "session_token": "psess_ef5678…",
"target_idempotency_key": "ik_win_<uuid>", "round_external_id": "round_77a2",
"provider_transaction_id": "sk_tx_8843", "metadata": { "amount_minor": 1200 } }
// response
{ "status": "rolled_back", "player_external_id": "p_abc", "currency": "EUR",
"balance_minor": 9650, "duplicate": false }Response contract
Wallet calls return a status string + the post-op balance_minor:
status | Meaning | HTTP |
|---|---|---|
accepted | Bet/win applied | 200 |
insufficient_funds | Balance too low (bet only) — returns the unchanged balance | 402 |
rolled_back | Reversal applied | 200 |
"duplicate": true | Idempotent replay of a prior call — original body + status replayed | (replays original) |
Error envelope is { "success": false, "error": { "code", "message" } }. Key codes: validation_error 400 · session_unknown / session_expired / session_revoked 401 · unknown_player / unknown_provider / unknown_game 404 · unknown_transaction 404 (rollback) · currency_mismatch 409 · bet_out_of_bounds 400 · launch_token_consumed 409.
Session lifecycle — bet requires an active session; win and rollback are honored on an expired session (the game runtime may queue a payout after the session lapses); revoked / unknown → 401 on everything.
Free rounds (optional)
If you run SK free-round campaigns, implement freerounds/{issue,get,cancel,settle}. The key rule: free-round bets are funded by the provider (no wallet/bet debits), and winnings are paid as a single lump sum at result time — so settle (mapped from SK's freerounds.result) credits total_win_minor to the wallet exactly once and closes the grant. Do not also credit free-round wins via wallet/win. Replays never double-credit; a re-settle of a closed grant returns 409 before any credit.
Step 3 — Sync + show the games
OneHazel pushes the SK catalogue to your POST /provider/v1/catalog/sync (upsert by <provider_slug>:<provider_game_code>). Your storefront then reads the public catalogue (GET /api/v1/public/v2/games) and renders the SK titles on your slots page alongside your other providers.
Read the right catalogue
Render from the catalogue catalog/sync writes to — not a separate hand-curated CMS list — or the games sync fine but never appear on your site.
Step 4 — Wire game launch
When a player clicks an SK game, get the launch URL from OneHazel for that player + game, and open it — embedded in an iframe or as a full-page redirect.
Two-stage launch token (why it's safe)
The launch URL carries only a single-use plaunch_* token — never a wallet-capable session token. OneHazel's connector exchanges it server-side (/session/exchange) for the psess_* used on wallet calls. A leaked launch URL can't touch the wallet, and a second exchange of the same token returns 409.
If a game won't embed
Some game clients forbid being shown inside another site's frame (X-Frame-Options). If a game shows "refused to display," launch it as a full-page redirect (window.location = launchUrl) instead of an iframe.
Step 5 — Verify end-to-end
- Catalogue — the SK titles appear on your storefront's slots page.
- Launch — click a game; it loads (iframe or redirect).
- Balance — the in-game balance matches the player's wallet (e.g.
€100.00, not0.00and not10000.00— check your minor-units conversion). - Bet / win — a bet lowers the balance and a win raises it; both are reflected in your ledger (
GET /api/v1/players/{id}/wallet). - Free rounds (if used) — issue a grant, play it out, confirm the lump-sum credit lands once at settle.
Keeping it running
- New games / updates — OneHazel re-runs
catalog/sync; verify the new titles in your public catalogue. No operator code change. - Going to production — moving from the SK test environment to live is handled by OneHazel; you'll receive an updated launch link.
Reference
The full vendor-neutral contract — every endpoint, the complete error table, idempotency, session semantics, money rules, and a worked reference implementation — is in OPERATOR_API.md (the SIM Casino reference implementation you can build against). Free-round custom-bet modes and the exact catalogue shape are covered there.
Need help?
If a game won't load, the in-game balance looks wrong, or a bet/win isn't reflected in your ledger, contact OneHazel with the game and player — OneHazel owns the ScatterKings integration end-to-end and will diagnose it.