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 + request_path + compact_body (e.g., for a POST to /operator/launch: "1708700000" + "/operator/launch" + '{"playerId":"player-1",...}'). For GET requests, use an empty string for the 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. The request path (e.g., /operator/launch) must be included in the signed message. Requests with a timestamp skew greater than 30 seconds are rejected.

Code Examples

POST Requests

BODY='{"playerId":"player-1","currency":"USD","gameCode":"dice-alpha","countryCode":"US"}'
SECRET="your-hmac-secret"
TIMESTAMP=$(date +%s)
PATH_="/operator/launch"
SIG=$(echo -n "${TIMESTAMP}${PATH_}${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"
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 path = "/operator/launch";

const signature = crypto
  .createHmac("sha256", secret)
  .update(timestamp + path + 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)
path := "/operator/launch"

mac := hmac.New(sha256.New, []byte("your-hmac-secret"))
mac.Write([]byte(timestamp))
mac.Write([]byte(path))
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)
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()))
path = "/operator/launch"

signature = hmac.new(
    secret.encode(), (timestamp + path + 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,
    },
)

GET Requests

For GET endpoints like /operator/games, sign the timestamp + path (no body):

TIMESTAMP=$(date +%s)
PATH_="/operator/games"
SIG=$(echo -n "${TIMESTAMP}${PATH_}" | 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"
const crypto = require("crypto");

const operatorId = "your-operator-uuid";
const secret = "your-hmac-secret";
const timestamp = Math.floor(Date.now() / 1000).toString();
const path = "/operator/games";

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

const resp = await fetch("https://rgs.example.com/operator/games", {
  headers: {
    "X-Operator-ID": operatorId,
    "X-Timestamp": timestamp,
    "X-HMAC-SHA256": signature,
  },
});
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
path := "/operator/games"

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

req, _ := http.NewRequest("GET",
    "https://rgs.example.com/operator/games", nil)
req.Header.Set("X-Operator-ID", "your-operator-uuid")
req.Header.Set("X-Timestamp", timestamp)
req.Header.Set("X-HMAC-SHA256", signature)
import hmac, hashlib, time, requests

operator_id = "your-operator-uuid"
secret = "your-hmac-secret"
timestamp = str(int(time.time()))
path = "/operator/games"

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

resp = requests.get(
    "https://rgs.example.com/operator/games",
    headers={
        "X-Operator-ID": operator_id,
        "X-Timestamp": timestamp,
        "X-HMAC-SHA256": signature,
    },
)

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 + request_path + compact_json_body.

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

# Example: verify a callback signature manually
TIMESTAMP="1708700000"  # from X-Timestamp header
BODY='{"requestId":"...","playerId":"player-1","amount":"1.50"}'
SECRET="your-hmac-secret"
PATH_="/callback/debit"

EXPECTED=$(echo -n "${TIMESTAMP}${PATH_}${BODY}" \
  | openssl dgst -sha256 -hmac "$SECRET" \
  | awk '{print $2}')

echo "Expected: $EXPECTED"
# Compare with the X-HMAC-SHA256 header value
const crypto = require("crypto");

function verifyCallback(req, secret) {
  const timestamp = req.headers["x-timestamp"];
  const signature = req.headers["x-hmac-sha256"];
  const path = req.path; // e.g., "/callback/debit"
  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 + path + body)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}
func verifyCallback(r *http.Request, secret string) bool {
	timestamp := r.Header.Get("X-Timestamp")
	signature := r.Header.Get("X-HMAC-SHA256")
	path := r.URL.Path
	body, _ := io.ReadAll(r.Body)

	// Check timestamp freshness
	ts, _ := strconv.ParseInt(timestamp, 10, 64)
	if abs(time.Now().Unix()-ts) > 30 {
		return false
	}

	// Verify HMAC
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(timestamp))
	mac.Write([]byte(path))
	mac.Write(body)
	expected := hex.EncodeToString(mac.Sum(nil))
	return hmac.Equal([]byte(signature), []byte(expected))
}
import hmac, hashlib, time

def verify_callback(request, secret):
    timestamp = request.headers["X-Timestamp"]
    signature = request.headers["X-HMAC-SHA256"]
    path = request.path  # e.g., "/callback/debit"
    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 + path + body).encode(), hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)