Documentation v1

Codrez RGS
B2B Integration

The Codrez Remote Gaming Server (RGS) provides casino game infrastructure for B2B operators. This documentation covers everything you need for a complete, certified integration.

🔑

Secure Authentication

API Key + HMAC-SHA256 on every callback

💰

Seamless Wallet

Your balance, your players — total control

🎮

Multi-game

Growing catalog with configurable RTP and volatility

Integration Flow

1. Get credentials2. Configure callbacks3. List games4. Generate playerToken5. Launch session✓ Live

Authentication

All requests to the RGS must include the header with your API Key. Credentials are available in the Admin Panel after your account is created.

Your credentials

API Keyck_...Authenticates requests to the RGS
API Secretcs_...Reserved for future features
Callback Secretcb_...Validates the HMAC signature on callbacks

Example of how the header is used across all calls:

curl https://rgs.codrez.com/rgs/v1/games \
  -H "X-Codrez-API-Key: ck_your_api_key_here"

Environments

Use the Sandbox environment for development and testing. Credentials differ per environment.

Sandbox
https://rgs-sandbox.codrez.com

Testing — no real transactions

Production
https://rgs.codrez.com

Live — real transactions

The Sandbox uses simulated wallets. Set your callback_url to a test endpoint (e.g., ngrok) to receive callbacks during development.

List Games

Returns all games available for your operator, filtered by the games enabled by Codrez on your account.

GEThttps://rgs.codrez.com/rgs/v1/games
curl -X GET https://rgs.codrez.com/rgs/v1/games \
  -H "X-Codrez-API-Key: ck_your_api_key_here"
Response 200 OK
{
  "success": true,
  "games": [
    {
      "id":              "game_001",
      "name":            "Spirit of Jungle",
      "slug":            "spirit-of-jungle",
      "provider":        "Codrez",
      "category":        "slot",
      "rtp_nominal":     96.5,
      "volatility":      "high",
      "bet_min":         100,
      "bet_max":         50000,
      "bet_options":     [100, 200, 500, 1000, 2000, 5000],
      "lines":           20,
      "jackpot_enabled": false,
      "allow_demo":      true,
      "thumbnail_url":   "https://cdn.codrez.com/games/spirit-of-jungle/thumb.webp"
    }
  ]
}
Values for bet_min, bet_max, and bet_options are in cents. E.g.: 100 = $1.00

Start Player Session

To start a game session, generate a temporary playerToken on your platform and redirect the player to the RGS launch URL. The RGS uses this token to authenticate the player via callback.

GEThttps://rgs.codrez.com/rgs/v1/launch
ParameterTypeDescription
gameSlug*stringGame slug (obtained from /games)
playerToken*stringShort-lived token generated by your platform for the player
currency*stringSession currency. E.g.: BRL, USD
langstringInterface language. E.g.: en, es, pt
curl -G "https://rgs.codrez.com/rgs/v1/launch" \
  -H "X-Codrez-API-Key: ck_your_api_key_here" \
  --data-urlencode "gameSlug=spirit-of-jungle" \
  --data-urlencode "playerToken=PLAYER_TOKEN" \
  --data-urlencode "currency=BRL" \
  --data-urlencode "lang=en"
The playerToken must be short-lived (recommended: 60s) and single-use. The RGS invalidates it after the first successful authentication.

HMAC Signature

All callbacks sent by Codrez to your server are signed with HMAC-SHA256 using your Callback Secret. Always validate the signature before processing any transaction.

Headers sent with every callback:

X-Codrez-TimestampUnix timestamp (seconds) at send time
X-Codrez-Signaturet={timestamp},s={hmac_sha256_hex}
X-Codrez-OperatorOperator slug (for identification)

// Signature formula

HMAC-SHA256(

key = callback_secret,

input = "{timestamp}:{raw_json_body}"

)

const crypto = require('crypto');

function verifySignature(rawBody, headers, callbackSecret) {
  const timestamp = headers['x-codrez-timestamp'];
  const sigHeader = headers['x-codrez-signature'];
  const match     = sigHeader.match(/s=([a-f0-9]+)/);
  if (!timestamp || !match) return false;

  const expected = crypto
    .createHmac('sha256', callbackSecret)
    .update(`${timestamp}:${rawBody}`)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(expected), Buffer.from(match[1])
  );
}
Reject requests with a timestamp older than 5 minutes to prevent replay attacks.

Callback: Authenticate

Called by Codrez when a player starts a session. Validate the playerToken and return the player's ID and balance.

CBPOST {callback_url}/authenticate

Request Body

ParameterTypeDescription
playerToken*stringToken generated by your platform
sessionId*stringSession ID generated by the RGS

Expected Response

ParameterTypeDescription
playerId*stringUnique player ID in your platform
balance*integerCurrent balance in cents
currency*stringCurrency. E.g.: BRL
app.post('/authenticate', async (req, res) => {
  const { playerToken, sessionId } = req.body;
  // 1. Verify HMAC signature (see HMAC section)
  const player = await db.findByToken(playerToken);
  if (!player) return res.status(401).json({ code: 'INVALID_TOKEN' });
  return res.json({ playerId: player.id, balance: player.balanceCents, currency: 'BRL' });
});
Response 200 OK
{
  "playerId": "player_12345",
  "balance":  125000,
  "currency": "BRL"
}

Callback: Balance

Called to retrieve the player's current balance during the session.

CBPOST {callback_url}/balance

Request Body

ParameterTypeDescription
playerId*stringPlayer ID
currency*stringSession currency
sessionId*stringSession ID

Response

ParameterTypeDescription
balance*integerCurrent balance in cents
currency*stringCurrency
app.post('/balance', async (req, res) => {
  const { playerId, currency, sessionId } = req.body;
  const player = await db.findById(playerId);
  if (!player) return res.status(404).json({ code: 'PLAYER_NOT_FOUND' });
  return res.json({ balance: player.balanceCents, currency: player.currency });
});
Response 200 OK
{
  "balance":  125000,
  "currency": "BRL"
}

Callback: Debit

Called to debit the player's wager. Idempotency is required — use the idempotencyKey to catch duplicates.

CBPOST {callback_url}/debit
ParameterTypeDescription
playerId*stringPlayer ID
amountCents*integerBet amount in cents
currency*stringCurrency
roundId*stringUnique round ID
gameSlug*stringGame slug
sessionId*stringSession ID
idempotencyKey*stringUnique idempotency key. Format: {roundId}:debit
app.post('/debit', async (req, res) => {
  const { playerId, amountCents, roundId, idempotencyKey } = req.body;
  // Idempotency: if already processed, return saved result
  const existing = await db.findTxByKey(idempotencyKey);
  if (existing) return res.json({ balance: existing.balanceAfter, transactionId: existing.id });
  const player = await db.findById(playerId);
  if (player.balanceCents < amountCents) return res.status(402).json({ code: 'INSUFFICIENT_FUNDS' });
  const tx = await db.debit({ playerId, amountCents, roundId, idempotencyKey });
  return res.json({ balance: tx.balanceAfter, transactionId: tx.id });
});
Response 200 OK
{
  "balance":       120000,
  "transactionId": "tx_a1b2c3d4"
}
If amountCents exceeds the player's balance, return HTTP 402 with {"code":"INSUFFICIENT_FUNDS"}. The RGS will abort the round.

Callback: Credit

Called after the round result to credit winnings to the player. amountCents may be 0 if the player did not win.

CBPOST {callback_url}/credit
ParameterTypeDescription
playerId*stringPlayer ID
amountCents*integerWinnings in cents (0 if no win)
currency*stringCurrency
roundId*stringRound ID (same as debit)
gameSlug*stringGame slug
sessionId*stringSession ID
idempotencyKey*stringFormat: {roundId}:credit
app.post('/credit', async (req, res) => {
  const { playerId, amountCents, roundId, idempotencyKey } = req.body;
  const existing = await db.findTxByKey(idempotencyKey);
  if (existing) return res.json({ balance: existing.balanceAfter, transactionId: existing.id });
  // amountCents can be 0 (no win)
  const tx = await db.credit({ playerId, amountCents, roundId, idempotencyKey });
  return res.json({ balance: tx.balanceAfter, transactionId: tx.id });
});
Response 200 OK
{
  "balance":       135000,
  "transactionId": "tx_f6g7h8i9"
}

Callback: Rollback

Called when a round fails after the debit was confirmed. Roll back the debit and restore the player's balance.

CBPOST {callback_url}/rollback
ParameterTypeDescription
playerId*stringPlayer ID
currency*stringCurrency
roundId*stringRound ID to roll back
gameSlug*stringGame slug
sessionId*stringSession ID
idempotencyKey*stringFormat: {roundId}:rollback
reasonstringRollback reason. E.g.: TIMEOUT, GAME_ERROR
app.post('/rollback', async (req, res) => {
  const { playerId, roundId, idempotencyKey } = req.body;
  const existing = await db.findTxByKey(idempotencyKey);
  if (existing) return res.json({ balance: existing.balanceAfter });
  const original = await db.findTxByKey(`${roundId}:debit`);
  if (!original) {
    const player = await db.findById(playerId);
    return res.json({ balance: player.balanceCents });
  }
  const tx = await db.rollback({ playerId, roundId, idempotencyKey, amountCents: original.amountCents });
  return res.json({ balance: tx.balanceAfter });
});
Response 200 OK
{
  "balance": 125000
}
If the original debit does not exist (e.g., was never processed), return the player's current balance — not an error. The rollback was implicit.

Error Codes

Use these codes in your callback responses to communicate errors to the RGS in a standardized way.

HTTPCodeDescription
200OKSuccess
401INVALID_TOKENplayerToken is invalid or expired
401INVALID_SIGNATUREHMAC signature is invalid
402INSUFFICIENT_FUNDSInsufficient balance for the debit
404PLAYER_NOT_FOUNDPlayer not found
409TRANSACTION_DUPLICATEDuplicate transaction (outside idempotency)
422INVALID_CURRENCYCurrency not supported by the operator
500INTERNAL_ERRORInternal error on the operator's server
503SERVICE_UNAVAILABLEService temporarily unavailable

Standard error format

{
  "code":    "INSUFFICIENT_FUNDS",
  "message": "..."
}

Need help?

Our technical team is available for integration support.

[email protected]