Authentication
All POST requests use Content-Type: application/json. Except health and create_token, public APIs require the token request header.
Server IP allowlist
Token requests and subsequent API calls validate your server's outbound IP. An unlisted IP receives HTTP 403 with ip_not_allowed. An empty allowlist denies access. Register the calling server's public outbound IP, not a platform proxy IP.
The HTTPS entry overwrites and forwards the actual source IP. WebDeliverServer trusts X-Real-IP only when the connection comes from an explicitly configured trusted proxy. Other proxy headers, X-Forwarded-For and IPs declared in request bodies cannot bypass allowlisting. Partners do not need to set these headers.
Allowlist changes, suspension and secret resets take effect on runtime configuration refresh, usually within seconds. Existing tokens are also subject to IP validation.
Obtain a token
POST /api/v1/create_token
{
"partner": "YOUR_CODE",
"timestamp": "UNIX_SECONDS",
"nonce": "32_LOWERCASE_HEX_CHARACTERS",
"sign": "64_LOWERCASE_HEX_HMAC"
}
Calculate HMAC-SHA256(secret, timestamp + ":" + nonce) and encode it as lowercase hexadecimal. timestamp is a string containing Unix seconds, with a permitted clock difference of 300 seconds. nonce must be exactly 32 lowercase hexadecimal characters, representing 16 cryptographically random bytes. Generate a new nonce and timestamp for every attempt, including retries. Use the displayed secret text as the HMAC key; do not hex-decode it.
{
"success": true,
"message": "TOKEN",
"expires_in": 3600
}
Invalid secrets, expired signatures, reused nonces and unapproved partners are rejected. Tokens last at most one hour. Repeated requests reuse an unexpired token; cache for no more than 50 minutes. Obtain a new token after DBServer restarts. Secret changes and partner suspension invalidate old tokens.
Do not request a token for every operation or replay failed business requests unconditionally. After obtaining a new token, retry only operations explicitly safe to retry. API errors, session interruption and wallet callback failures are different conditions.
Go signing example
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(timestamp + ":" + nonce))
sign := hex.EncodeToString(h.Sum(nil))
Example domains and credentials are placeholders. Use HTTPS for deployment; HTTP is acceptable for local testing. Exclude token, secret, sign, user_key and session-bearing launch URLs from access logs.
Request and response conventions
Obtain the API base URL from Applications → app → Credentials. Endpoint, code and secret must all belong to the app. partner contains the app's code, not the app ID, email or literal word code. Use secret only to calculate signatures locally; never send it to the API. This protocol uses the token header, not Authorization: Bearer.
| Token request field | Type | Requirement |
|---|---|---|
| partner | string | Required: approved app code |
| timestamp | string | Required: decimal Unix seconds, not milliseconds |
| nonce | string | Required: 32 random lowercase hexadecimal characters |
| sign | string | Required: 64 lowercase hexadecimal HMAC-SHA256 characters |
| Token response field | Type | Meaning |
|---|---|---|
| success | boolean | Business result |
| message | string | Token on success; explanation on failure |
| expires_in | integer | Currently 3600; reuse does not extend the token's remaining life |
POST /api/v1/gamelist HTTP/1.1
Host: api.example.invalid
Content-Type: application/json
token: YOUR_TOKEN
{}
Check HTTP status first, then success. Only game catalog, machine catalog and game activity return a JSON-encoded message string requiring a second parse. Other APIs use top-level fields; health has no success field. Never JSON.parse every message unconditionally.
const response = await fetch(apiBase + "/api/v1/gamelist", {
method: "POST",
headers: { "Content-Type": "application/json", token },
body: "{}"
});
const body = await response.json();
if (!response.ok || body.success !== true) {
throw new Error(body.message || body.error || "API request failed");
}
const catalog = JSON.parse(body.message);
const games = catalog.list;
Run this on your server only. Production implementations also need timeouts, non-JSON response handling and sensitive-log redaction.
Errors and retries
There are two error shapes; error is not always present. HTTP 200 can still indicate business failure.
{"success": false, "message": "invalid user code"}
{"error": "ip_not_allowed", "message": "server IP is not in the partner whitelist"}
| HTTP status | Typical response | Action |
|---|---|---|
| 200 with success=false | message explains the business failure | Correct parameters or business conditions; do not treat as success |
| 400 | request_body_required / invalid_json_body | Check body and JSON encoding |
| 401 | invalid credentials | Check endpoint, code, secret, clock and nonce |
| 401 | missing partner token / invalid or expired partner token | Supply a valid token or obtain a new one |
| 403 | ip_not_allowed | Update the app's server outbound IP, not the player's browser IP |
| 404 | route_not_found / method_not_allowed | Check path and method; wrong methods currently also return 404 |
| 413 | Body too large | Reduce the request; do not repeat it unchanged |
| 500 / 503 | Service error or db_unavailable | Log redacted context; bounded backoff for read-only requests |
| 504 | http_timeout | Result may be unknown; check status or reconcile records first |
Read-only catalog and status queries may use bounded retries after transient failures. A launch or kick timeout does not prove the operation was not executed; check player status first. There is no generic idempotency request header. Do not assume another platform's Idempotency-Key semantics. See Callbacks and settlement.