{"_id":"@aggregator-gg/sdk","name":"@aggregator-gg/sdk","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@aggregator-gg/sdk","version":"1.0.0","description":"TypeScript SDK for The Aggregator machine API","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"publishConfig":{"access":"public","tag":"latest"},"repository":{"type":"git","url":"git+https://github.com/aggregator-gg/developer-tools.git","directory":"packages/sdk"},"scripts":{"build":"npm run clean && tsup","dev":"tsup --watch","typecheck":"tsc --noEmit","clean":"rm -rf dist","test":"node --test ../../test/sdk.test.mjs","prepack":"node ../../scripts/verify-contract.mjs","prepublishOnly":"npm run build && node ../../scripts/verify-contract.mjs"},"devDependencies":{"tsup":"8.5.1","typescript":"5.9.3"},"keywords":["aggregator","igaming","sdk","api-client"],"license":"MIT","engines":{"node":">=24.0.0"},"_id":"@aggregator-gg/sdk@1.0.0","bugs":{"url":"https://github.com/aggregator-gg/developer-tools/issues"},"homepage":"https://github.com/aggregator-gg/developer-tools#readme","_integrity":"sha512-+XEwKYqC9tY6iKBwVrTeQ4/Gk22uG5FIhjimsIv2sb361/r3ljFGJUWzb4waTqfgUSFsoswH0OM1roaPWtULmQ==","_resolved":"/Users/dandrianov/Projects/aggregator-gg/.release-candidates/developer-tools/sdk-v1.0.0/run-30708095278-attempt-1/aggregator-gg-sdk-1.0.0.tgz","_from":"file:/Users/dandrianov/Projects/aggregator-gg/.release-candidates/developer-tools/sdk-v1.0.0/run-30708095278-attempt-1/aggregator-gg-sdk-1.0.0.tgz","_nodeVersion":"24.18.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-+XEwKYqC9tY6iKBwVrTeQ4/Gk22uG5FIhjimsIv2sb361/r3ljFGJUWzb4waTqfgUSFsoswH0OM1roaPWtULmQ==","shasum":"12adfb8c3bb6c6398d67d106cb4e50e36becd105","tarball":"https://registry.npmjs.org/@aggregator-gg/sdk/-/sdk-1.0.0.tgz","fileCount":7,"unpackedSize":114609,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIF14N2Uu2cPC4Kcv/Ibej4FVr3YTZ4/nRHKSSN0QdLqHAiAX2r+sRzk8yW26DfZvRnZBMIGnhr27j+n9faOW58Yhgw=="}]},"_npmUser":{"name":"gregator","email":"gregator@proton.me"},"directories":{},"maintainers":[{"name":"gregator","email":"gregator@proton.me"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sdk_1.0.0_1785607349182_0.42067998577532384"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-01T18:02:28.996Z","1.0.0":"2026-08-01T18:02:29.329Z","modified":"2026-08-01T18:02:29.589Z"},"maintainers":[{"name":"gregator","email":"gregator@proton.me"}],"description":"TypeScript SDK for The Aggregator machine API","homepage":"https://github.com/aggregator-gg/developer-tools#readme","keywords":["aggregator","igaming","sdk","api-client"],"repository":{"type":"git","url":"git+https://github.com/aggregator-gg/developer-tools.git","directory":"packages/sdk"},"bugs":{"url":"https://github.com/aggregator-gg/developer-tools/issues"},"license":"MIT","readme":"# @aggregator-gg/sdk\n\n> TypeScript SDK for integrating with The Aggregator iGaming API -- browse games, launch sessions, and query transactions with a single client.\n\n> **Package availability is not operational approval.** Installing this SDK\n> does not authorize a Core consumer switch, production use, live-money\n> readiness, or any change to the Aggregator runtime. Use `sk_test_*`\n> credentials for integration work until those separate gates are approved.\n\n## Why This Exists\n\nIntegrating 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.\n\n## Quick Start\n\nInstall the public package from npm. Repository contributors validating an\nunpublished candidate should instead use this repository's locked workspace\nand `npm ci`; repository source or a green build does not authorize publication.\n\nRun the SDK only in a trusted server-side Node.js process. Never bundle an\nAggregator API key into browser or mobile code. Your backend may return the\nresulting `game_url` to an authenticated frontend, which can then navigate the\nplayer without receiving the API key.\n\n```typescript\nimport { Aggregator } from \"@aggregator-gg/sdk\";\n\nconst apiKey = process.env.AGGREGATOR_API_KEY;\nif (!apiKey) {\n  throw new Error(\"AGGREGATOR_API_KEY is required\");\n}\n\nconst aggregator = new Aggregator({\n  apiKey, // use an sk_test_* key for integration/sandbox work\n});\n\n// List available games\nconst { games } = await aggregator.games.list({ per_page: 10 });\nconst game = games[0];\nif (!game) {\n  throw new Error(\"No games are available\");\n}\nconsole.log(game.name); // \"Book of Sun\"\n\n// Launch a no-money integration demo\nconst session = await aggregator.sessions.createDemo({\n  game_id: game.id,\n});\n\nconsole.log(`Created demo session ${session.session_id}`);\n// Return { game_url: session.game_url } from your authenticated backend route.\n```\n\n## Installation\n\n**Prerequisites**: Node.js 24+\n\n```bash\nnpm install @aggregator-gg/sdk\n# or\nyarn add @aggregator-gg/sdk\n# or\npnpm add @aggregator-gg/sdk\n```\n\nThe package ships ESM and CommonJS builds. TypeScript type definitions are included -- no `@types` package needed.\n\n## Configuration\n\nCreate a client by passing an `AggregatorConfig` object to the constructor:\n\n```typescript\nimport { Aggregator } from \"@aggregator-gg/sdk\";\n\nconst apiKey = process.env.AGGREGATOR_API_KEY;\nif (!apiKey) {\n  throw new Error(\"AGGREGATOR_API_KEY is required\");\n}\n\nconst aggregator = new Aggregator({\n  apiKey,\n  baseUrl: \"https://api.aggregator.gg/v1\", // optional, this is the default\n  fetch: customFetch,                      // optional, for custom environments\n  timeoutMs: 30_000,                       // optional, per attempt\n  retry: { maxRetries: 2 },                // optional; false disables retries\n});\n```\n\nFor a non-production environment, pass the HTTPS base URL supplied by the\nAggregator operator. This public package does not publish private staging\nhostnames.\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `apiKey` | `string` | **(required)** | Use an `sk_test_*` key for integration/sandbox work. Use `sk_live_*` only after readiness approval. |\n| `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. |\n| `fetch` | `typeof fetch` | `globalThis.fetch` | Custom fetch implementation. Useful for testing or environments without a global `fetch`. |\n| `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`. |\n| `retry` | `RetryConfig \\| false` | 2 retries | Bounded retry policy. Only safe GETs and idempotency-keyed environment-bound session creation are retried. |\n| `userAgent` | `string` | unset | Optional product identifier. Control characters are rejected. |\n\nEvery public operation accepts a caller `AbortSignal`. Automatic retries honor\n`Retry-After` (delta seconds or an HTTP date), clamp the delay to the configured\nmaximum, and stop immediately when the caller aborts. Demo-session POSTs are\nnever replayed. A route-allowlisted `request()` compatibility surface exists\nfor the SDK-owned MCP package, but it accepts no caller headers, rejects\nunknown method/path pairs before fetch, and never retries POSTs. Environment-bound\nsession creation must use `sessions.create()` so idempotency cannot be\nbypassed. The\nreadiness compatibility POST accepts only the complete\n`{ provider_code, mode: \"direct_wallet\", currency, amount }` body; unknown,\nmissing, or malformed fields are rejected before fetch.\n\n## Environments\n\nThe key tiers have distinct safety boundaries:\n\n- Product exploration uses the separate keyless demo front door.\n- Machine integration and sandbox calls use `sk_test_*`; these keys cannot\n  enter the live-money path.\n- `sk_live_*` keys are issued only after readiness and are required for live\n  money-bearing session operations.\n- The same `sessions.create()` method selects the sandbox or production\n  environment from the API key; there is no caller-supplied environment switch.\n\nThe current Core OpenAPI still describes `POST /sessions` as real-money only,\nwhile the current Core implementation carries a test/live environment on the\nauthenticated identity. This package preserves that implementation-compatible\nshape, but it is not deployment proof. Do not rely on `sk_test_*` session\nsemantics until the target runtime has been verified separately, and never use\nan `sk_live_*` key for exploratory validation.\n\n```typescript\nconst apiKey = process.env.AGGREGATOR_API_KEY;\nif (!apiKey) {\n  throw new Error(\"AGGREGATOR_API_KEY is required\");\n}\nconst aggregator = new Aggregator({ apiKey });\n```\n\n## Usage\n\nThe SDK exposes three sub-clients: `games`, `sessions`, and `transactions`.\n\n### Games\n\n#### List games\n\nRetrieve a paginated list of games with optional filters.\n\n```typescript\nconst { games, total, page, per_page } = await aggregator.games.list({\n  sub_operator_ref: \"brand-01\", // platform accounts only\n  provider: \"truelabs\",\n  type: \"slots\",\n  volatility: \"high\",\n  rtp_min: 95,\n  rtp_max: 97,\n  features: \"free_spins\",\n  search: \"book\",\n  sort: \"name\",\n  page: 1,\n  per_page: 25,\n  currency: \"EUR\",\n});\n```\n\n**Filter parameters:**\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `sub_operator_ref` | `string` | Platform accounts only: brand storefront selector. Omit for single-casino/default-brand use; never send an empty value. |\n| `provider` | `string` | Filter by provider code (e.g. `\"truelabs\"`, `\"bgaming\"`) |\n| `provider_game_id` | `string` | Filter by the provider's own game identifier |\n| `type` | `string` | Filter by game type (e.g. `\"slots\"`, `\"table\"`, `\"crash\"`) |\n| `volatility` | `string` | Filter by volatility level (e.g. `\"low\"`, `\"medium\"`, `\"high\"`) |\n| `rtp_min` | `number` | Minimum RTP percentage (inclusive) |\n| `rtp_max` | `number` | Maximum RTP percentage (inclusive) |\n| `features` | `string` | Filter by game features (e.g. `\"free_spins\"`, `\"bonus_buy\"`) |\n| `search` | `string` | Full-text search across game names and brands |\n| `sort` | `string` | Sort field (e.g. `\"name\"`, `\"rtp\"`, `\"release_date\"`) |\n| `page` | `number` | Page number (1-based) |\n| `per_page` | `number` | Results per page (1–200) |\n| `currency` | `string` | ISO-4217 currency filter |\n\n#### Get a single game\n\n```typescript\nconst game = await aggregator.games.get(\"d4f7a2b1-3c8e-4f5a-9b6d-1e2f3a4b5c6d\");\n\nconsole.log(game.name);              // \"Book of Sun\"\nconsole.log(game.provider_code);     // \"truelabs\"\nconsole.log(game.rtp);               // 96.5\nconsole.log(game.has_demo);          // true\nconsole.log(game.blocked_countries); // [\"US\", \"GB\"]\n```\n\n**Game object properties:**\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `id` | `string` | Unique game identifier |\n| `provider_game_id` | `string` | Provider's own game ID |\n| `provider_code` | `string` | Provider code (e.g. `\"truelabs\"`) |\n| `name` | `string` | Display name |\n| `brand` | `string?` | Game brand or studio |\n| `category` | `string` | Game category |\n| `game_type` | `string?` | Game type (slots, table, crash, etc.) |\n| `rtp` | `number?` | Return to player percentage |\n| `volatility` | `\"low\" \\| \"medium\" \\| \"high\"?` | Volatility level |\n| `has_mobile` | `boolean?` | Mobile-compatible |\n| `has_desktop` | `boolean?` | Desktop-compatible |\n| `has_demo` | `boolean` | Demo mode available |\n| `thumbnail_url` | `string?` | Game thumbnail image URL |\n| `free_rounds_support` | `boolean?` | Whether free-round grants are supported |\n| `blocked_countries` | `string[]?` | ISO country codes where the game is unavailable |\n| `certified_markets` | `CertifiedMarkets \\| null?` | Informational provider certification metadata; not a launch gate |\n| `features` | `string[]?` | Game features (free spins, bonus buy, etc.) |\n| `release_date` | `string?` | ISO date string |\n| `supported_currencies` | `string[]?` | Effective declared launch currencies |\n| `min_bet`, `max_bet`, `default_bet` | `number \\| null?` | Provider-declared limits in `bet_limits_currency` |\n| `max_win_multiplier` | `number \\| null?` | Provider-declared maximum win multiple |\n| `bet_steps` | `GameBetSteps \\| null?` | Discrete stake ladder when available |\n\n### Sessions\n\n#### Create an environment-bound player session\n\nLaunch a game for a player. The response includes a `game_url` you redirect the\nplayer to. The API key selects the environment:\n\n- `sk_test_*` creates a sandbox session with provider test credentials; it does\n  not bill or settle live money.\n- `sk_live_*` creates a production real-money session and is issued only after\n  readiness approval.\n\n> **Live-money warning:** A call made with `sk_live_*` enters the production\n> money path. Keep integration and exploratory calls on `sk_test_*`.\n\nThe SDK automatically sends an `Idempotency-Key` header for this call. Pass\n`{ idempotencyKey }` as the second argument if you want to control retries\nexplicitly from your side.\n\n```typescript\nconst session = await aggregator.sessions.create({\n  game_id: \"d4f7a2b1-3c8e-4f5a-9b6d-1e2f3a4b5c6d\",\n  player_id: \"player_42\",\n  balance: 10000,        // 100.00 EUR in cents\n  currency: \"EUR\",\n  country: \"DE\",\n  lang: \"de\",            // optional, game UI language\n  return_url: \"https://your-casino.com/lobby\", // optional, where the player returns after closing the game\n  sub_operator_ref: \"brand-01\", // platform accounts only\n});\n\nconsole.log(session.session_id);           // \"8c8e8c8e-8c8e-4c8e-8c8e-8c8e8c8e8c8e\"\nconsole.log(session.game_url);             // \"https://...\"\nconsole.log(session.provider_session_uid); // provider reference when returned\nconsole.log(session.provider_code);        // optional on create responses\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `game_id` | `string` | Yes | The Aggregator catalog UUID (`id` from `GET /v1/games`), not the provider's `provider_game_id` |\n| `player_id` | `string` | Yes | Your unique player identifier |\n| `balance` | `number` | Yes | Player balance in minor currency units (cents); sandbox balance with `sk_test_*`, live balance with `sk_live_*` |\n| `currency` | `string` | Yes | 3–8 ASCII letters; outer spaces/tabs and either case are accepted, then sent uppercase |\n| `country` | `string` | Yes | Two ASCII letters; outer spaces/tabs and either case are accepted, then sent uppercase |\n| `lang` | `string` | No | Two ASCII letters; outer spaces/tabs and either case are accepted, then sent lowercase |\n| `return_url` | `string` | No | URL the player is redirected to after closing the game |\n| `sub_operator_ref` | `string` | No | Platform brand selector. Use the same non-empty ref for catalog list, launch, and lookup. |\n\nPunctuation, wrong lengths, inner whitespace, and control characters in the\nthree code fields are rejected before any network call. Normalization returns a\nfresh request body and does not mutate the caller's object.\n\n```typescript\nconst session = await aggregator.sessions.create(\n  {\n    game_id: \"d4f7a2b1-3c8e-4f5a-9b6d-1e2f3a4b5c6d\",\n    player_id: \"player_42\",\n    balance: 10000,\n    currency: \"EUR\",\n    country: \"DE\",\n  },\n  { idempotencyKey: \"launch-player-42-round-001\" },\n);\n```\n\n#### Create a demo session\n\nLaunch a game in free-play mode. No player credentials or balance required.\n\n```typescript\nconst demo = await aggregator.sessions.createDemo({\n  game_id: \"d4f7a2b1-3c8e-4f5a-9b6d-1e2f3a4b5c6d\",\n});\n\nconsole.log(demo.session_id); // \"8c8e8c8e-8c8e-4c8e-8c8e-8c8e8c8e8c8e\"\nconsole.log(demo.game_url);   // \"https://...\"\n```\n\n#### Get session details\n\nRetrieve the current state of a session.\n\n```typescript\nconst session = await aggregator.sessions.get(\n  \"8c8e8c8e-8c8e-4c8e-8c8e-8c8e8c8e8c8e\",\n  { sub_operator_ref: \"brand-01\" }, // platform accounts only\n);\n\nconsole.log(session.status);     // \"active\" | \"completed\" | \"expired\" | \"error\"\nconsole.log(session.created_at); // ISO-8601 timestamp\n```\n\nThe SDK accepts both the canonical unwrapped session-detail response and the\nlegacy `{ \"session\": ... }` envelope during Core cutover. An `id`-only legacy\nresponse is normalized so `SessionDetail` always exposes matching `id` and\n`session_id`; conflicting identifiers fail closed.\n\n### Transactions\n\n#### List transactions\n\nRetrieve a paginated list of transactions.\n\n```typescript\nconst { transactions, limit, offset } =\n  await aggregator.transactions.list({\n    page: 2,\n    per_page: 50,\n  });\n\nfor (const tx of transactions) {\n  console.log(tx.id, tx.type, tx.amount, tx.currency);\n  // \"tx_001\" \"bet\" <Core-provided amount> \"EUR\"\n}\n```\n\nThe current API implements `limit` (1–100) and `offset`. For convenience, the\nSDK also accepts `page` and `per_page` (1–100) and translates them to those\nwire parameters; do not mix the two families. The current runtime ignores the\ndocumented brand/session/player/type/time filters, so this SDK does not expose\nor send them. Current responses do not promise `total`, `page`, or `per_page`.\nThe SDK normalizes the current `transaction_type` response field into `type`.\nThe deployed handler returns its stored `amount` without a normalized string\nalias. The SDK preserves that `number | string` value and does not guess a\nmajor-unit conversion. Do not use this endpoint for exact financial\nreconciliation until Core publishes and deploys one authoritative amount/unit\ncontract.\n\n**Transaction object properties:**\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `id` | `string` | Unique transaction identifier |\n| `session_id` | `string` | Associated session |\n| `type` | `string` | Transaction type (`\"bet\"`, `\"win\"`, `\"refund\"`) |\n| `amount` | `number \\| string` | Current database-shaped value; no normalized unit/string alias is guaranteed |\n| `currency` | `string` | ISO 4217 currency code |\n| `provider_transaction_id` | `string?` | Provider-side transaction identifier when available |\n| `provider_round_id` | `string?` | Canonical provider round identifier |\n| `status` | `string?` | Processing state: `pending`, `forwarded`, `completed`, `failed`, or `duplicate` |\n| `environment` | `\"test\" \\| \"live\"?` | Ledger environment |\n| `operator_status` | `number \\| null?` | Operator callback HTTP status |\n| `operator_response` | `object \\| null?` | Authenticated operator wallet response body |\n| `processing_time_ms` | `number \\| null?` | Callback processing latency in milliseconds |\n| `error_message` | `string \\| null?` | Stored callback error when processing failed |\n| `created_at` | `string` | ISO 8601 timestamp |\n\n## Error Handling\n\nAll API errors throw an `AggregatorError` with structured details.\n\n```typescript\nimport { Aggregator, AggregatorError } from \"@aggregator-gg/sdk\";\n\ntry {\n  await aggregator.games.list({\n    per_page: 25,\n  }, {\n    signal: AbortSignal.timeout(5_000),\n  });\n  await aggregator.sessions.create({\n    game_id: \"00000000-0000-4000-8000-000000000000\",\n    player_id: \"player_42\",\n    balance: 10000,\n    currency: \"EUR\",\n    country: \"DE\",\n  });\n} catch (err) {\n  if (err instanceof AggregatorError) {\n    console.error(err.status);  // 404\n    console.error(err.message); // \"Aggregator API request failed with status 404\"\n    console.error(err.code);    // \"GAME_NOT_FOUND\" (or null if not provided)\n    console.error(err.body);    // full response body for debugging\n  }\n}\n```\n\n**`AggregatorError` properties:**\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `message` | `string` | Generic status-based description that does not relay untrusted upstream detail |\n| `status` | `number` | HTTP status code |\n| `code` | `string \\| null` | Machine-readable error code from the API, if available |\n| `body` | `unknown` | Full response body for debugging |\n\n**Common error codes:**\n\n| Status | Meaning | What to do |\n|--------|---------|------------|\n| `400` | Bad request -- missing or invalid parameters | Check your request body against the parameter tables above |\n| `401` | Invalid API key | Verify your `apiKey` is correct and has not been revoked |\n| `403` | Forbidden -- key lacks permission for this resource | Ensure you are using a key with the right scope |\n| `404` | Resource not found | Check the ID you passed (game, session, etc.) |\n| `429` | Rate limit exceeded | Back off and retry after the `Retry-After` header interval |\n\n## TypeScript Support\n\nThe SDK is written in TypeScript and ships type definitions for every interface and method. All types are exported from the package root:\n\n```typescript\nimport type {\n  Game,\n  ListGamesParams,\n  ListGamesResponse,\n  CreatedSession,\n  SessionDetail,\n  CreateSessionBody,\n  DemoSession,\n  CreateDemoSessionBody,\n  Transaction,\n  ListTransactionsParams,\n  ListTransactionsResponse,\n  AggregatorConfig,\n  RequestOptions,\n  RetryConfig,\n} from \"@aggregator-gg/sdk\";\n```\n\n## Requirements\n\n- **Node.js 24+** (uses `globalThis.fetch` by default)\n\n## Security\n\nReport vulnerabilities privately to the\n[Aggregator Security Team](mailto:security@aggregator.gg). Do not disclose\nvulnerability details or credentials in a public issue. The team aims to\nacknowledge reports within 3 business days.\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-02a8f9b743dbcfda83d6c24de9a7863d"}