Conventions & Authentication
Base path: /api/v1/user-bonuses
The User Bonus API allows game clients and back-office integrations (the PAM) to query a user's applicable bonuses, record bonus consumption against wager events, revert erroneous consumptions, and inspect the full bonus transaction history.
Authentication — S2S HMAC signature
All requests are server-to-server (S2S). Every API endpoint requires these headers.
The signature check runs at the router level. No endpoint is unauthenticated. This includes GET /applicable-codes, POST /validate-code, POST /consume/{consume_txn_id}/revert, and GET /{user_id}/referral-code.
X-Client-Id
string
Your registered client identifier
X-Timestamp
string
Current Unix epoch seconds, such as 1716112200
X-Signature
string
HMAC-SHA256 hexadecimal digest of the canonical string
Requests more than 300 seconds from the server clock are rejected. This prevents replay attacks.
Build the canonical string
{client_id}\n{timestamp}\n{query_string}\n{raw_body}client_idis theX-Client-Idvalue.timestampis the exactX-Timestampstring.query_stringis the raw URL query string. Use an empty string without parameters.raw_bodyis the raw UTF-8 request body. Use an empty string forGETrequests.
Sign the canonical string:
Client secrets are provisioned per client. A Redis-cached lookup resolves them. Contact the platform team to register your client_id and client_secret.
Applicable APIs
The S2S HMAC signature is required for these endpoints:
GET /applicable-codesPOST /validate-codePOST /consumePOST /consume/{consume_txn_id}/revertGET /{user_id}/summaryGET /{user_id}/transactionsGET /{user_id}/transactions/{txn_id}GET /{user_id}/referral-code
Code examples
Python — POST with body
Node.js — POST with body
Python — GET with query parameters
Common conventions
Monetary values are decimal strings (e.g.
"300.00") to avoid floating-point precision loss. Parse with aDecimalor equivalent type.Timestamps are UTC in ISO 8601 format (
YYYY-MM-DDTHH:MM:SS).Chip type — player-facing operations take
chip_type:"cash"or"in_app_purchase".Idempotency (
consume_txn_id) — generate it on your side (e.g. a transaction UUID) and store it before calling the API. A retry with the sameconsume_txn_idreturns409 Conflict— it is your duplicate-detection signal, not a silent no-op. Pass the same value to the revert endpoint to cancel a consumption.Chunk status lifecycle:
PENDING → RELEASE → CONSUMED / EXPIRED. A chunk must be inRELEASEstatus before it can be consumed.Pagination —
limit(1–200, default 50) +offset(default 0, ≥ 0).PII —
email,phone,ipmust be hashed (SHA-256 with project salt) before persistence; raw values may transitapi-serviceover TLS but must never be logged or written to ClickHouse / MongoDB unhashed.
Error responses
All errors follow a consistent envelope:
400 Bad Request
Request validation failed (missing fields, out-of-range values)
401 Unauthorized
An authentication header is missing, the client is unregistered, the signature is invalid, or the timestamp is outside the allowed window
404 Not Found
The referenced bonus grant, chunk, or consumption record does not exist, or does not belong to the given user
409 Conflict
Duplicate consumption (consume_txn_id already recorded), no released chunk available for user, or consumption already reverted
500 Internal Server Error
Unexpected database error
Admin configuration conventions
Back-office configuration operations (heads, subheads, configures — see Configuration APIs) additionally use:
Actor fields — every write carries
created_by/updated_byfor the audit trail; history endpoints return the full change log.JSON-string parameters for budgets, limits, and owners:
budget_json:'[{"period_type": "DAILY", "budget_limit": 5000}]'—period_type∈DAILY|WEEKLY|MONTHLY,budget_limit: null= uncappedlimits_json:'{"limits": [{"period_type": "MONTHLY", "budget_limit": 50000}], "updated_by": "admin"}'owners_json:'{"owners": [{"username": "alice", "role": "OPS_LEAD", "active": true}], "updated_by": "admin"}'— role ∈OPS_LEAD|CAMPAIGN_MANAGER|FINANCE_APPROVER|ESCALATION_CONTACT
Last updated
Was this helpful?