How it works

Two services are involved. The Backoffice API manages operator creation and configuration. The Games API mints launch tokens and handles the actual game session. Both authenticate via your aggregator API key.

You
POST /bo/aggregator/operators — create an operator under your account (Backoffice API)
You
PUT /bo/aggregator/operators/:id/config — set currencies, games, bet limits (Backoffice API)
You
POST /v1/aggregator/operators/:code/launch-tokens — mint a launch token for a player (Games API)
Platform
GET /launch?token=… — player session established, redirected to game
HS256 operators only Aggregator token minting is only supported for operators using the HS256 JWT algorithm. RS256 operators must mint tokens themselves using their own private key.

Get provisioned by an admin

A platform super-admin creates your aggregator account and sends you your first API key. This is a one-time operation — the plaintext key is shown only once and never stored by the platform.

🔑
Your API key format Keys look like a1b2c3d4e5f6.<secret> — a 12-character prefix followed by a dot and a random secret. Store this securely; it cannot be recovered. Use key rotation if you lose it.

All Backoffice API requests use the header:

Request header — all aggregator endpoints
X-Aggregator-Key: a1b2c3d4e5f6.your-secret-here

Verify your credentials work by calling the me endpoint:

GET
/api/v1/bo/aggregator/me
200
{
  "aggregator": {
    "id":     "agg-uuid",
    "code":   "acme-games",
    "name":   "Acme Games Ltd",
    "status": "active"
  }
}

Create an operator

Each operator is a white-label tenant — a distinct brand with its own wallet endpoint, JWT secret, and frontend URL. Operators you create are automatically linked to your aggregator account.

POST
/api/v1/bo/aggregator/operators
201
// Request body
{
  "code":                "mybrand",        // unique slug, lowercase a-z 0-9 hyphens
  "name":                "My Brand Casino",
  "walletBaseUrl":       "https://wallet.mybrand.com",
  "gameFrontendBaseUrl": "https://play.mybrand.com",
  "walletTimeoutMs":    5000           // optional, default 5000
}
Response — operator created
201
{
  "operator": {
    "id":                  "op-uuid",
    "code":                "mybrand",
    "jwtAlg":             "HS256",
    "jwtSharedSecret":    "hex-secret-shown-once",  // ← store this!
    "walletHmacSecret":   "hex-secret-shown-once",  // ← store this!
    "walletBaseUrl":       "https://wallet.mybrand.com",
    "gameFrontendBaseUrl": "https://play.mybrand.com",
    "status":              "active"
  }
}
One-time secrets jwtSharedSecret and walletHmacSecret are returned exactly once. Store them in your secrets manager immediately. They cannot be retrieved again — only rotated via the platform admin.
FieldTypeDescription
code string required Unique operator slug. Pattern: ^[a-z0-9][a-z0-9-]*[a-z0-9]$. Used as iss in launch JWTs.
name string required Display name for the operator.
walletBaseUrl string required Base URL of your wallet service (receives Bet/Win/Rollback calls).
gameFrontendBaseUrl string required URL players land on after launch. The platform appends ?game=, currency=, etc.
walletTimeoutMs integer optional Wallet call timeout in ms. Default: 5000.

Configure the operator

Set the currencies, enabled games, and bet limits. The platform enforces this config on every bet — a currency or game not in the config will be rejected.

PUT
/api/v1/bo/aggregator/operators/:id/config
200
// Request body
{
  "enabledGames": ["crash", "dice", "blackjack", "baccarat"],
  "currencies": {
    "USDT": { "minBet": "0.1", "maxBet": "500" },
    "BTC":  { "minBet": "0.000005", "maxBet": "0.01" }
  },
  "featureFlags": {
    "autoplay":    true,
    "turbo":       true,
    "chat":        false,
    "soundDefault": true
  },
  "demoStartingBalance": 1000
}
Available game IDs dice limbo keno wheel plinko mines hilo crash roulette blackjack baccarat coinflip russian_roulette dice_fight multiplayer_blackjack

Mint a launch token for a player

When one of your players wants to play, your server mints a short-lived launch token on their behalf. The token is a signed HS256 JWT — the platform verifies it and starts the session.

🔒
Server-side only This call must be made from your backend. Never expose your aggregator API key to a browser or mobile client.
POST
/v1/aggregator/operators/:code/launch-tokens
200
// Request body
{
  "sub":        "player-123",    // your internal player ID
  "currency":   "USDT",
  "game":       "crash",
  "language":   "en",            // optional
  "country":    "US",            // optional
  "ttlSeconds": 300,             // optional, max 600, default 600
  "returnUrl":  "https://play.mybrand.com/lobby"  // optional
}
Response
200
{
  "token":     "eyJhbGciOiJIUzI1NiJ9...",
  "launchUrl": "/launch?token=eyJ...&game=crash&currency=USDT",
  "expiresIn": 300,
  "jti":       "uuid-nonce"    // single-use nonce, burned at /launch
}
FieldTypeDescription
sub string required Your player's ID. This is the value your wallet will receive in Bet/Win/Rollback calls.
currency string required ISO currency code. Must be enabled in the operator's config.
game string required Game ID the player should land on. Must be in enabledGames.
language string optional BCP 47 language tag (en, fa, tr, …). Falls back to operator default.
country string optional ISO 3166-1 alpha-2 country code. Passed to the wallet for geo-compliance checks.
ttlSeconds integer optional Token lifetime in seconds. Hard ceiling: 600. Default: 600. Shorter values reduce replay exposure.
returnUrl string optional URL the game UI shows as the "exit to lobby" link. Usually your lobby page.

Redirect the player

Take the launchUrl from the mint response and redirect the player's browser to it. The platform will verify the token, call your wallet's /auth endpoint, set a session cookie, and redirect the player to the game frontend.

Example — server-side redirect (Node.js)
// 1. Mint the token (server-side)
const res = await fetch(
  `https://api.betterplay.com/v1/aggregator/operators/mybrand/launch-tokens`,
  {
    method: 'POST',
    headers: {
      'X-Aggregator-Key': process.env.AGGREGATOR_API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      sub:       req.user.id,
      currency:  'USDT',
      game:      'crash',
      ttlSeconds: 120,
      returnUrl: 'https://play.mybrand.com/lobby',
    }),
  }
);
const { launchUrl } = await res.json();

// 2. Redirect the player
reply.redirect(`https://api.betterplay.com${launchUrl}`);
Token is single-use The launch token (identified by its jti nonce) is burned the moment the player hits /launch. A second redirect with the same token will be rejected. Always mint a fresh token per session attempt.

Full request flow

What happens when a player clicks "Play" on your lobby:

1

Player clicks "Play"

Your frontend sends a request to your own backend — e.g. POST /start-game.

2

Your backend mints a token

Your server calls POST /v1/aggregator/operators/mybrand/launch-tokens with the player's ID, currency, and game. Games API returns a signed JWT and a launchUrl.

3

Redirect to the platform

Your backend redirects the player's browser to https://api.betterplay.com{launchUrl}.

4

Platform verifies the token

GET /launch?token=… verifies the HS256 signature using the operator's shared secret, checks the jti nonce has not been used, and burns it.

5

Wallet auth

The platform calls POST {walletBaseUrl}/auth on your wallet to confirm the player exists, retrieve their balance and currency, and check for freezes.

6

Session established — player lands on the game

A session cookie is set and the player is redirected to gameFrontendBaseUrl?game=crash&currency=USDT&return_url=…. They are now authenticated and can bet.


Endpoint reference

All Backoffice API endpoints require X-Aggregator-Key. All Games API endpoints require the same header.

MethodPathServiceDescription
GET /api/v1/bo/aggregator/me Backoffice Return your aggregator profile.
POST /api/v1/bo/aggregator/operators Backoffice Create an operator owned by your aggregator.
PUT /api/v1/bo/aggregator/operators/:id/config Backoffice Upsert game/currency/feature config for an operator.
GET /api/v1/bo/aggregator/operators/:id Backoffice Read an operator's details.
GET /api/v1/bo/aggregator/operators Backoffice List all operators owned by your aggregator.
GET /api/v1/bo/aggregator/operators/:id/config Backoffice Read the current config for an operator.
POST /api/v1/bo/aggregator/api-keys/rotate Backoffice Issue a new API key. Old keys continue to work until explicitly revoked.
POST /v1/aggregator/operators/:code/launch-tokens Games API Mint a single-use HS256 launch token for a player.
GET /v1/aggregator/operators Games API List all operators owned by your aggregator.

API key rotation

Rotate your aggregator API key without downtime. The new key is issued immediately; switch over your services, then the old key expires naturally.

POST
/api/v1/bo/aggregator/api-keys/rotate
200
// Request body (optional)
{ "label": "rotated 2025-Q3" }

// Response
{
  "apiKey": "newprefix.new-secret-shown-once"
}

Error codes

HTTPConditionFix
401 Missing or invalid X-Aggregator-Key Check the header value and key format (prefix.secret).
401 Aggregator is disabled Contact the platform admin.
404 Operator not found or not owned by your aggregator Verify the operator code/ID and that it was created under your aggregator.
409 Operator code already exists Choose a different code — codes are globally unique.
422 Operator uses RS256 — minting not supported Aggregator minting is HS256 only. RS256 operators must self-mint.
422 Operator is disabled Enable the operator first via POST /bo/operators/:id/enable.