Skip to content
KinetX
Operator API v1

Authentication & signing

Every server-to-server request is signed with your API secret using HMAC-SHA256 — the secret itself never travels over the network.

API keys

An API key has a public key id (kx_live_…, sent in every request) and a secret (kxs_…, used only to sign). Staff with the "Manage API & Webhooks" permission create keys in the backoffice, choose their scopes, IP allowlist, rate limit and expiry, and can rotate the secret or revoke the key at any time.

The secret is displayed once, when the key is created or rotated. Store it in your secrets manager; KinetX staff cannot show it again.

Signing requests

Send three headers with every request:

X-Api-KeyYour public key id.
X-TimestampCurrent Unix time in seconds. Requests more than 300 seconds away from the server clock are refused.
X-SignatureLower-case hex HMAC-SHA256 of the string to sign, keyed with your secret.
X-NonceOptional. A unique value appended to the string to sign — needed only if you may send byte-identical requests within the same second.

The string to sign joins four fields with a newline (\n): the HTTP method in upper case, the request URI exactly as sent (path and query string, starting with /api/v1), the timestamp, and the raw body (an empty string for GET). With X-Nonce, the nonce is appended as a fifth line.

string to sign
POST
/api/v1/operator/players?source=web
1760520600
{"username":"jane_doe",…}

Each signature is accepted once: a captured request cannot be replayed (REPLAYED_REQUEST). Sign the exact bytes you send — serialize the JSON once and use the same string for the signature and the body.

Examples

Register a player with a signed, idempotent POST:

API_KEY="kx_live_your_key_id"
API_SECRET="kxs_your_secret"
URI="/api/v1/operator/players"
BODY='{"username":"jane_doe","email":"jane@example.com","first_name":"Jane","last_name":"Doe","birth_date":"1990-04-12","country":"MT","terms_accepted":true}'
TS=$(date +%s)

# METHOD \n REQUEST_URI \n TIMESTAMP \n BODY  →  hex HMAC-SHA256 with your secret
SIG=$(printf '%s\n%s\n%s\n%s' "POST" "$URI" "$TS" "$BODY" \
  | openssl dgst -sha256 -hmac "$API_SECRET" -hex | sed 's/^.* //')

curl -sS -X POST "https://kinetx.amhtechnologies.com$URI" \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: $API_KEY" \
  -H "X-Timestamp: $TS" \
  -H "X-Signature: $SIG" \
  -H "Idempotency-Key: $(uuidgen)" \
  --data "$BODY"

Scopes

A key may only call endpoints whose scope it holds; otherwise the API answers 403 INSUFFICIENT_SCOPE. Give each integration the smallest set it needs.

players:readRead players, their profiles and responsible-gaming limits
players:writeRegister players, update profiles and status, issue player session tokens
limits:writeSet responsible-gaming limits and self-exclusions
wallet:readRead wallet balances and the transaction ledger
payments:readRead deposits, withdrawals and payment methods
payments:writeRecord paid deposits and create withdrawal requests
games:readRead the game catalogue
games:launchLaunch games for players (real and demo)
bonuses:readRead the bonus catalogue and player bonuses
bonuses:writeGrant bonuses and redeem promo codes
reports:readRead GGR and financial summaries

IP allowlist

Restrict a key to the addresses of your servers: single IPv4/IPv6 addresses or CIDR ranges such as 203.0.113.0/24. Calls from other addresses get 403 IP_NOT_ALLOWED. An empty list allows any address.

Player session tokens

Your front end must never hold the API secret. When a player signs in on your site, your server issues a short-lived player session token (POST /api/v1/operator/players/{player}/session-tokens, default 30 minutes, at most 720) and hands it to the browser or app, which calls the player endpoints with Authorization: Bearer kxp_….

JavaScript
// Your server: POST /api/v1/operator/players/1042/session-tokens  →  { data: { token, expires_at } }
// Your front end (never holds the API secret):
const res = await fetch('https://kinetx.amhtechnologies.com/api/v1/player/games/311/launch', {
  method: 'POST',
  headers: { Authorization: `Bearer ${playerToken}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ mode: 'real', platform: 'mobile' }),
});
const { data } = await res.json();
window.location.href = data.launch_url;

A token only gives access to its own player (profile, balances, transactions, game launch, KinetX Originals). It stops working when it expires or when the API key that issued it is revoked.

Security checklist

  • Keep secrets server-side, in a secrets manager — never in front-end code, mobile apps or repositories.
  • Use one key per integration and environment, with the fewest scopes and an IP allowlist.
  • Rotate secrets periodically and immediately if one may have leaked; revoke keys you no longer use.
  • Keep your server clock synchronised (NTP): signatures depend on it.
  • Verify the signature of every webhook before trusting it.