@aggregator-gg/sdk
v1.0.0
Published
TypeScript SDK for The Aggregator machine API
Maintainers
Readme
@aggregator-gg/sdk
TypeScript SDK for integrating with The Aggregator iGaming API -- browse games, launch sessions, and query transactions with a single client.
Package availability is not operational approval. Installing this SDK does not authorize a Core consumer switch, production use, live-money readiness, or any change to the Aggregator runtime. Use
sk_test_*credentials for integration work until those separate gates are approved.
Why This Exists
Integrating a game aggregator API means managing authentication, pagination, error handling, and multiple resource endpoints. This SDK wraps all of that into a typed, promise-based client so you can ship your integration in hours instead of days.
Quick Start
Install the public package from npm. Repository contributors validating an
unpublished candidate should instead use this repository's locked workspace
and npm ci; repository source or a green build does not authorize publication.
Run the SDK only in a trusted server-side Node.js process. Never bundle an
Aggregator API key into browser or mobile code. Your backend may return the
resulting game_url to an authenticated frontend, which can then navigate the
player without receiving the API key.
import { Aggregator } from "@aggregator-gg/sdk";
const apiKey = process.env.AGGREGATOR_API_KEY;
if (!apiKey) {
throw new Error("AGGREGATOR_API_KEY is required");
}
const aggregator = new Aggregator({
apiKey, // use an sk_test_* key for integration/sandbox work
});
// List available games
const { games } = await aggregator.games.list({ per_page: 10 });
const game = games[0];
if (!game) {
throw new Error("No games are available");
}
console.log(game.name); // "Book of Sun"
// Launch a no-money integration demo
const session = await aggregator.sessions.createDemo({
game_id: game.id,
});
console.log(`Created demo session ${session.session_id}`);
// Return { game_url: session.game_url } from your authenticated backend route.Installation
Prerequisites: Node.js 24+
npm install @aggregator-gg/sdk
# or
yarn add @aggregator-gg/sdk
# or
pnpm add @aggregator-gg/sdkThe package ships ESM and CommonJS builds. TypeScript type definitions are included -- no @types package needed.
Configuration
Create a client by passing an AggregatorConfig object to the constructor:
import { Aggregator } from "@aggregator-gg/sdk";
const apiKey = process.env.AGGREGATOR_API_KEY;
if (!apiKey) {
throw new Error("AGGREGATOR_API_KEY is required");
}
const aggregator = new Aggregator({
apiKey,
baseUrl: "https://api.aggregator.gg/v1", // optional, this is the default
fetch: customFetch, // optional, for custom environments
timeoutMs: 30_000, // optional, per attempt
retry: { maxRetries: 2 }, // optional; false disables retries
});For a non-production environment, pass the HTTPS base URL supplied by the Aggregator operator. This public package does not publish private staging hostnames.
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| apiKey | string | (required) | Use an sk_test_* key for integration/sandbox work. Use sk_live_* only after readiness approval. |
| baseUrl | string | https://api.aggregator.gg/v1 | Override the API base URL. Arbitrary HTTPS origins are supported for private deployments. Credentials, query strings, and fragments are rejected; plain HTTP is limited to exact loopback hosts. |
| fetch | typeof fetch | globalThis.fetch | Custom fetch implementation. Useful for testing or environments without a global fetch. |
| timeoutMs | number | 30000 | Per-attempt timeout, from 1 ms through 10 minutes. Long-running clients such as readiness orchestration must set an explicit larger budget and still pass an outer AbortSignal. |
| retry | RetryConfig \| false | 2 retries | Bounded retry policy. Only safe GETs and idempotency-keyed environment-bound session creation are retried. |
| userAgent | string | unset | Optional product identifier. Control characters are rejected. |
Every public operation accepts a caller AbortSignal. Automatic retries honor
Retry-After (delta seconds or an HTTP date), clamp the delay to the configured
maximum, and stop immediately when the caller aborts. Demo-session POSTs are
never replayed. A route-allowlisted request() compatibility surface exists
for the SDK-owned MCP package, but it accepts no caller headers, rejects
unknown method/path pairs before fetch, and never retries POSTs. Environment-bound
session creation must use sessions.create() so idempotency cannot be
bypassed. The
readiness compatibility POST accepts only the complete
{ provider_code, mode: "direct_wallet", currency, amount } body; unknown,
missing, or malformed fields are rejected before fetch.
Environments
The key tiers have distinct safety boundaries:
- Product exploration uses the separate keyless demo front door.
- Machine integration and sandbox calls use
sk_test_*; these keys cannot enter the live-money path. sk_live_*keys are issued only after readiness and are required for live money-bearing session operations.- The same
sessions.create()method selects the sandbox or production environment from the API key; there is no caller-supplied environment switch.
The current Core OpenAPI still describes POST /sessions as real-money only,
while the current Core implementation carries a test/live environment on the
authenticated identity. This package preserves that implementation-compatible
shape, but it is not deployment proof. Do not rely on sk_test_* session
semantics until the target runtime has been verified separately, and never use
an sk_live_* key for exploratory validation.
const apiKey = process.env.AGGREGATOR_API_KEY;
if (!apiKey) {
throw new Error("AGGREGATOR_API_KEY is required");
}
const aggregator = new Aggregator({ apiKey });Usage
The SDK exposes three sub-clients: games, sessions, and transactions.
Games
List games
Retrieve a paginated list of games with optional filters.
const { games, total, page, per_page } = await aggregator.games.list({
sub_operator_ref: "brand-01", // platform accounts only
provider: "truelabs",
type: "slots",
volatility: "high",
rtp_min: 95,
rtp_max: 97,
features: "free_spins",
search: "book",
sort: "name",
page: 1,
per_page: 25,
currency: "EUR",
});Filter parameters:
| Parameter | Type | Description |
|-----------|------|-------------|
| sub_operator_ref | string | Platform accounts only: brand storefront selector. Omit for single-casino/default-brand use; never send an empty value. |
| provider | string | Filter by provider code (e.g. "truelabs", "bgaming") |
| provider_game_id | string | Filter by the provider's own game identifier |
| type | string | Filter by game type (e.g. "slots", "table", "crash") |
| volatility | string | Filter by volatility level (e.g. "low", "medium", "high") |
| rtp_min | number | Minimum RTP percentage (inclusive) |
| rtp_max | number | Maximum RTP percentage (inclusive) |
| features | string | Filter by game features (e.g. "free_spins", "bonus_buy") |
| search | string | Full-text search across game names and brands |
| sort | string | Sort field (e.g. "name", "rtp", "release_date") |
| page | number | Page number (1-based) |
| per_page | number | Results per page (1–200) |
| currency | string | ISO-4217 currency filter |
Get a single game
const game = await aggregator.games.get("d4f7a2b1-3c8e-4f5a-9b6d-1e2f3a4b5c6d");
console.log(game.name); // "Book of Sun"
console.log(game.provider_code); // "truelabs"
console.log(game.rtp); // 96.5
console.log(game.has_demo); // true
console.log(game.blocked_countries); // ["US", "GB"]Game object properties:
| Property | Type | Description |
|----------|------|-------------|
| id | string | Unique game identifier |
| provider_game_id | string | Provider's own game ID |
| provider_code | string | Provider code (e.g. "truelabs") |
| name | string | Display name |
| brand | string? | Game brand or studio |
| category | string | Game category |
| game_type | string? | Game type (slots, table, crash, etc.) |
| rtp | number? | Return to player percentage |
| volatility | "low" \| "medium" \| "high"? | Volatility level |
| has_mobile | boolean? | Mobile-compatible |
| has_desktop | boolean? | Desktop-compatible |
| has_demo | boolean | Demo mode available |
| thumbnail_url | string? | Game thumbnail image URL |
| free_rounds_support | boolean? | Whether free-round grants are supported |
| blocked_countries | string[]? | ISO country codes where the game is unavailable |
| certified_markets | CertifiedMarkets \| null? | Informational provider certification metadata; not a launch gate |
| features | string[]? | Game features (free spins, bonus buy, etc.) |
| release_date | string? | ISO date string |
| supported_currencies | string[]? | Effective declared launch currencies |
| min_bet, max_bet, default_bet | number \| null? | Provider-declared limits in bet_limits_currency |
| max_win_multiplier | number \| null? | Provider-declared maximum win multiple |
| bet_steps | GameBetSteps \| null? | Discrete stake ladder when available |
Sessions
Create an environment-bound player session
Launch a game for a player. The response includes a game_url you redirect the
player to. The API key selects the environment:
sk_test_*creates a sandbox session with provider test credentials; it does not bill or settle live money.sk_live_*creates a production real-money session and is issued only after readiness approval.
Live-money warning: A call made with
sk_live_*enters the production money path. Keep integration and exploratory calls onsk_test_*.
The SDK automatically sends an Idempotency-Key header for this call. Pass
{ idempotencyKey } as the second argument if you want to control retries
explicitly from your side.
const session = await aggregator.sessions.create({
game_id: "d4f7a2b1-3c8e-4f5a-9b6d-1e2f3a4b5c6d",
player_id: "player_42",
balance: 10000, // 100.00 EUR in cents
currency: "EUR",
country: "DE",
lang: "de", // optional, game UI language
return_url: "https://your-casino.com/lobby", // optional, where the player returns after closing the game
sub_operator_ref: "brand-01", // platform accounts only
});
console.log(session.session_id); // "8c8e8c8e-8c8e-4c8e-8c8e-8c8e8c8e8c8e"
console.log(session.game_url); // "https://..."
console.log(session.provider_session_uid); // provider reference when returned
console.log(session.provider_code); // optional on create responses| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| game_id | string | Yes | The Aggregator catalog UUID (id from GET /v1/games), not the provider's provider_game_id |
| player_id | string | Yes | Your unique player identifier |
| balance | number | Yes | Player balance in minor currency units (cents); sandbox balance with sk_test_*, live balance with sk_live_* |
| currency | string | Yes | 3–8 ASCII letters; outer spaces/tabs and either case are accepted, then sent uppercase |
| country | string | Yes | Two ASCII letters; outer spaces/tabs and either case are accepted, then sent uppercase |
| lang | string | No | Two ASCII letters; outer spaces/tabs and either case are accepted, then sent lowercase |
| return_url | string | No | URL the player is redirected to after closing the game |
| sub_operator_ref | string | No | Platform brand selector. Use the same non-empty ref for catalog list, launch, and lookup. |
Punctuation, wrong lengths, inner whitespace, and control characters in the three code fields are rejected before any network call. Normalization returns a fresh request body and does not mutate the caller's object.
const session = await aggregator.sessions.create(
{
game_id: "d4f7a2b1-3c8e-4f5a-9b6d-1e2f3a4b5c6d",
player_id: "player_42",
balance: 10000,
currency: "EUR",
country: "DE",
},
{ idempotencyKey: "launch-player-42-round-001" },
);Create a demo session
Launch a game in free-play mode. No player credentials or balance required.
const demo = await aggregator.sessions.createDemo({
game_id: "d4f7a2b1-3c8e-4f5a-9b6d-1e2f3a4b5c6d",
});
console.log(demo.session_id); // "8c8e8c8e-8c8e-4c8e-8c8e-8c8e8c8e8c8e"
console.log(demo.game_url); // "https://..."Get session details
Retrieve the current state of a session.
const session = await aggregator.sessions.get(
"8c8e8c8e-8c8e-4c8e-8c8e-8c8e8c8e8c8e",
{ sub_operator_ref: "brand-01" }, // platform accounts only
);
console.log(session.status); // "active" | "completed" | "expired" | "error"
console.log(session.created_at); // ISO-8601 timestampThe SDK accepts both the canonical unwrapped session-detail response and the
legacy { "session": ... } envelope during Core cutover. An id-only legacy
response is normalized so SessionDetail always exposes matching id and
session_id; conflicting identifiers fail closed.
Transactions
List transactions
Retrieve a paginated list of transactions.
const { transactions, limit, offset } =
await aggregator.transactions.list({
page: 2,
per_page: 50,
});
for (const tx of transactions) {
console.log(tx.id, tx.type, tx.amount, tx.currency);
// "tx_001" "bet" <Core-provided amount> "EUR"
}The current API implements limit (1–100) and offset. For convenience, the
SDK also accepts page and per_page (1–100) and translates them to those
wire parameters; do not mix the two families. The current runtime ignores the
documented brand/session/player/type/time filters, so this SDK does not expose
or send them. Current responses do not promise total, page, or per_page.
The SDK normalizes the current transaction_type response field into type.
The deployed handler returns its stored amount without a normalized string
alias. The SDK preserves that number | string value and does not guess a
major-unit conversion. Do not use this endpoint for exact financial
reconciliation until Core publishes and deploys one authoritative amount/unit
contract.
Transaction object properties:
| Property | Type | Description |
|----------|------|-------------|
| id | string | Unique transaction identifier |
| session_id | string | Associated session |
| type | string | Transaction type ("bet", "win", "refund") |
| amount | number \| string | Current database-shaped value; no normalized unit/string alias is guaranteed |
| currency | string | ISO 4217 currency code |
| provider_transaction_id | string? | Provider-side transaction identifier when available |
| provider_round_id | string? | Canonical provider round identifier |
| status | string? | Processing state: pending, forwarded, completed, failed, or duplicate |
| environment | "test" \| "live"? | Ledger environment |
| operator_status | number \| null? | Operator callback HTTP status |
| operator_response | object \| null? | Authenticated operator wallet response body |
| processing_time_ms | number \| null? | Callback processing latency in milliseconds |
| error_message | string \| null? | Stored callback error when processing failed |
| created_at | string | ISO 8601 timestamp |
Error Handling
All API errors throw an AggregatorError with structured details.
import { Aggregator, AggregatorError } from "@aggregator-gg/sdk";
try {
await aggregator.games.list({
per_page: 25,
}, {
signal: AbortSignal.timeout(5_000),
});
await aggregator.sessions.create({
game_id: "00000000-0000-4000-8000-000000000000",
player_id: "player_42",
balance: 10000,
currency: "EUR",
country: "DE",
});
} catch (err) {
if (err instanceof AggregatorError) {
console.error(err.status); // 404
console.error(err.message); // "Aggregator API request failed with status 404"
console.error(err.code); // "GAME_NOT_FOUND" (or null if not provided)
console.error(err.body); // full response body for debugging
}
}AggregatorError properties:
| Property | Type | Description |
|----------|------|-------------|
| message | string | Generic status-based description that does not relay untrusted upstream detail |
| status | number | HTTP status code |
| code | string \| null | Machine-readable error code from the API, if available |
| body | unknown | Full response body for debugging |
Common error codes:
| Status | Meaning | What to do |
|--------|---------|------------|
| 400 | Bad request -- missing or invalid parameters | Check your request body against the parameter tables above |
| 401 | Invalid API key | Verify your apiKey is correct and has not been revoked |
| 403 | Forbidden -- key lacks permission for this resource | Ensure you are using a key with the right scope |
| 404 | Resource not found | Check the ID you passed (game, session, etc.) |
| 429 | Rate limit exceeded | Back off and retry after the Retry-After header interval |
TypeScript Support
The SDK is written in TypeScript and ships type definitions for every interface and method. All types are exported from the package root:
import type {
Game,
ListGamesParams,
ListGamesResponse,
CreatedSession,
SessionDetail,
CreateSessionBody,
DemoSession,
CreateDemoSessionBody,
Transaction,
ListTransactionsParams,
ListTransactionsResponse,
AggregatorConfig,
RequestOptions,
RetryConfig,
} from "@aggregator-gg/sdk";Requirements
- Node.js 24+ (uses
globalThis.fetchby default)
Security
Report vulnerabilities privately to the Aggregator Security Team. Do not disclose vulnerability details or credentials in a public issue. The team aims to acknowledge reports within 3 business days.
License
MIT
