One Shot
Primitive

Single random outcome per bet.

You define the possible multipliers. The platform calculates probabilities to guarantee 97% RTP. Good for slots, dice, wheel spins, scratch cards.

How It Works

You provide:

A list of multipliers (e.g., [0, 0.5, 2, 5, 10])

Platform handles:

Probabilities, RNG, payouts — always 97% RTP

Game Config

Declare this in your HTML file. The platform reads it on upload.

1window.GAME_CONFIG = {
2 primitive: 'one-shot',
3 multipliers: [0, 0.5, 1, 1.5, 2, 3, 5, 10, 25, 50]
4};

Config Rules

RuleDetail
At least 2 multipliersNeed at least 2 unique values
At least one < 1Must have a losing outcome (0 counts)
No duplicatesEach value must be unique
Max 100 multipliersPerformance limit
Max value: 100,000xPlatform payout cap
Max 4 decimalsValues rounded to 4 decimal places
Common config mistakes that will be rejected:
[2, 5, 10] — no losing outcome (need at least one value < 1)
[0, 2, 2, 5] — duplicate value (2 appears twice)
[0] — only one multiplier (need at least 2)
[0, 200000] — exceeds max value cap (100,000×)

PostMessage API

Place a Bet

1parent.postMessage({
2 type: 'bet',
3 id: Date.now(), // Unique request ID
4 stake: window.GAME_STAKE, // Crypto amount (read-only, set by platform UI)
5 currency: window.GAME_CURRENCY // 'ETH' | 'SOL' | 'USDT' (read-only)
6}, '*');
Stake & currency are platform-controlled. The values you send in the message are for reference only — the platform always uses the bet amount set by the player in the UI. Your game cannot change the bet size.

Receive Result

1window.addEventListener('message', (e) => {
2 if (e.data.type === 'betResult' && e.data.id === myRequestId) {
3 const r = e.data.result;
4
5 // Errors are inside result for one-shot bets
6 if (r.error) {
7 showError(r.error);
8 return;
9 }
10
11 // r.multiplier — winning multiplier (e.g., 5)
12 // r.result — 'WIN' (multiplier > 1) | 'PUSH' (multiplier = 1) | 'LOSS' (multiplier < 1)
13 // r.payout — crypto payout amount
14 // r.payoutUsd — USD payout amount
15 // r.profit — crypto profit (can be negative)
16 // r.profitUsd — USD profit
17 // r.balance — new balance after bet
18 }
19});

Possible Errors

When a bet fails, r.error inside the result will contain one of these strings:

ErrorCause
'Insufficient balance'Player doesn't have enough funds for the bet
'Rate limited'Bets sent faster than 1 per 25ms
'Invalid stake'Stake is zero, negative, or not a number
'Game not found'GAME_CONFIG is missing or game hasn't been uploaded
'Session expired'Game session timed out (rare — usually after long inactivity)

Stake Updates

The platform sends updated stake info when the player changes their bet amount:

1window.addEventListener('message', (e) => {
2 if (e.data.type === 'stakeUpdate') {
3 stake = e.data.stake;
4 stakeUsd = e.data.stakeUsd;
5 currency = e.data.currency;
6 }
7});

Variants

If your game has multiple multiplier sets — for example, different risk levels or board sizes — use variants instead of multipliers. Each variant is a named multiplier array. The player's selection determines which set is used for each bet.

1window.GAME_CONFIG = {
2 primitive: 'one-shot',
3 variants: {
4 'low-8': [0.5, 1, 1.1, 2.1, 5.6],
5 'medium-8': [0.4, 0.7, 1.3, 3, 13],
6 'high-8': [0.2, 0.3, 1.5, 4, 29],
7 'low-12': [0.5, 1, 1.1, 1.4, 1.6, 3, 10],
8 'medium-12': [0.3, 0.6, 1.1, 2, 4, 11, 33],
9 // ... more variants
10 }
11};

Include the variant key in your bet message to select which set to use:

1parent.postMessage({
2 type: 'bet',
3 id: Date.now(),
4 stake: window.GAME_STAKE,
5 currency: window.GAME_CURRENCY,
6 variant: 'medium-12' // Selects which multiplier set
7}, '*');
Each variant follows the same rules as a normal multipliers array (at least 2 values, at least one < 1, no duplicates, etc.). The platform calculates independent probabilities for each variant to guarantee 97% RTP.
Use either multipliers or variants, not both. If your game has only one multiplier set, use multipliers. If it has multiple, use variants.

Continuous Mode

Optional alternative: instead of fixed multipliers, the player picks any target from 1.01x to 100,000x. The result is binary — they either win their target or get 0x.

P(win) = 0.97 ÷ target

Example: 2x target → 48.5% win chance

1window.GAME_CONFIG = {
2 primitive: 'one-shot',
3 mode: 'continuous',
4 maxMultiplier: 100000 // Optional, defaults to 100,000
5};

The bet postMessage is the same, but include the player's chosen target:

1parent.postMessage({
2 type: 'bet',
3 id: Date.now(),
4 stake: window.GAME_STAKE,
5 currency: window.GAME_CURRENCY,
6 targetMultiplier: 2.5 // Player's chosen target
7}, '*');

Balance Reveal Timing

By default, the player's visible balance updates the moment the bet API responds — before any outcome animation plays. To preserve the dopamine hit, send balanceReveal at the moment you show the outcome (e.g. ball lands, wheel stops, card flips).

1// Inside your animation callback:
2parent.postMessage({ type: 'balanceReveal' }, '*');
For multi-ball or concurrent bets, send one balanceReveal per outcome — they are processed in FIFO order. A 30-second fallback fires automatically if you never send it.

Complete Template

1<!DOCTYPE html>
2<html>
3<head><title>My Slots</title></head>
4<body>
5 <button onclick="spin()">SPIN</button>
6 <div id="result"></div>
7
8 <script>
9 window.GAME_CONFIG = {
10 primitive: 'one-shot',
11 multipliers: [0, 0.5, 1, 2, 5, 10, 50]
12 };
13
14 let pendingId = null;
15
16 function spin() {
17 if (pendingId) return;
18 pendingId = Date.now();
19 var payload = {
20 type: 'bet',
21 id: pendingId,
22 stake: window.GAME_STAKE || 1,
23 currency: window.GAME_CURRENCY || 'USDT'
24 };
25 // In preview mode, include game config so the platform
26 // can validate without an upload
27 if (window.TEST_MODE) {
28 payload.gameConfig = window.GAME_CONFIG_FOR_API;
29 }
30 parent.postMessage(payload, '*');
31 }
32
33 window.addEventListener('message', (e) => {
34 if (e.data.type === 'betResult' && e.data.id === pendingId) {
35 pendingId = null;
36 const r = e.data.result;
37 if (r.error) return alert(r.error);
38
39 document.getElementById('result').textContent =
40 r.result === 'WIN'
41 ? `Won ${r.multiplier}x — $${r.payoutUsd.toFixed(2)}`
42 : r.result === 'PUSH'
43 ? 'Push — money back'
44 : 'Lost!';
45 }
46
47 if (e.data.type === 'stakeUpdate') {
48 // Update local stake variables
49 }
50 });
51
52 parent.postMessage({ type: 'gameReady' }, '*');
53 </script>
54</body>
55</html>
In preview mode, window.TEST_MODE is true and the platform gives you a $1,000 test balance. Game logic runs normally — just no real money.