Skip to content

Code samples

The five operations every operator integration needs, in the three languages we ship hand-curated samples for. TypeScript, PHP, and Python — chosen because they cover ~85% of incumbent B2B casino backend stacks. For Java, Go, C#, Ruby, Kotlin, Rust — generate clients from our OpenAPI spec with openapi-generator-cli.

The minimal “is auth working” call. List a single page of games for your enabled providers.

const res = await fetch(
"https://api.aggregator.gg/v1/games?per_page=10",
{
headers: { Authorization: `Bearer ${process.env.AGGREGATOR_API_KEY}` },
},
);
if (!res.ok) {
throw new Error(`list games failed: ${res.status} ${await res.text()}`);
}
const { games, total } = await res.json();
console.log(`Got ${games.length} of ${total} games`);

Real-money game launch. Requires an Idempotency-Key header — see Idempotency for the why.

import { randomUUID } from "node:crypto";
const idempotencyKey = `launch-${playerId}-${spinId}`; // stable for retries
const res = await fetch("https://api.aggregator.gg/v1/sessions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AGGREGATOR_API_KEY}`,
"Idempotency-Key": idempotencyKey,
"Content-Type": "application/json",
},
body: JSON.stringify({
// The Aggregator catalog UUID (the `id` from GET /v1/games) — NOT the
// provider's own game code (provider_game_id).
game_id: "d4f7a2b1-3c8e-4f5a-9b6d-1e2f3a4b5c6d",
player_id: playerId,
balance: 10_000, // minor units — 100.00 EUR
currency: "EUR",
country: "DE",
lang: "de",
return_url: "https://your-casino.example/lobby",
}),
});
if (!res.ok) {
throw new Error(`create session failed: ${res.status}`);
}
const { session_id, game_url } = await res.json();
// Redirect the player to game_url immediately — it is single-use.

The single highest-leverage snippet on this site. Copy verbatim. Full guide and replay-protection guidance: Signature verification.

The platform sends an X-SIGNATURE header containing a hex-encoded HMAC-SHA256 digest of the raw body, keyed with your callback_secret. No sha256= prefix.

import crypto from "node:crypto";
export function verifyAggregatorSignature(
rawBody: Buffer,
signatureHeader: string | undefined,
): boolean {
if (!signatureHeader) return false;
const expected = crypto
.createHmac("sha256", process.env.AGGREGATOR_CALLBACK_SECRET!)
.update(rawBody)
.digest("hex");
// Guard against Node's timingSafeEqual throwing on length mismatch.
if (expected.length !== signatureHeader.length) return false;
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(signatureHeader, "hex"),
);
}

Receive a bet/win callback, verify signature, debit/credit your wallet, respond with the new balance. Pseudo-wallet helpers (debit, credit) are placeholders for your real ledger code.

// Express
import express from "express";
const app = express();
app.post(
"/aggregator/callback",
express.raw({ type: "application/json" }), // raw body for HMAC
async (req, res) => {
const rawBody = req.body as Buffer;
const signature = req.header("X-SIGNATURE");
if (!verifyAggregatorSignature(rawBody, signature)) {
return res.status(401).json({ error: "invalid_signature" });
}
const event = JSON.parse(rawBody.toString("utf8"));
let newBalance: number;
try {
if (event.transaction_type === "bet") {
newBalance = await wallet.debit(event.player_id, event.amount, event.transaction_id);
} else if (event.transaction_type === "win") {
newBalance = await wallet.credit(event.player_id, event.amount, event.transaction_id);
} else if (event.transaction_type === "refund") {
// A refund reverses the round's original bet — credit it back.
newBalance = await wallet.credit(event.player_id, event.amount, event.transaction_id);
} else {
return res.status(400).json({ error: "unknown_transaction_type" });
}
} catch (e) {
if (e.code === "INSUFFICIENT_BALANCE") {
return res.status(402).json({ error: "insufficient_balance" });
}
throw e;
}
// The Aggregator requires balance + currency; player_id is echoed back.
// balance is the player's NEW balance in minor units (cents).
res.json({ balance: newBalance, currency: event.currency, player_id: event.player_id });
},
);

Every API error follows the { error: { code, message, request_id, details } } envelope. See Error codes for the full code list.

async function callAggregator(path: string, init?: RequestInit) {
const res = await fetch(`https://api.aggregator.gg/v1${path}`, {
...init,
headers: {
Authorization: `Bearer ${process.env.AGGREGATOR_API_KEY}`,
...init?.headers,
},
});
if (res.ok) return res.json();
let payload;
try {
payload = await res.json();
} catch {
throw new AggregatorError(res.status, "E0000", "non_json_response", null);
}
throw new AggregatorError(
res.status,
payload.error?.code,
payload.error?.message,
payload.error?.request_id,
);
}
class AggregatorError extends Error {
constructor(
public httpStatus: number,
public code: string,
message: string,
public requestId: string | null,
) {
super(`[${code}] ${message} (request_id=${requestId})`);
}
}
// Use:
try {
await callAggregator("/sessions", { method: "POST", body: ... });
} catch (e) {
if (e instanceof AggregatorError && e.code === "E3004") {
// Player self-excluded — show a soft message, not "API error"
} else {
throw e;
}
}

For Java, Go, C#, Ruby, Kotlin, Rust — generate a client from the spec:

Terminal window
npx @openapitools/openapi-generator-cli generate \
-i https://raw.githubusercontent.com/aggregator-gg/docs/main/openapi/aggregator.yaml \
-g python \ # or java, go, csharp, ruby, kotlin, rust, ...
-o ./aggregator-client

The generator supports 30+ languages. Output is a typed client with request/response models matching the spec exactly.