Partner-facing API
External Game Aggregator API
One integration for player creation, game launch, and seamless wallet settlement. The aggregator layer handles the provider bridge while your wallet remains the balance source of truth.
- Auth
x-api-key- Format
- JSON or form body
- Wallet
- HMAC-SHA256
Base URL
All API requests are made to your assigned base URL. Replace https://api.example.com with the host provided to you.
https://api.example.com
Authentication
Authenticate every request with your secret API key in the x-api-key header. Your key is unique to your account and must be kept private - never expose it in client-side code.
| Header | Required | Description |
|---|---|---|
x-api-key | required | Your secret API key. |
x-api-prefix | optional | Your account prefix. Speeds up routing when supplied; otherwise it is resolved from the key. |
Content-Type | optional | application/json (recommended) or application/x-www-form-urlencoded. |
curl https://api.example.com/game/provider \
-H "x-api-key: YOUR_API_KEY"
IP whitelist
For an extra layer of security your account can be locked to a list of allowed source IP addresses. When a whitelist is configured, requests from any other address are rejected with 403.
- Provide the public IPs (or CIDR ranges) of your servers to enable the whitelist.
- Both single addresses (
203.0.113.10) and CIDR ranges (203.0.113.0/24) are supported. - Leave it unset to allow any source IP (API key only).
Conventions
- Request bodies may be sent as JSON or form-encoded. Responses are always JSON.
- The
user_agentis the unique identifier of a player, returned by Register (for exampleLAG1.0632094194). - Monetary amounts are decimal numbers in your account currency.
- Timestamps are ISO-8601 strings in UTC.
/create-user
Register
Create a player in the system. The returned user_agent is the handle you use for every subsequent call for this player.
Body parameters
| Field | Type | Description | |
|---|---|---|---|
user_agent | string | required | Your unique player reference. Send either the local value (0632094194) or the full public value (LAG1.0632094194); if the prefix is already present it will be normalised before creation. |
firstname | string | optional | Player first name. |
lastname | string | optional | Player last name. |
password | string | optional | Player password. |
phone | string | optional | Player phone number. |
ip | string | optional | Player IP address. |
curl https://api.example.com/create-user \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"user_agent": "0632094194",
"firstname": "Test",
"lastname": "01",
"password": "aa1234",
"phone": "0632094194",
"ip": "139.180.143.22"
}'
{
"user_agent": "LAG1.0632094194"
}
PREFIX.local_user. For example, both 0632094194 and LAG1.0632094194 create or return LAG1.0632094194, never LAG1.LAG1.0632094194.
/game/provider
Product list
Return the list of available game providers (products).
curl https://api.example.com/game/provider \
-H "x-api-key: YOUR_API_KEY"
[
{
"provider_name": "PGSoft",
"display_name": "pg soft",
"image": "https://img.example.com/providers/pg.jpg"
},
{
"provider_name": "Evolution Gaming",
"display_name": "evolution gaming",
"image": "https://img.example.com/providers/evo.jpg"
}
]
/game/list
Game list
Return the games offered by a single provider.
Body parameters
| Field | Type | Description | |
|---|---|---|---|
game_provider | string | required | provider_name from Product list. |
mobile | boolean | optional | Return the mobile catalogue. |
curl https://api.example.com/game/list \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "game_provider": "PGSoft", "mobile": true }'
{
"is_item": true,
"items": [
{
"game_id": "1513328",
"game_name": "Super Golf Drive",
"game_provider": "PGSoft",
"game_type": "slot",
"game_image": "https://cdn.example.com/pgsoft/spr-golf-drive.png",
"is_active": true
}
]
}
/game/open-game
Open game
Create a launch URL for a player. Redirect the player to the returned url to start playing.
Body parameters
| Field | Type | Description | |
|---|---|---|---|
user_agent | string | required | Player handle from Register. |
game_provider | string | required | provider_name of the game. |
game_id | string | required | game_id from Game list. Use 0 for the provider lobby. |
redirect_url | string | optional | Where to return the player after the game. |
mobile | boolean | optional | Launch the mobile client. |
ip | string | optional | Player IP address. |
curl https://api.example.com/game/open-game \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"user_agent": "LAG1.0632094194",
"game_provider": "PGSoft",
"game_id": "1513328",
"redirect_url": "https://your-site.com/lobby",
"mobile": true,
"ip": "139.180.143.22"
}'
{
"is_item": false,
"url": "https://play.example.com/launch?token=..."
}
/bet-history/report/detail-bet
Bet history
Return a detail view for a single bet. The item is a URL that renders the round result.
Body parameters
| Field | Type | Description | |
|---|---|---|---|
bet_id | string | required | The public bet reference received in the wallet callback or History callback. |
curl https://api.example.com/bet-history/report/detail-bet \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "bet_id": "64aa6ac13b909c3b91986abe" }'
{
"status": true,
"item": "https://history.example.com/redirect.html?trace_id=..."
}
Overview
In the seamless wallet model you hold the player balance. When a player bets or wins, our platform calls your wallet endpoints in real time and uses your response as the source of truth - no balance transfers are required.
You implement and host the five callback endpoints below under a single base URL (your wallet URL). Every request is signed with HMAC-SHA256 so you can verify it is authentic.
| Callback | When we call it |
|---|---|
POST /balance | To read the player's current balance. |
POST /bet | To debit a bet amount. |
POST /settle-bet | To credit the payout for a previously debited bet. |
POST /bet-end | To debit and finalise an immediate-result round in one callback. |
POST /history | To deliver a bet record for your reporting. |
bet_id identifies the public bet record used by /history and /bet-history/report/detail-bet. txn_id identifies the current wallet callback, and the following /history callback echoes the same txn_id.
Wallet callback response contract
| Result | HTTP status | Response body | Meaning |
|---|---|---|---|
| Applied successfully | 2xx | JSON with a finite numeric balance. | balance is the authoritative post-operation wallet balance. A genuine value of 0 is valid. |
| Rejected, not applied | 409 or 422 | Optional error JSON; omit balance. | The wallet guarantees that this callback made no balance change. |
| Technical/unknown failure | Other non-2xx | Optional error JSON; omit balance. | The operation is failed, but the platform does not infer a post-operation balance. |
{
"code": "INSUFFICIENT_FUNDS",
"error": "insufficient balance"
}
{
"balance": 0
}
200 with balance: 0 for a rejected operation. Use 409 or 422 and omit balance. If a repeated txn_id was already applied, return the original successful result idempotently; do not report it as not applied.
Wallet callback relationship
The money callback always happens first. /history is sent afterwards when the bet record is created or updated. Treat /history as reporting/reconciliation data only; do not change wallet balance from /history.
bet_id
Public bet record id. It is the internal history row _id, not the provider transaction id.
txn_id
Unique id for one wallet movement. The matching /history callback repeats the same value.
/history
Confirms the record details for the preceding /bet, /settle-bet, or /bet-end.
Debit then settle - one history row
Use this model when the provider first debits a stake and later settles the same round. The settle callback uses the same bet_id as the original bet, while txn_id changes for each wallet movement.
Debit then settle - separate history rows
Some providers store the bet and the settle as separate reporting rows. In that case each money callback gets its own bet_id, and each following /history uses the matching pair.
Immediate-result round
Use /bet-end only when one provider callback contains both the stake and final payout. The wallet should debit the stake and apply the payout in one idempotent operation.
| Callback | Money action | History relationship | Idempotency key |
|---|---|---|---|
/bet | Debit the stake. | The next /history repeats the same bet_id and txn_id. | txn_id for this debit; bet_id for the bet record. |
/settle-bet | Credit the payout. The payout may be 0 when the round closes without a win. | For one-row games it updates the original bet_id. For separate-row games it may use a new settle bet_id. | txn_id for this settle; bet_id for the affected record. |
/bet-end | Debit stake and apply final payout in one operation. | The next /history repeats the same bet_id and txn_id. | txn_id for the combined movement; bet_id for the final record. |
/history | No balance change. | Reporting copy of the record created or updated after the money callback. | Store by bet_id + txn_id to avoid duplicate processing. |
bet_id. The public bet_id must match the _id used by /history and by /bet-history/report/detail-bet.
HMAC signature
Every callback body includes an hmac field. Verify it with your shared AGENT_SECRET before trusting the payload.
- Remove the
hmacfield. The remaining fields are your payload. - Sort the payload keys alphabetically (A to Z).
- Serialise to a compact JSON string (no extra whitespace).
- Compute
HMAC-SHA256of that string withAGENT_SECRETand output hex. - Compare against the received
hmac. If equal, the request is authentic.
const crypto = require('crypto');
function verifyCallbackSignature(reqBody, agentSecret) {
const { hmac, ...payload } = reqBody;
if (!hmac) return false;
const sorted = Object.keys(payload).sort()
.reduce((acc, k) => { acc[k] = payload[k]; return acc; }, {});
const expected = crypto
.createHmac('sha256', agentSecret)
.update(JSON.stringify(sorted))
.digest('hex');
return hmac === expected;
}
// app.post('/bet', (req, res) => {
// if (!verifyCallbackSignature(req.body, AGENT_SECRET))
// return res.status(401).json({ error: 'invalid signature' });
// ...
// });
{wallet}/balanceCheck balance
Return the player's current balance.
{
"user_agent": "LAG1.0632094194",
"hmac": "8f2be832..."
}
{
"balance": 296.59,
"tmp": 0,
"fish_remaining": 0,
"turn_over": 1580.38,
"game_type": "slot"
}
{wallet}/betBet
Debit a bet from the player's balance. Use this for debit-only provider events where the final result is not included yet. Return the resulting balance.
409 or 422. Do not include balance in the response. Never return HTTP 200 with balance: 0 or success: false for a rejected debit, because every 2xx response represents an applied wallet operation.
| Field | Type | Description |
|---|---|---|
user_agent | string | Player handle. |
balance | number | The bet amount to debit. |
bet_id | string | Bet-history reference shared by related callbacks. |
txn_id | string | Unique money-callback transaction id; use this as the idempotency key. The following /history payload will echo this value. |
hmac | string | Signature - verify before processing. |
{
"user_agent": "LAG1.0632094194",
"balance": 5,
"bet_id": "1677950013029748736",
"txn_id": "01KZ9Q2AMJH7W8G8BNC6Q7TEJ4",
"hmac": "8f2be832..."
}
{
"balance": 291.59,
"tmp": 0,
"fish_remaining": 0
}
Rejected debit responses
Use 422 for a known business rejection such as insufficient balance. Use 409 for a known transaction conflict or other deterministic rejection. In both cases, the debit must not have been applied and the response must omit balance. The response body is optional.
HTTP/1.1 422 Unprocessable Content
Content-Length: 0
HTTP/1.1 409 Conflict
Content-Type: application/json
{
"code": "BET_REJECTED",
"message": "Debit was not applied"
}
txn_id. A repeated txn_id must never move money a second time and must return the original HTTP status and body. If the first request was applied, replay the original successful 2xx response with its original balance. If it was rejected, replay the original 409 or 422 response.
{wallet}/settle-betSettle bet
Credit the payout for a previously debited bet. Use this when the provider settles after /bet; use /bet-end instead when stake and final payout arrive together.
| Field | Type | Description |
|---|---|---|
user_agent | string | Player handle. |
bet | number | The original bet amount. |
balance | number | The payout amount to credit. It may be 0 when the round closes without a win. |
bet_id | string | Unique bet reference. |
txn_id | string | Unique money-callback transaction id; use this as the idempotency key. The following /history payload will echo this value. |
hmac | string | Signature. |
{
"user_agent": "LAG1.0632094194",
"bet": 5,
"balance": 20,
"bet_id": "1677950013029748736",
"txn_id": "01KZ9Q2BK6XRE9G1WKY4K1TW7B",
"hmac": "8f2be832..."
}
{
"balance": 311.59,
"tmp": 0,
"fish_remaining": 0
}
{wallet}/bet-endBet end
Finalise an immediate-result round. Sent with both the bet amount and final win amount when the provider reports the result in the same callback.
409 or 422 and omit balance. Never return a rejection after applying only part of the operation.
| Field | Type | Description |
|---|---|---|
user_agent | string | Player handle. |
balance | number | The bet amount. |
win_balance | number | The win amount. |
bet_id | string | Unique bet reference. |
txn_id | string | Unique money-callback transaction id; use this as the idempotency key. The following /history payload will echo this value. |
hmac | string | Signature. |
{
"user_agent": "LAG1.0632094194",
"balance": 5,
"win_balance": 20,
"bet_id": "1677950013029748736",
"txn_id": "01KZ9Q2CSYYFA5QHHKHPD12F3Y",
"hmac": "8f2be832..."
}
{
"balance": 311.59,
"tmp": 0,
"fish_remaining": 0
}
Rejected bet-end responses
Use 422 for a known business rejection such as insufficient balance. Use 409 for a known transaction conflict or other deterministic rejection. Both statuses mean the complete bet-end operation was not applied: no stake was debited, no payout was credited, and the response omits balance. The response body is optional.
HTTP/1.1 422 Unprocessable Content
Content-Length: 0
HTTP/1.1 409 Conflict
Content-Type: application/json
{
"code": "BET_END_REJECTED",
"message": "Bet-end was not applied"
}
txn_id. A repeated txn_id must not debit the stake or credit the payout again. Replay an applied operation with its original successful 2xx response and original balance; replay a rejected operation with its original 409 or 422 response.
{wallet}/historyHistory
Delivered whenever a bet record is created or updated, so you can keep a full copy for reporting and reconciliation. This callback is not a wallet movement. Acknowledge with HTTP 200.
| Field | Type | Description |
|---|---|---|
bet_id | string | Public bet record id. This matches the bet_id sent in the wallet callback. |
txn_id | string | Wallet callback transaction id. This matches the preceding /bet, /settle-bet, or /bet-end callback. |
createdAt | string | Created timestamp (ISO-8601). |
updatedAt | string | Updated timestamp (ISO-8601). |
provider | string | Provider name. |
game | string | Game name. |
before_balance | number | Balance before the round. |
after_balance | number | Balance after the round. |
used_balance | number | Amount staked. |
user_agent | string | Player handle. |
receive_balance | number | Amount returned. |
winlose | number | Net win (+) or loss (-). |
hmac | string | Signature. |
{
"bet_id": "1677950013029748736",
"txn_id": "01KZ9Q2BK6XRE9G1WKY4K1TW7B",
"createdAt": "2026-06-04T09:30:00.000Z",
"updatedAt": "2026-06-04T09:30:04.000Z",
"provider": "PGSoft",
"game": "Super Golf Drive",
"before_balance": 296.59,
"after_balance": 311.59,
"used_balance": 5,
"user_agent": "LAG1.0632094194",
"receive_balance": 20,
"winlose": 15,
"hmac": "8f2be832..."
}
Errors
The API uses standard HTTP status codes. Authentication and authorisation failures return a JSON body with an error message.
| Status | Meaning | Body |
|---|---|---|
| 200 | Success. | Endpoint payload. |
| 401 | Missing or invalid API key. | { "error": "Invalid API key" } |
| 403 | IP not allowed, or account not permitted. | { "error": "IP not allowed" } |
| 404 | Unknown route. | { "error": "Not Found" } |
| 500 | Unexpected server error. | { "error": "..." } |
Integration flow
A typical end-to-end integration looks like this:
- Register each of your players once with
/create-userand store the returneduser_agent. - Browse games with
/game/providerand/game/list. - Launch a game with
/game/open-gameand redirect the player to the returned URL. - Settle play as it happens - implement the seamless wallet callbacks so we can read balance, debit bets, and credit wins against your wallet.
- Reconcile using the
/historyfeed and/bet-history/report/detail-bet.
AGENT_SECRET, and to register your wallet URL and IP whitelist.