Skip to content
Getting started / Authentication
Getting started

Authentication

Every request under /v1 carries three headers. There are no sessions, cookies or tokens.

HeaderValue
X-Agg-Key-IdThe key_id the request is signed with
X-Agg-TimestampUnix time in seconds, UTC. Accepted within ±5 minutes of our clock
X-Agg-SignatureLowercase hex of HMAC_SHA256(secret, timestamp + "." + payload)
  1. 1Request
  2. 2X-Agg-Timestamp
  3. 3Canonical string: timestamp + "." + payload
  4. 4HMAC-SHA256 with your secret
  5. 5X-Agg-Signature (lowercase hex)

What is signed

  • A request with a body (POST): the raw body, the exact bytes you send. Sign the string you send, not an object you serialise again; whitespace, key order and escaped slashes all change the digest without changing the JSON.
  • A request without a body (GET, and a POST with an empty body): the request line, method, a space, then path and query exactly as sent: GET /v1/games?limit=50.
  • The comparison is constant-time and ignores the case of the hex digits.

Code samples

KEY_ID=ak_5f2c9e01a34b7d8e2f0c1b6a
SECRET=…            # never in a URL, never in a log
BASE=https://sandbox-api.example.com

# GET: sign the request line — method, path and query exactly as sent.
TS=$(date +%s)
PAYLOAD='GET /v1/games?limit=50'
SIG=$(printf '%s.%s' "$TS" "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $2}')
curl "$BASE/v1/games?limit=50" \
  -H "X-Agg-Key-Id: $KEY_ID" -H "X-Agg-Timestamp: $TS" -H "X-Agg-Signature: $SIG"

# POST: sign the raw body — the exact bytes you send.
TS=$(date +%s)
BODY='{"player_id":"p-1","currency":"EUR","game_id":"6ddd5f30-0b1c-4c52-9f3e-2a7c1b8e4d10"}'
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $2}')
curl "$BASE/v1/sessions" -H "Content-Type: application/json" --data-binary "$BODY" \
  -H "X-Agg-Key-Id: $KEY_ID" -H "X-Agg-Timestamp: $TS" -H "X-Agg-Signature: $SIG"
Start with GET /v1/ping. It has no side effects and tells you which of the three is wrong: the headers, the clock or the digest. A timestamp outside the window answers UNAUTHORIZED with retryable: true; every other signature failure answers retryable: false.

Test vectors

Run your signing function on these inputs before anything else. Secret sandbox_test_secret_not_for_use, timestamp 1790000000. The same three are checked by the API's own tests.

CasePayloadX-Agg-Signature
GET without a queryGET /v1/ping0387fadb9dd2317c2341b3a7dd751d444ae519125b4bcee70bd97d0b1b442356
GET with a queryGET /v1/games?limit=50&provider_code=pragmatic6c5e583527feed45d179c6ea05db9c916bc1c290df7fcd402000e704db0f69c3
POST with a body{"player_id":"p-1","currency":"EUR","game_id":"g-1"}bdb3dd91f3dd07fc2839b2e4196fb4eaeea3065fd4f155c1b629339908d01c1d

Server-side check

When a real request is refused, let the sandbox look at it: it checks your signature with your secret and tells you which step fails and what we signed. It never returns the signature it expected. In the Operator Portal this is Validation → Signature check; the same check is an unsigned sandbox endpoint:

POST

/v1/debug/signature

Sandbox only: check a signature step by step

Not signedSandbox
json
{
  "key_id": "ak_…",
  "timestamp": "1790000000",
  "signature": "…",
  "method": "GET",
  "path": "/v1/games?limit=50",
  "body": null
}

The answer carries valid, a one-sentence diagnosis, each step (key, timestamp with its drift from our clock, payload, signature), the canonical_string we signed and hints: the common mistakes your signature turned out to match, such as a path signed without its query, a missing timestamp prefix, a lowercase method, a timestamp in milliseconds or a trailing newline. 30 checks a minute per key and 60 per caller address; not available in production.

Signature tester

Paste the values of a failing request and compare the canonical string and signature with what your code produced.

Signature tester

Runs in your browser with Web Crypto. Nothing you type is sent anywhere. Use sandbox secrets only.

Canonical string
1759150800.{"player_id":"p-1","currency":"EUR","game_id":"6ddd5f30-0b1c-4c52-9f3e-2a7c1b8e4d10"}
X-Agg-Signature
enter a secret