For the complete documentation index, see llms.txt. This page is also available as Markdown.

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.

Header
Type
Description

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_id is the X-Client-Id value.

  • timestamp is the exact X-Timestamp string.

  • query_string is the raw URL query string. Use an empty string without parameters.

  • raw_body is the raw UTF-8 request body. Use an empty string for GET requests.

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-codes

  • POST /validate-code

  • POST /consume

  • POST /consume/{consume_txn_id}/revert

  • GET /{user_id}/summary

  • GET /{user_id}/transactions

  • GET /{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 a Decimal or 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 same consume_txn_id returns 409 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 in RELEASE status before it can be consumed.

  • Pagination — limit (1–200, default 50) + offset (default 0, ≥ 0).

  • PII — email, phone, ip must be hashed (SHA-256 with project salt) before persistence; raw values may transit api-service over TLS but must never be logged or written to ClickHouse / MongoDB unhashed.

Error responses

All errors follow a consistent envelope:

HTTP Status
Scenario

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_by for 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 = uncapped

    • limits_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?