THE CHART GAMES

Developers

Integration guide

Everything you need to add our games to your casino. Base URL: https://thechartgames.com/api/v1

Overview

Your server talks to our API with an API key. To start a game, your server opens a session for one of your players and a game (identified by its service id), and embeds the returned launchUrl in an iframe. While the player plays, we call your wallet to read the balance, debit stakes and credit wins. Your players keep a single balance in your casino.

All amounts are integers in minor units of the session currency (cents for EUR/USD). Responses are JSON: { "ok": true, "data": … } or { "ok": false, "error": { "code", "message" } }.

Authentication

Create keys in the partner portal. Sandbox keys start with tcg_test_, live keys with tcg_live_. The key decides the environment; keys are shown once — store them as secrets on your server and never in a browser.

Authorization: Bearer tcg_test_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

List games

GET /partner/games

{ "ok": true, "data": [
  { "serviceId": 1000, "slug": "integration-test", "title": "Integration test game",
    "rtp": 98, "volatility": "low", "minStake": 1, "maxStake": 1000000,
    "launchable": true, "sandboxOnly": true, "rate": 0 }
] }

rate is your cost rate for the game in live play (share of GGR). Sandbox keys also see unreleased games so you can test them early.

Open a session

POST /partner/sessions
{ "serviceId": 1000, "playerId": "your-player-123", "currency": "EUR",
  "locale": "en", "lobbyUrl": "https://your-casino.example/lobby" }

{ "ok": true, "data": {
  "sessionId": "cm…", "serviceId": 1000, "environment": "SANDBOX",
  "expiresAt": "2026-10-25T14:00:00.000Z",
  "launchUrl": "https://thechartgames.com/play/1000?session=…" } }
  • playerId is your own player reference (up to 100 characters). We send it back in every wallet call.
  • Embed launchUrl in an iframe (or open it in a new window). It is valid for the session lifetime (2 hours by default) and is tied to this player only.
  • Open a new session each time the player opens a game.

Seamless wallet

Expose one HTTPS endpoint and enter it in the portal (separately for sandbox and live). We send POST requests with a JSON body:

{ "requestId": "uuid", "action": "debit", "sessionId": "cm…",
  "playerId": "your-player-123", "currency": "EUR", "serviceId": 1000,
  "roundId": "cm…", "amount": 100, "transactionId": "cm…:bet" }
actionWhenWhat you do
balanceGame startReturn the player's balance.
debitBefore each roundTake amount (the stake). Refuse with INSUFFICIENT_FUNDS if the player cannot afford it.
creditEnd of each roundAdd amount (the win, can be 0). roundFinished: true.
rollbackOnly if a debit may have failedReverse referenceTransactionId if you processed it; otherwise just answer.

Answer HTTP 200 with the new balance, or an error:

{ "balance": 9900 }

{ "error": { "code": "INSUFFICIENT_FUNDS", "message": "Not enough money" } }

Other useful codes: SESSION_EXPIRED, PLAYER_BLOCKED, LIMIT_REACHED. Answer within 8 seconds.

Verifying our signature

Every request carries x-tcg-timestamp (Unix seconds) and x-tcg-signature = hex HMAC-SHA256 of `${timestamp}.${rawBody}` with your wallet secret (generated in the portal, shown once). Reject requests with a wrong signature or a timestamp older than 5 minutes.

// Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';

function verify(rawBody, timestamp, signature, secret) {
  const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');
  const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300;
  return fresh && expected.length === signature.length &&
    timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
<?php // PHP
function verify($rawBody, $ts, $sig, $secret) {
  $expected = hash_hmac('sha256', $ts . '.' . $rawBody, $secret);
  return abs(time() - (int)$ts) < 300 && hash_equals($expected, $sig);
}

Idempotency, rollbacks and retries

  • transactionId is unique per money movement (…:bet, …:win, …:rollback). If you receive one you already processed, do not move money again — just return the current balance.
  • If your wallet does not answer a debit, the round is cancelled and we send a rollback for it in case you did take the stake.
  • If your wallet does not confirm a credit, we keep the round open and send the same credit again every minute (up to 20 times) until you confirm it. Wins are never lost.
  • The game screen sends its own idempotency key per round, so a player double-clicking or reconnecting never plays the same round twice.

Rounds and reports

GET /partner/rounds?page=1&playerId=your-player-123&status=SETTLED
GET /partner/rounds/{roundId}     // full result, server seed and its hash
GET /partner/usage?month=2026-10  // live bets, wins, GGR, rate and cost per game

Round statuses: SETTLED (done), FAILED (stake refused or not charged), CREDIT_PENDING (win waiting for your wallet to confirm). Every round can be verified with its server seed — see Fairness.

Errors

HTTPcodeMeaning
400VALIDATION_FAILEDA field is missing or wrong (see fields).
401UNAUTHENTICATEDMissing, wrong or revoked API key.
403FORBIDDENLive access not active yet, or the account is suspended.
409CONFLICTWallet URL or secret not set for this environment.
422GAME_UNAVAILABLEUnknown, unreleased or retired service id.
429RATE_LIMITEDMore than 600 API requests per minute.

Sandbox and test game

Sandbox keys work as soon as you sign up. Service 1000 is our integration test game: one coin flip per round (a match pays 1.96×), so you can check the whole flow — balance, debit, credit, rollback and retries — against your sandbox wallet. In the portal you can test your wallet connection and launch the test game for any player id. Sandbox rounds are never billed.

Go-live checklist

  • Your sandbox wallet passes the connection test and you have played settled rounds.
  • Repeated transactionIds do not move money twice; INSUFFICIENT_FUNDS is returned when needed.
  • Our team approves your account (we may ask for your licence details).
  • You create a live key, set your live wallet URL and secret, and run the connection test again.

Questions? Contact our integration team.