Skip to content

Operator Integration Guide

This guide walks you through integrating with The Aggregator from start to go-live. After completing the steps below, you will have a working game integration that can launch real-money sessions and process wallet callbacks.

Start after access, provider availability, callback setup and funding are confirmed. Implementation and validation time depend on your existing wallet integration.

Prerequisites:

  • An operator account in The Aggregator cabinet (register here)
  • A server that can receive HTTPS callbacks on a public URL
  • Familiarity with REST APIs and webhook/callback patterns

Before you start writing code, here is the full lifecycle of a single game round. This representative paid-round flow does not cover every provider or free-round sequence; see the free-round contract.

sequenceDiagram
participant Player
participant Operator
participant Aggregator
participant Provider
Player->>Operator: Opens game lobby
Operator->>Aggregator: POST /v1/sessions (game_id, player_id, balance)
Aggregator->>Provider: Create session
Provider-->>Aggregator: Session URL
Aggregator-->>Operator: { session_id, game_url }
Operator->>Player: Redirect to game_url
Note over Player, Provider: Player places a bet
Provider->>Aggregator: Bet callback
Aggregator->>Operator: POST {callback_url} (bet transaction)
Operator-->>Aggregator: 200 OK { balance, currency }
Aggregator-->>Provider: Balance confirmed
Note over Player, Provider: Player wins
Provider->>Aggregator: Win callback
Aggregator->>Operator: POST {callback_url} (win transaction)
Operator-->>Aggregator: 200 OK { balance, currency }
Aggregator-->>Provider: Balance confirmed
Player->>Operator: Closes game (redirected to return_url)

Key points to understand:

  1. You own the wallet. The Aggregator never holds player funds. Paid bets debit your ledger; a free bet with is_free: true does not debit real money. Apply wins and refunds according to the wallet contract.
  2. Callbacks are synchronous. The game provider waits for your response before showing the result to the player. Your callback endpoint must respond within 5 seconds.
  3. Idempotency is required. You may receive the same callback more than once. Use integration/organization, provider_code and transaction_id to deduplicate.

  1. Confirm access to the operator organization and deployment for your integration.
  2. Obtain a credential with permission for the intended operations, using the key-management controls available to your organization.
  3. Store the issued key unchanged in your secrets manager; capture it securely when it is provided.
  4. Configure an HTTPS callback URL and a separate signing secret. Where your deployment exposes Organization → Callback configuration or an integration wizard, use those settings; confirm the available navigation with your account contact. Store the secret when you set it rather than expecting to retrieve it from API Keys.
  5. Confirm provider/game availability and funding or any welcome credits agreed for real-session validation.

Complete the applicable commercial and compliance requirements before operating. Key access and technical tests do not constitute launch approval.

Funding: welcome credits depend on your launch agreement and activation. Confirm availability and permitted use before creating real-money sessions; registration does not guarantee credits. See Environments & testing.

Security: Store your API key and callback secret in environment variables or a secrets manager. Never commit them to source control. Never expose the key in browser-side code — this API is server-to-server only.


Use the credential issued for your integration to browse its enabled games.

Request:

Terminal window
curl -X GET "https://api.aggregator.gg/v1/games?per_page=5" \
--header @- <<EOF
Authorization: Bearer ${AGGREGATOR_API_KEY:?Set the issued API key in the protected environment}
EOF

Response:

{
"games": [
{
"id": "d4f7a2b1-3c8e-4f5a-9b6d-1e2f3a4b5c6d",
"name": "Book of Sun",
"provider_code": "truelabs",
"game_type": "slots",
"rtp": 96.5,
"volatility": "high",
"has_mobile": true,
"has_desktop": true,
"has_demo": true,
"thumbnail_url": "https://cdn.aggregator.gg/thumbs/book-of-sun.jpg",
"blocked_countries": ["US", "GB"],
"features": ["free_spins", "bonus_buy"],
"supported_currencies": ["USD", "EUR"]
}
],
"total": 142,
"page": 1,
"per_page": 5
}

Using the SDK: @aggregator-gg/sdk@1.0.0 requires Node.js 24 or newer.

import { Aggregator } from "@aggregator-gg/sdk";
const apiKey = process.env.AGGREGATOR_API_KEY;
if (!apiKey) throw new Error("Set AGGREGATOR_API_KEY in the server environment");
const aggregator = new Aggregator({ apiKey });
// Published SDK errors can contain untrusted upstream details. Keep them out
// of uncaught exceptions and logs; reuse this helper in the examples below.
async function safeSdkRequest<T>(operation: () => Promise<T>): Promise<T> {
try {
return await operation();
} catch {
throw new Error("Aggregator request failed");
}
}
const { games, total } = await safeSdkRequest(() => aggregator.games.list({
type: "slots",
rtp_min: 95,
per_page: 25,
}));
// Validate the returned fields against the API contract before using them.

You can filter by provider, game type, volatility, RTP range, features, currency, and full-text search. See the full API reference for all query parameters.

Every game returns supported_currencies — the ISO-4217 currencies it can be launched in. This is the game’s own list when set, otherwise the currencies its provider declares (provider→game inheritance), so a game with an empty list still inherits provider support.

Use it to build a storefront that never shows a player a game their wallet currency can’t open. Filter the catalog to a single currency with the currency query parameter:

Terminal window
curl -X GET "https://api.aggregator.gg/v1/games?currency=EUR" \
--header @- <<EOF
Authorization: Bearer ${AGGREGATOR_API_KEY:?Set the issued API key in the protected environment}
EOF

Only games whose effective supported_currencies includes EUR are returned (filtered before pagination, so total reflects it). You can also read provider-level support directly — without paging through games — from GET /v1/providers/registry, where each provider carries its own supported_currencies.

A session launched (Step 3) in a currency the selected game and provider don’t support can fail at launch, so filtering the catalog up front is the difference between a clean storefront and a dead-end click for the player.


To launch a game for a player, create a session. The response includes a game_url that you redirect the player to.

Request:

Terminal window
curl -X POST "https://api.aggregator.gg/v1/sessions" \
--header @- \
-H "Idempotency-Key: launch-player-42-20260402-001" \
-H "Content-Type: application/json" \
-d '{
"game_id": "d4f7a2b1-3c8e-4f5a-9b6d-1e2f3a4b5c6d",
"player_id": "player_42",
"balance": 10000,
"currency": "EUR",
"country": "DE",
"lang": "de",
"return_url": "https://your-casino.com/lobby"
}' <<EOF
Authorization: Bearer ${AGGREGATOR_API_KEY:?Set the issued API key in the protected environment}
EOF

Response:

{
"session_id": "8c8e8c8e-8c8e-4c8e-8c8e-8c8e8c8e8c8e",
"game_url": "https://games.aggregator.gg/launch/8c8e8c8e-8c8e-4c8e-8c8e-8c8e8c8e8c8e",
"provider_session_uid": "tl_session_abc",
"provider_code": "truelabs"
}

Using the SDK: @aggregator-gg/sdk@1.0.0 requires Node.js 24 or newer.

const session = await safeSdkRequest(() => aggregator.sessions.create({
game_id: "d4f7a2b1-3c8e-4f5a-9b6d-1e2f3a4b5c6d",
player_id: "player_42",
balance: 10000,
currency: "EUR",
country: "DE",
lang: "de",
return_url: "https://your-casino.com/lobby",
}));
// Validate the response against the API contract before redirecting the player
// to its game_url. Do not log the single-use URL.

If you need stable retries across your own queue or worker boundary, pass the same key explicitly:

const session = await safeSdkRequest(() => aggregator.sessions.create(
{
game_id: "d4f7a2b1-3c8e-4f5a-9b6d-1e2f3a4b5c6d",
player_id: "player_42",
balance: 10000,
currency: "EUR",
country: "DE",
lang: "de",
return_url: "https://your-casino.com/lobby",
},
{ idempotencyKey: "launch-player-42-20260402-001" },
));

Important details:

  • game_id is The Aggregator catalog UUID — the id returned by GET /v1/games. It is not the provider’s own game code (provider_game_id, for reference only). Always send The Aggregator id.
  • balance is in the currency’s minor units — the ISO-4217 minor unit, determined by the currency (not configurable per operator). Every currency The Aggregator currently supports is 2-decimal, so the minor unit is 1/100: 10000 = 100.00 EUR. (A 0-decimal currency such as JPY would use whole units, but no non-2-decimal currency is enabled today.)
  • country determines jurisdiction rules. Games blocked in the player’s country return a 403.
  • return_url is where the player lands when they close the game. If omitted, the Aggregator’s game-complete page is used.
  • game_url is single-use. Create a new session each time the player needs to launch the game.

You may redirect the browser to game_url or embed it in an iframe. If you rely on a provider’s URL-navigation fallback inside a sandboxed iframe, include allow-top-navigation-by-user-activation alongside the permissions your provider needs. Every sandboxed ancestor iframe must also permit this user-activated top-level navigation; a nested iframe cannot relax its ancestor’s restrictions:

<iframe
src="https://provider.example/launch/..."
title="Game"
sandbox="allow-scripts allow-same-origin allow-popups allow-forms allow-top-navigation-by-user-activation"
></iframe>

TRUE LABS’ current URL-based in-game Top-up fallback uses a direct player click to navigate the top-level casino page. Without allow-top-navigation-by-user-activation, the browser blocks that navigation with a SecurityError. The session contract carries a separate optional deposit_url; when you omit it, the Aggregator falls back to sending your return_url as the deposit destination (or the Aggregator game-complete page if both URLs are omitted). Either way the in-game Top-up CTA navigates to that exact URL — it does not invoke an external Deposit button or cashier modal. Send a trusted deposit_url on your casino domain that opens the cashier flow. If you leave it unset and your return_url route automatically restores the current game, Top-up will appear to refresh or relaunch the game instead.

This iframe permission is a compatibility requirement for the URL fallback, not a no-reload cashier integration. A URL with a different origin, path, or query performs a full top-level document navigation and may recreate both the casino page and game iframe.

To keep the casino page in place, use a same-document host hook. Set deposit_url to your current casino URL with only a dedicated fragment added — for example https://casino.example/game/abc#deposit. Fragment-only navigation stays in the current document; your front-end can handle the exact fragment on initial load and hashchange, open its existing cashier UI, and remove the fragment with history.replaceState so repeated clicks work. The origin, path, and query must otherwise match the current top-level URL exactly.

After changing deposit_url, create a new session with a fresh Idempotency-Key and launch its new game_url. Reconcile any ambiguous earlier launch before starting this new logical session. /v1/sessions caches the response for each operator and key for 24 hours. Reusing the previous key during that window replays the original session without evaluating the changed request body, so TRUE LABS would still receive the old deposit destination.

An agreed provider-to-host event bridge is another no-reload option. For that design, validate the exact message schema, source iframe, and allowed origin before opening the cashier.

Do not replace this narrow permission with allow-top-navigation, which permits top-level navigation without a user click.

You can also launch demo sessions (no player credentials required):

const demo = await safeSdkRequest(() => aggregator.sessions.createDemo({
game_id: "d4f7a2b1-3c8e-4f5a-9b6d-1e2f3a4b5c6d",
}));
// demo.game_url opens the game in free-play mode

When a player places a bet or receives a win, The Aggregator sends an HTTPS callback to the URL configured for your organization. Where your deployment exposes Organization → Callback configuration, use those settings; confirm the available controls before testing. Keep the signing secret separate from the API key.

The Aggregator sends a POST request with a JSON body:

{
"action": "BET-WIN",
"transaction_type": "bet",
"transaction_id": "tx_bet_001",
"amount": 500,
"currency": "EUR",
"player_id": "player_42",
"provider_code": "truelabs",
"game": "book-of-sun",
"game_id": "d4f7a2b1-3c8e-4f5a-9b6d-1e2f3a4b5c6d",
"round_id": "round_abc",
"finished": false,
"session_id": "8c8e8c8e-8c8e-4c8e-8c8e-8c8e8c8e8c8e"
}

Key fields (full reference in the wallet callbacks guide):

  • Branch on transaction_type (bet / win / refund) — not action, which is a provider-specific label ("BET-WIN", "BET", …).
  • amount is an integer in minor units; supported zero-win settlements may have amount: 0 (e.g. 500 = 5 EUR) — the same unit as the response balance below. The minor unit is determined by the ISO-4217 currency: 2-decimal currencies (EUR/USD) use 1/100 (500 = €5.00), 0-decimal currencies (JPY/KRW) are 1:1 (278 = ¥278), and 3-decimal currencies (KWD/BHD/OMR) use 1/1000.
  • game_id is The Aggregator catalog UUID; game is the provider’s own slug.
  • finished is true on the final callback of a round.

Transaction types (the transaction_type field):

transaction_type Description Your action
bet Player placed a bet Debit paid bets only; on is_free: true, do not debit the player’s real-money balance
win Player won Credit amount to the player’s balance
refund A previous bet was reversed Reverse the original recorded wallet debit once; reconcile a missing or ambiguous original outcome

Verify the exact raw request bytes with the reviewed signature helpers. They reject missing or malformed signatures and missing or empty protected environment secrets before comparison. Keep the callback secret separate from the API key. Node exposes the X-SIGNATURE header as req.headers["x-signature"].

Verify before parsing or changing money. Keep raw bodies, headers and secrets out of errors and logs; use fixed-code diagnostics.

Your endpoint must return HTTP 200 with a JSON body containing balance and currency (both required — a balance-only body is rejected), and echo the player_id:

Success (200):

{
"balance": 9500,
"currency": "EUR",
"player_id": "player_42"
}

balance is the player’s new balance in minor units (cents — e.g. 9500 = 95.00 EUR). This is the same denomination as the request amount (an integer in minor units, 500 = €5.00), so you can apply amount to balance directly with no unit conversion. currency must match the request.

Error responses:

Status When to use
200 Transaction processed successfully. Return the new balance.
402 Insufficient funds. Only valid for bet transactions. The bet is not placed.
500 Internal error. Retry eligibility follows the deployed delivery policy.

A duplicate of the full scoped transaction identity is not a distinct status — it is an idempotent retry. Replay your original response (same HTTP status and balance you returned the first time — 200 for a settled bet), never a fresh 409. See Handle idempotency.

Use the full integration/organization, provider_code and transaction_id as the wallet transaction identity. Provider IDs are opaque and are not globally unique.

  1. Validate the signed callback against the transaction and provider contract. On a free bet with is_free: true, do not debit the player’s real-money balance.
  2. For a duplicate, replay the stored original status and body without another wallet movement. Never rebuild that response from values in a later request.
  3. Atomically commit the wallet change and its stored original status and body before returning success. A separate balance update followed by a separate result save is not sufficient.
  4. Reverse only the original recorded wallet debit for a refund, once. Reconcile a missing or ambiguous original outcome; a free bet must not create an extra real-money refund credit.

Implement these guarantees in your persistent ledger using the callback integration requirements; a short in-memory endpoint does not provide them. A timeout does not prove the wallet change failed: reconcile before recovery.

For full callback documentation, see the wallet callbacks guide.


Run real-session tests only after deployment access, providers, callbacks and funding or agreed credits are enabled. Use authorized test players and reconcile every movement. See Testing your integration for the recommended sequence.

  • Game catalog – List games and verify filtering works
  • Session creation – Create a session and confirm you receive a valid game_url
  • Demo session – Create a demo session and verify free-play mode works (no wallet movement)
  • Bet callback – Verify paid bets debit once and free bets do not debit real money
  • Win callback – Verify your endpoint credits the correct amount
  • Refund callback – Verify your endpoint reverses a bet correctly
  • Insufficient funds – Send a bet larger than the player’s balance and confirm you return 402
  • Idempotency – Replay the same integration/organization, provider_code and transaction_id and confirm the stored original status and body; the same ID from a different provider must remain distinct
  • Signature verification – Send a request with a tampered signature and confirm your endpoint rejects it with 401
  • Timeout handling – Confirm your callback endpoint responds within 5 seconds under load
  • Blocked country – Attempt to create a session for a blocked country and confirm you receive 403

You can inspect all your transactions through the API:

const { transactions } = await safeSdkRequest(() => aggregator.transactions.list({
page: 1,
per_page: 100,
}));

Or view them in the operator cabinet under Transactions.


After validating the full callback path, confirm the applicable launch requirements and approvals before opening customer traffic. Technical checks and any welcome credits do not themselves authorize launch.

API and authentication:

  • Your issued API key and separate callback secret are stored in a secrets manager, not in code
  • Where supported, CI/integration runs use a separate credential with permissions appropriate to those runs
  • Your production callback URL is configured in the operator cabinet

Callback endpoint:

  • Endpoint is publicly accessible over HTTPS (TLS 1.2+)
  • Signature verification is enabled and tested
  • Idempotency handling is implemented and tested
  • Endpoint responds within 5 seconds under expected load
  • Error responses follow the documented status codes (200, 402, 500); duplicates replay the original response

Reliability:

  • Callback endpoint has monitoring and alerting configured
  • You have a process for investigating and resolving failed callbacks
  • Your database transactions for balance updates are atomic (no partial updates)
  • Your protected ledger retains validated transaction records for reconciliation; operational diagnostics use fixed codes and locally generated correlation IDs, without raw request values or secrets

Compliance:

  • You respect blocked_countries and do not launch games for restricted jurisdictions
  • Player balances are displayed in the correct currency and denomination
  • You have completed KYB verification and your operator account is approved for production

Final verification:

  • Run one real session with a small balance to confirm the full flow works in production
  • Verify callback delivery by checking the transaction in both your system and the operator cabinet

  • Full API Reference – Complete endpoint documentation with request/response schemas
  • Wallet Callback Guide – Detailed callback specification, retry behavior, and edge cases
  • TypeScript SDK 1.0.0 — install @aggregator-gg/sdk@1.0.0 from npm; requires Node.js 24 or newer. SDK 1.1.0 is not required for these examples.