Last updated: November 2026

Operator Integration Guide

A simple, step-by-step guide for backend and frontend engineers who want to embed Galaxy Games into their platform.

The 60-second version

You only have to build three things: (1) a server endpoint that asks Galaxy Games for a game link, (2) a page with an iframe that shows that link, and (3) a webhook endpoint that moves money in your player's wallet when they bet or win. Everything else is optional or already handled for you.

1The big picture

Galaxy Games is a crash-game platform. You connect to it with a small server-to-server API. Your backend asks for a game link, your frontend shows the link in an iframe, and Galaxy Games tells your wallet service about every bet and win.

  1. Create sessionYour backend calls Galaxy Games with your API key.
  2. Get game linkYou receive a signed gameUrl.
  3. Player playsBrowser shows the game in an iframe.
  4. Wallet updatesGalaxy Games sends signed webhooks to you.

Available games

gameType valueGame name
aviatorAviator
jetxJetX
chicken_roadChicken Road
pilot_chickenPilot Chicken
fast_kenoFast Keno

2Getting started

A provider admin creates your operator account. You will receive the items below. Save them safely.

ItemWhat it is
API Key64-character secret. Shown only once. Store it in a secrets manager.
API Key PrefixFirst 8 characters. Safe to log. Used for support.
Admin username / passwordLogin for your operator admin panel.
Webhook SecretShared secret used to verify webhook signatures.
Operator UUIDYour operator ID, used in dashboard URLs and reports.

Default operator settings (all editable later in the admin panel):

SettingDefault
RTP target97 %
Min / Max bet1 / 10,000
Max win per bet500,000
CurrencyETB
Revenue share20 %
Webhook URLYour endpoint for bet/win events
Lost or leaked your API key?

Ask a provider admin to rotate it (POST /api/admin/operators/:id/rotate-key). A new key is shown once and the old one stops working immediately.

3Authentication

There are two kinds of credentials. Do not mix them up.

Operator API keyPlayer session token (JWT)
Used byYour backendThe player's browser
Sent asHeader X-Api-KeyInside the gameUrl after the # sign
LifetimeUntil rotated4 hours
RuleNever expose in a browser or appNever send to your backend or log it
  • The token sits in the URL fragment (after #). Browsers never send fragments to servers, so it stays out of logs.
  • The game reads the token once and removes it from the address bar.
  • The token contains: playerId, operatorId, externalPlayerId, currency, isDemo, gameType and a one-time jti (for instant revocation).

4Launching a game

Step 1: Create a session (from your backend)

POST/api/v1/session/create

Headers: Content-Type: application/json and X-Api-Key: <your key>

{
  "externalPlayerId": "player_123",
  "username": "JohnD",
  "currency": "ETB",
  "isDemo": false,
  "gameType": "aviator",
  "launch": "game"
}
FieldRequiredMeaning
externalPlayerIdYesYour own user ID for this player (max 128 chars).
usernameNoDisplay name in the game (max 64).
currencyNoCurrency code. Default ETB.
isDemoNotrue = play money, no webhooks. Default false.
gameTypeNoOne of the available games. Default aviator.
launchNo"game" opens the game, "lobby" opens the game picker.
{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "playerId": "a1b2c3d4-...",
  "expiresIn": 14400,
  "gameUrl": "https://games.example.com/aviator#token=eyJ..."
}
  • playerId is the Galaxy Games ID. Keep it: you need it for free bets and reports.
  • New players are created automatically. Existing players get their username updated if it changed.

Step 2: Show the game in an iframe

<iframe
  src="https://games.example.com/aviator#token=eyJ..."
  width="800" height="600" allow="fullscreen"
  sandbox="allow-scripts allow-same-origin allow-popups allow-forms">
</iframe>
Iframe blocked?

Your website's origin must be added to ALLOWED_ORIGINS on the Galaxy Games server. Ask your provider to add it.

Step 3 (optional): Let the player switch games

From inside the lobby, the game calls POST /api/v1/session/switch with the player token as Authorization: Bearer ... and body { "gameType": "fast_keno" }. You get a new token and gameUrl back. This is automatic. Player and operator status are re-checked every time, so a suspended player cannot sneak back in.

When session creation is refused

HTTPMessageWhat to do
403Player account is suspendedShow a friendly message. Do not retry.
403Player is self-excluded (with until date)Show the date. After it passes, a new session works again.

5Wallet webhooks

For every real-money action, Galaxy Games sends a signed POST request to your webhook URL. Your server must update the player's wallet. Demo sessions never send webhooks.

EventWhen it happensWhat you do
betPlayer places a betDebit the bet amount
winPlayer cashes out (manual or auto)Credit the winnings
refundBet refunded (e.g. round cancelled)Credit the amount back

Example payload

{
  "type": "bet",
  "playerId": "a1b2c3d4-e5f6-...",
  "betId": "b7c8d9e0-...",
  "roundId": "f1a2b3c4-...",
  "amount": 50.00,
  "currency": "ETB",
  "multiplier": 2.50,        // win events only
  "trigger": "manual",       // win events only: "auto" or "manual"
  "gameType": "aviator",
  "isDemo": false,
  "timestamp": "2024-06-01T12:34:56.789Z"
}
Heads up: how to calculate the win amount

The event table says to credit payout, but the example payload only shows amount and multiplier. In the example code, payout = amount × multiplier. Please confirm with your provider whether a payout field is included, and how free-bet wins should be treated, before you go live.

Verify every webhook

Each request carries X-Galaxy-Signature: sha256=<hex> and X-Galaxy-Event. (The older X-Aviator-* headers are also sent.) Compute an HMAC-SHA256 of the raw body with your webhook secret and compare it using a constant-time check. Reject anything that does not match.

  • Read the body as raw bytes, not parsed JSON. The signature is calculated over the exact raw text.

Delivery rules

RuleValue
AttemptsUp to 3 (waits 0.5 s, then 1.5 s, then 4 s)
Timeout per attempt5 seconds
Success meansAny HTTP 2xx response
URL requirementsPublic HTTPS. Private/internal IPs are blocked.
IdempotencyUse betId to skip events you already processed
Make it idempotent

The same event can arrive more than once. Save each processed betId and ignore repeats, so a player is never debited or credited twice.

6Free bets

Free bets are promotional bets you give to players. Placing one does not debit the wallet and sends no webhook. If it wins, you receive a normal win webhook.

FieldMeaning
amountValue of the free bet
wageringRequirementPlayer must wager amount × this (1–20x) before winnings are released
maxWinCapOptional limit on winnings
expiresAtWhen the free bet expires
bonusTypemanual, welcome, consecutive_loss, deposit_match, bulk, custom
gameTypeGame it can be used in

Grant to one player

POST/api/admin/free-bets

{
  "playerId": "<player UUID>",
  "amount": 10.00,
  "currency": "ETB",
  "wageringRequirement": 1,
  "expiresInHours": 72,
  "bonusType": "welcome",
  "gameType": "aviator"
}

Grant in bulk

POST/api/admin/free-bets/bulk

targetWho gets itAvailable to
self_allAll your active playersOperator admins
singlePlayers in a playerIds listOperator admins
operatorAll players of one operator (operatorId)Provider admins only
all_platformEvery active playerProvider admins only
  • Auto bonus: the platform can grant a consecutive_loss free bet automatically after N losses in a row. Set it up in the admin panel; no API call needed.
  • Safe by design: when a free bet is used, the server checks it belongs to the player, game and operator, matches the amount and is not expired or used. It is marked used and the bet saved in one atomic step.

7Reports & dashboard

All endpoints live under /api/admin/operator-dashboard/ and need admin login. Provider admins add ?operatorId=<uuid>.

EndpointWhat you get
/summaryKPIs: players, bets, wagered, payout, GGR, RTP, and what you owe
/financial?range=30&gameType=aviatorDaily financials. range: 7, 30, 90 or custom (from/to)
/revenue-trend30 daily points of wagered, payout, GGR
/game-history?page=1&limit=50Paginated rounds for your players
/game-history/:roundId/betsAll bets inside one round
/player-history?search=johnPlayers with lifetime stats
/top-playersTop 20 players by volume
/big-wins?limit=20Largest payouts, good for risk monitoring
/hourly-heatmap7×24 grid of activity over the last 30 days
/webhook-statusDelivery rate, failures and recent errors
Tip

Check /webhook-status regularly. A delivery rate below 100 % usually means your endpoint is slow or returning errors.

8Player safety

Each player is identified by your externalPlayerId. The same ID at two different operators is two separate players.

StatusEffect
activePlays normally
suspendedNo new sessions; open connections are cut immediately
self_excludedCannot play until the end date; reactivated automatically afterwards

Status is checked at: session create, every WebSocket connection, every bet, and every game switch. Suspending a player disconnects them instantly. Suspending an operator disconnects all its players.

Responsible-gambling fields per player: dailyDepositLimit, sessionTimeLimitMins, selfExcludedUntil. Manage them in the admin panel under /admin/players/:id.

9Custom client (WebSocket)

Most operators can skip this section

The embedded game already handles WebSockets. Read on only if you are building your own game interface.

Connect with Socket.IO and pass the player token. Crash games (Aviator, JetX) use the root namespace. Others use /chicken-road, /pilot-chicken or /fast-keno.

import { io } from 'socket.io-client';

const socket = io(url, { auth: { token: playerJwt } });

Client sends

EventPayloadPurpose
client:seed8–64 hex charsAdds your entropy to the provably fair seed
bet:placeamount, slot (1–4), autoCashoutAt (1.01 or more), freeBetIdPlace a bet
bet:cashout{ betId }Manual cash-out
round:historynoneLast 50 crash results

Server sends

EventMeaning
round:sync / round:idleState on connect / no round running
round:betting_openNew round, betting window open (includes serverSeedHash)
round:startedBetting closed, multiplier climbing
multiplier:tickCurrent multiplier, about every 100 ms
round:crashedRound over; server seed revealed for fairness checks
round:live-betsSnapshot of all live bets
bet:accepted / bet:rejectedBet confirmed / refused (with reason)
cashout:confirmed / cashout:failedCash-out result
player:cashoutSocial feed: someone cashed out
game:statusGame enabled or disabled
player:restoredAccount reactivated
server:pingKeepalive every 10 s; no reply needed
errorFatal; connection will close

Rate limits (per connection): 30 bets per minute and 10 cash-outs per minute. Going over returns a "Too many requests" rejection.

10Server settings

These live in the Galaxy Games server .env file. They are managed by the provider, but good to know.

VariableWhat it does
GAME_BASE_URLPrefix for gameUrl in session responses
ALLOWED_ORIGINSComma-separated list of sites allowed to embed the iframe
JWT_SECRETSigns player tokens. Never share.
WEBHOOK_HMAC_SECRETWebhook signing secret. Share securely with operators.
WEBHOOK_ALLOW_PRIVATEtrue only in development. Never in production.
REST_RATE_LIMIT_PER_MINPublic API limit per IP (default 120)
WS_BET_RATE_LIMIT_PER_MINBets per minute per connection (default 30)
WS_CASHOUT_RATE_LIMIT_PER_MINCash-outs per minute per connection (default 10)
TRUST_PROXYTrusted proxy hops for IP detection (default 1)

11Go-live checklist

Tick every box before you launch. Your ticks are saved in this browser.

0 / 10 complete

12Code examples

Create a session (Node.js)

async function createGameSession(playerId, username, currency = 'ETB') {
  const res = await fetch('https://games.example.com/api/v1/session/create', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Api-Key': process.env.GALAXY_API_KEY,
    },
    body: JSON.stringify({
      externalPlayerId: playerId, username, currency,
      isDemo: false, gameType: 'aviator', launch: 'game',
    }),
  });
  if (!res.ok) {
    const err = await res.json();
    throw new Error(`Session create failed: ${err.error}`);
  }
  return res.json(); // { token, playerId, expiresIn, gameUrl }
}

Receive webhooks (Node.js / Express)

const express = require('express');
const crypto  = require('crypto');
const app = express();

app.post('/webhooks/galaxy', express.raw({ type: 'application/json' }),
  async (req, res) => {
    const signature = req.headers['x-galaxy-signature'] ?? '';
    const expected = 'sha256=' + crypto
      .createHmac('sha256', process.env.GALAXY_WEBHOOK_SECRET)
      .update(req.body).digest('hex');

    const a = Buffer.from(expected), b = Buffer.from(signature);
    if (a.length !== b.length || !crypto.timingSafeEqual(a, b))
      return res.status(401).json({ error: 'Invalid signature' });

    const event = JSON.parse(req.body.toString());
    if (await isAlreadyProcessed(event.betId)) return res.sendStatus(200);

    switch (event.type) {
      case 'bet':
        await debitPlayerWallet(event.playerId, event.amount,
                                event.currency, event.betId);
        break;
      case 'win':   // payout = amount x multiplier (confirm with provider)
        await creditPlayerWallet(event.playerId,
                                 event.amount * event.multiplier,
                                 event.currency, event.betId);
        break;
      case 'refund':
        await creditPlayerWallet(event.playerId, event.amount,
                                 event.currency, event.betId);
        break;
    }
    await markProcessed(event.betId);
    res.sendStatus(200);
  });
One more thing to watch

A single bet can produce both a bet and a win event with the same betId. If you deduplicate on betId alone, store it together with the event type (for example betId + type) so the win is not skipped.

Embed the game (React)

function GameFrame({ playerId, username }) {
  const [gameUrl, setGameUrl] = React.useState(null);

  React.useEffect(() => {
    fetch('/api/game-session', {            // your own backend endpoint
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ playerId, username }),
    }).then(r => r.json()).then(({ gameUrl }) => setGameUrl(gameUrl));
  }, [playerId]);

  if (!gameUrl) return <div>Loading game...</div>;
  return (
    <iframe src={gameUrl} title="Galaxy Games" width="100%" height="700"
      allow="fullscreen"
      sandbox="allow-scripts allow-same-origin allow-popups allow-forms"
      style={{ border: 'none', borderRadius: 8 }} />
  );
}

13Error reference

Session errors

HTTPErrorCause
400Invalid requestMissing or malformed fields
401UnauthorizedWrong or missing API key
403Player account is suspendedPlayer is suspended
403Player is self-excludedActive self-exclusion (includes until)
500Internal server errorRetry after a short delay

Bet rejection reasons (WebSocket)

ReasonCause
Betting window is closedRound is flying or none is active
Bet must be between X and YAmount outside operator limits
Potential payout N exceeds max win MAuto-cashout times bet is above max win
Slot N already has a bet this roundSlot already used
Free bet not available or expiredFree bet missing, used or expired
Free bet amount mismatchAmount differs from free bet value
Game is currently disabledDisabled by an admin
Your account has been suspendedSuspended during the session
Too many bet requests - slow downRate limit hit
Invalid bet dataMalformed payload
Internal server errorTemporary problem, retry

If webhook deliveries keep failing

  • Your endpoint takes longer than 5 seconds to respond.
  • It returns a non-2xx status, including redirects (3xx). Use the final URL.
  • There is an SSL certificate error on your endpoint.
  • A firewall blocks Galaxy Games server IPs.

Failed transactions stay in the log. Contact your provider if problems continue after you fix your endpoint.

© GALAXY_GAMES · Operator Integration Guide Support: contact your provider admin