Authentication

Operator-facing endpoints (/operator/launch, /operator/games, /operator/currencies) require HMAC-SHA256 request signing with replay protection.

Required Headers

Header Description
X-Operator-ID Your operator UUID
X-Timestamp Current Unix epoch seconds (e.g., 1708700000). Must be within ±30s of server time.
X-HMAC-SHA256 Hex-encoded HMAC-SHA256 signature (see below)

Computing the Signature

Get Timestamp

Get the current Unix timestamp as a decimal string (e.g., "1708700000").

Serialize Body

Serialize the JSON request body as compact JSON (no extra whitespace or newlines). For GET requests, use an empty string.

Concatenate

Concatenate: timestamp + compact_body

Sign

Compute HMAC-SHA256 of the concatenated string using your operator secret key.

Encode

Hex-encode the result and pass it in the X-HMAC-SHA256 header. Pass the same timestamp in X-Timestamp.

The server normalizes the request body to compact JSON before verifying the signature. You must sign the compact (minified) form of your JSON payload. Requests with a timestamp skew greater than 30 seconds are rejected.

Code Examples

POST Requests

import hmac, hashlib, json, time, requests

operator_id = "your-operator-uuid"
secret = "your-hmac-secret"
payload = {"playerId": "player-1", "currency": "USD", "gameCode": "dice-alpha", "countryCode": "US"}
body = json.dumps(payload, separators=(",", ":"))
timestamp = str(int(time.time()))

signature = hmac.new(
    secret.encode(), (timestamp + body).encode(), hashlib.sha256
).hexdigest()

resp = requests.post(
    "https://rgs.example.com/operator/launch",
    json=json.loads(body),
    headers={
        "X-Operator-ID": operator_id,
        "X-Timestamp": timestamp,
        "X-HMAC-SHA256": signature,
    },
)
const crypto = require("crypto");

const operatorId = "your-operator-uuid";
const secret = "your-hmac-secret";
const body = JSON.stringify({
  playerId: "player-1",
  currency: "USD",
  gameCode: "dice-alpha",
  countryCode: "US",
});
const timestamp = Math.floor(Date.now() / 1000).toString();

const signature = crypto
  .createHmac("sha256", secret)
  .update(timestamp + body)
  .digest("hex");

const resp = await fetch("https://rgs.example.com/operator/launch", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Operator-ID": operatorId,
    "X-Timestamp": timestamp,
    "X-HMAC-SHA256": signature,
  },
  body,
});
body, _ := json.Marshal(map[string]string{
    "playerId": "player-1",
    "currency": "USD",
    "gameCode": "dice-alpha",
    "countryCode": "US",
})
timestamp := strconv.FormatInt(time.Now().Unix(), 10)

mac := hmac.New(sha256.New, []byte("your-hmac-secret"))
mac.Write([]byte(timestamp))
mac.Write(body)
signature := hex.EncodeToString(mac.Sum(nil))

req, _ := http.NewRequest("POST",
    "https://rgs.example.com/operator/launch",
    bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Operator-ID", "your-operator-uuid")
req.Header.Set("X-Timestamp", timestamp)
req.Header.Set("X-HMAC-SHA256", signature)
BODY='{"playerId":"player-1","currency":"USD","gameCode":"dice-alpha","countryCode":"US"}'
SECRET="your-hmac-secret"
TIMESTAMP=$(date +%s)
SIG=$(echo -n "${TIMESTAMP}${BODY}" \
  | openssl dgst -sha256 -hmac "$SECRET" \
  | awk '{print $2}')

curl -X POST https://rgs.example.com/operator/launch \
  -H "Content-Type: application/json" \
  -H "X-Operator-ID: your-operator-uuid" \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-HMAC-SHA256: $SIG" \
  -d "$BODY"

GET Requests

For GET endpoints like /operator/games, sign only the timestamp (empty body):

TIMESTAMP=$(date +%s)
SIG=$(echo -n "$TIMESTAMP" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')

curl https://rgs.example.com/operator/games \
  -H "X-Operator-ID: your-operator-uuid" \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-HMAC-SHA256: $SIG"

Verifying Callbacks

The RGS signs all outgoing callbacks (authenticate, debit, credit, balance, rollback) with the same HMAC-SHA256 scheme. Each callback includes X-Timestamp and X-HMAC-SHA256 headers — the signature covers timestamp + compact_json_body.

Your server should verify both the timestamp freshness (within ±30s) and the HMAC signature on every incoming callback.

import hmac, hashlib, time

def verify_callback(request, secret):
    timestamp = request.headers["X-Timestamp"]
    signature = request.headers["X-HMAC-SHA256"]
    body = request.get_data(as_text=True)

    # Check timestamp freshness
    if abs(time.time() - int(timestamp)) > 30:
        return False

    # Verify HMAC
    expected = hmac.new(
        secret.encode(), (timestamp + body).encode(), hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)
const crypto = require("crypto");

function verifyCallback(req, secret) {
  const timestamp = req.headers["x-timestamp"];
  const signature = req.headers["x-hmac-sha256"];
  const body = JSON.stringify(req.body);

  // Check timestamp freshness
  if (Math.abs(Date.now() / 1000 - parseInt(timestamp)) > 30) {
    return false;
  }

  // Verify HMAC
  const expected = crypto
    .createHmac("sha256", secret)
    .update(timestamp + body)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}