Overview
Appixa is a B2B iGaming aggregator. One integration gives your platform access to every game provider we carry — slots, live casino, crash games and sportsbook — through a single wallet API.
Base URL
https://appixa-api.comAll requests and responses are JSON. All endpoints require TLS. Timestamps are ISO 8601 UTC.
How a game round works
- Your platform calls
POST /api/game/launchwith a player ID and a game ID. - Appixa returns a launch URL. You open it in an iframe or redirect the player to it.
- The game provider sends bet/win events to Appixa.
- Appixa moves the money — from its own ledger (transfer wallet) or by calling your wallet (seamless wallet).
- Appixa notifies you of every movement, then deducts the GGR fee from your prepaid balance.
GGR balance low.
Authentication
Every request carries your API key as a bearer token.
Authorization: Bearer <your_api_key>
Content-Type: application/jsonOptional IP allow-list
You can restrict your API key to specific source IPs. While an allow-list is set, requests from any other address are rejected with 403 IP not whitelisted.
Rate limit
500 requests per minute per IP. Request bodies are capped at 2 MB.
Wallet models
Pick one at onboarding. It determines who holds the player's money.
Transfer wallet
Appixa holds the player balance. You move funds in with /player/deposit. Appixa notifies you of each debit and credit via your webhook.
Seamless wallet
You hold the player balance. Appixa calls your endpoints in real time for every balance read, debit and credit. No pre-funding of players needed.
| Transfer | Seamless | |
|---|---|---|
| Player balance held by | Appixa | You |
| You must implement | One webhook receiver | Three endpoints (balance, debit, credit) |
| Player funding | POST /player/deposit | Not required |
| Latency sensitivity | Low | High — respond under 8s |
Quick start
A working launch in three calls.
1 · Confirm your account is reachable
curl https://appixa-api.com/health{ "status": "ok", "time": "2026-09-30T10:00:00.000Z" }2 · Find a game
curl -H "Authorization: Bearer $APPIXA_KEY" \
"https://appixa-api.com/api/game/list?limit=5"3 · Launch it
curl -X POST https://appixa-api.com/api/game/launch \
-H "Authorization: Bearer $APPIXA_KEY" \
-H "Content-Type: application/json" \
-d '{"ext_player_id":"player_001","game_uid":"KAGG_Zorro"}'Open the returned url in an iframe. That is the whole integration for transfer wallet.
Game list
Returns the games enabled for your account. Use it to build your lobby and to cache game_uid values.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| page | integer | optional | Page number, 1-based. Default 1. |
| pageSize | integer | optional | Rows per page. Default 50. |
| provider | string | optional | Filter by studio code, e.g. PGS, EVO. |
| search | string | optional | Substring match on the game name. |
Response 200
{
"Code": 0,
"Value": {
"Total": 12724,
"Games": [
{
"Code": "KAGG_Zorro",
"Name": "The Mask of Zorro",
"sub_provider": "KAGG",
"ProviderCode": "KAGG",
"ProviderName": "KA Gaming",
"Thumbnails": [
{ "Language": "en", "ImagePath": "https://cdn.example.com/zorro.png" }
],
"synced_env": "production"
}
]
}
}Code: 0 means success. Value.Games[].Code is the game_uid you pass to /api/game/launch. Each entry also carries any extra metadata the studio publishes, so treat the object as open-ended and read only the fields you need.page and pageSize.Launch game
Creates a game session and returns the URL to load for the player. Call it every time a player opens a game — URLs are single-session and expire.
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
| ext_player_id | string | required | Your player identifier. Unique within your account, created automatically on first use. Must be 1–40 lowercase alphanumeric characters (a-z, 0-9) — no underscores, hyphens, dots or uppercase. |
| game_uid | string | required | Game identifier from /api/game/list. |
| currency | string | optional | ISO 4217 code. Must be in your allowed currencies. Defaults to your account currency. |
Request
curl -X POST https://appixa-api.com/api/game/launch \
-H "Authorization: Bearer $APPIXA_KEY" \
-H "Content-Type: application/json" \
-d '{
"ext_player_id": "player_001",
"game_uid": "KAGG_Zorro",
"currency": "TRY"
}'Response 200
The response carries either url or html, depending on the game. Handle both.
// Casino, slots, crash — the usual case
{ "url": "https://launch.provider.com/game?token=eyJhbGciOi..." }
// Sportsbook (LuckySports) — a pre-rendered document
{ "html": "<!DOCTYPE html><html>..." }url is always present. Launching the sportsbook returns html instead, and reading url there gives undefined — a blank game window. Branch on whichever key is present.Embedding
const r = await fetch('https://appixa-api.com/api/game/launch', {
method: 'POST',
headers: { 'Authorization': 'Bearer ' + KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ ext_player_id: 'player_001', game_uid: 'KAGG_Zorro' })
});
const data = await r.json();
const frame = document.getElementById('game');
if (data.url) frame.src = data.url;
else if (data.html) frame.srcdoc = data.html;
else /* show the error in data.error */;<iframe id="game"
style="width:100%;height:100%;border:0"
allow="autoplay; fullscreen"
allowfullscreen></iframe>403 GGR balance low before the session is created. Top up to resume — it takes effect immediately, with no restart or re-integration.Errors
| Status | Error | Cause |
|---|---|---|
| 400 | ext_player_id is required | Field missing or empty. |
| 400 | game_uid is required | Field missing or empty. |
| 400 | ext_player_id must be 1-40 lowercase alphanumeric … | Player id contains characters the provider rejects. |
| 401 | Invalid API key | Key unknown or malformed. |
| 403 | GGR balance low | Prepaid balance exhausted. |
| 403 | Client account is suspended | Account not active. |
| 404 | Game not found | game_uid unknown or not enabled for you. |
Player balance
Reads a player's current balance. Transfer wallet only — under seamless wallet the balance lives on your side.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| ext_player_id | string | required | Your player identifier. |
curl -H "Authorization: Bearer $APPIXA_KEY" \
"https://appixa-api.com/api/game/player/balance?ext_player_id=player_001"Response 200
{
"ext_player_id": "player_001",
"balance": "1250.00",
"currency": "TRY"
}Player deposit
Moves funds into a player's Appixa wallet. Transfer wallet only.
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
| ext_player_id | string | required | Your player identifier. |
| amount | number | required | Positive amount to credit. |
| txn_id | string | optional | Your unique transaction reference, used as the idempotency key. Technically optional — but see the warning below. Always send it. |
| currency | string | optional | Defaults to the player's currency. |
curl -X POST https://appixa-api.com/api/game/player/deposit \
-H "Authorization: Bearer $APPIXA_KEY" \
-H "Content-Type: application/json" \
-d '{
"ext_player_id": "player_001",
"amount": 500.00,
"txn_id": "dep_20260930_0001",
"currency": "TRY"
}'Response 200
{
"success": true,
"balance": "1750.00",
"txn_id": "dep_20260930_0001"
}txn_id. Re-sending the same txn_id does not credit twice. The repeat response carries "duplicate": true and the balance from the original call — safe to retry on timeout.txn_id. If you omit it the server generates a random one, so every retry looks like a brand-new deposit and credits the player again. A timeout you retry without txn_id will double the money. Use your own ledger reference and keep it stable across retries.Providers
Lists every game studio with its game count. Use it to build lobby filters, then pass a code as the provider parameter on /api/game/list.
Response 200
{
"Code": 0,
"Value": [
{ "code": "PGS", "name": "PGS", "aggregator": "ELITEHUB", "gameCount": 172 },
{ "code": "EVO", "name": "EVO", "aggregator": "ELITEHUB", "gameCount": 210 }
]
}/api/game/list returns for your account.Seamless wallet
Under the seamless model you keep the player balance and expose three endpoints. Appixa calls them synchronously during play, so they must be fast and always answer in the documented shape.
You register one base URL. Appixa appends the action:
POST {your_seamless_url}/balance
POST {your_seamless_url}/debit
POST {your_seamless_url}/creditRequest signing
Every call carries an HMAC-SHA256 signature over the raw request body.
Content-Type: application/json
X-Appixa-Timestamp: 1790000000
X-Appixa-Signature: 9f2c... // hex
signature = HMAC_SHA256(seamless_secret, timestamp + raw_body)Verification example (Node.js)
const crypto = require('crypto');
// Mount with a raw body parser so `req.body` is a Buffer:
// app.use('/wallet', express.raw({ type: 'application/json' }));
function verify(req) {
const ts = req.headers['x-appixa-timestamp'];
const sig = req.headers['x-appixa-signature'];
const raw = req.body.toString('utf8');
if (Math.abs(Date.now()/1000 - Number(ts)) > 300) return false;
const want = crypto.createHmac('sha256', SEAMLESS_SECRET)
.update(ts + raw).digest('hex');
return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(want));
}POST /balance
// Appixa sends
{ "ext_player_id": "player_001", "currency": "TRY" }
// You return
{ "status": "success", "balance": 1250.00 }POST /debit
// Appixa sends
{
"ext_player_id": "player_001",
"txn_id": "EH-8891273",
"amount": 10.00,
"currency": "TRY",
"game_uid": "KAGG_Zorro",
"round_id": "R-55231",
"type": "bet",
"provider_id": 1
}
// You return
{
"status": "success",
"balance_before": 1250.00,
"balance": 1240.00
}
// Not enough funds — return code 2001
{ "status": "error", "code": 2001, "message": "Insufficient balance" }POST /credit
Same shape as /debit. type is usually win, refund or rollback.
txn_id with a unique constraint and, on a repeat, return the original result instead of applying the movement again. Answer within 8 seconds — Appixa treats a slower reply as a failure.force: true field may appear on /debit. It marks a rollback that must succeed even if it drives the balance negative. Do not reject it for insufficient funds.Webhooks
Under the transfer model Appixa notifies your callback_url after every confirmed movement. Set the URL from the client panel or POST /api/client/callback-url.
// POST {your_callback_url}
{
"event": "debit",
"txn_id": "3f6a1c2e-...",
"player_id": 4192,
"ext_player_id": "player_001",
"amount": 10.00,
"balance_before": 1250.00,
"balance_after": 1240.00,
"type": "bet",
"game_uid": "KAGG_Zorro",
"round_id": "R-55231",
"currency": "TRY",
"provider_id": 1
}| Field | Meaning |
|---|---|
event | debit (money left the player) or credit (money arrived). |
txn_id | Appixa's ledger reference. Unique and stable. |
type | bet, win, refund, rollback, reserve, release, cancel and related states. |
round_id | Groups every transaction in one game round. Use it to reconcile. |
Respond 200 quickly. Do your processing asynchronously.
round_id to detect anything you missed.Currencies
Currencies you can be onboarded on today. Your account is limited to the subset agreed at onboarding — sending any other code is rejected.
AUD BDT BND BRL EUR INR MYR
NPR PHP SGD THB TRY USD404 Game not found.GGR fees are settled in USD, converted at the daily reference rate on the day the fee is charged.
Error codes
Errors return the matching HTTP status with a JSON body.
{ "error": "GGR balance low" }HTTP status meanings
| Status | Meaning | Retry? |
|---|---|---|
| 200 | Success. | — |
| 400 | Malformed request. A field is missing or invalid. | No — fix the request. |
| 401 | Authentication failed. | No — check the key. |
| 403 | Authenticated but not permitted. | No — resolve the cause. |
| 404 | Resource does not exist. | No. |
| 429 | Rate limit exceeded (500 req/min per IP). | Yes, with backoff. |
| 500 | Server or upstream provider error. | Yes, with backoff. |
Full error reference
| Status | Message | What it means | How to fix |
|---|---|---|---|
| 400 | ext_player_id is required | Player identifier missing. | Send a non-empty ext_player_id. |
| 400 | game_uid is required | Game identifier missing. | Take a game_uid from /api/game/list. |
| 400 | currency is required | Currency missing where it cannot be inferred. | Send an ISO 4217 code you are enabled for. |
| 400 | ext_player_id must be 1-40 lowercase alphanumeric characters (a-z, 0-9) for this provider | Player id contains underscores, hyphens, dots or uppercase. | Normalise your player ids to lowercase letters and digits only, e.g. player_001 → player001. Keep the mapping stable — it identifies the wallet. |
| 400 | amount must be a positive number | Deposit amount is zero, negative or not numeric. | Send a positive number. |
| 400 | Player ID already exists | ext_player_id already registered. | Use the existing player; creation is not needed. |
| 400 | callback_url required | No URL supplied when setting the webhook. | Send an absolute HTTPS URL. |
| 400 | Currency <CUR> is not supported by <Provider> | That studio does not operate in the player's currency. | Launch a game from a studio that supports the currency. See Currencies. |
| 400 | LuckySports not available | Sportsbook not enabled for your account. | Contact your account manager. |
| 401 | API key required | Authorization header absent. | Send Authorization: Bearer <key>. |
| 401 | Invalid API key | Key not recognised. | Check the key; regenerate if leaked. |
| 401 | Invalid credentials | Wrong panel email or password. | Re-enter credentials. |
| 401 | Session expired. Please login again. | Panel JWT expired. | Log in again. |
| 403 | GGR balance low | Prepaid balance is at or below zero. | Top up. Launches resume immediately — no restart needed. |
| 403 | IP not whitelisted | Source IP not in your allow-list. | Add the egress IP of your server. |
| 403 | Client account is suspended | Account suspended. | Contact your account manager. |
| 403 | Account inactive. Please contact support. | Account not activated. | Contact support. |
| 403 | Player blocked | Player status is blocked. | Unblock the player, or route them elsewhere. |
| 404 | Game not found | Unknown game_uid, or not enabled for your currency. | Re-read /api/game/list. Check currency support. |
| 404 | Player not found | No such player on your account. | Check ext_player_id; launching creates it automatically. |
| 500 | Launch failed | Upstream provider rejected the session. | Retry with backoff. Persisting? Send us the timestamp and game_uid. |
| 500 | Failed to get game URL | Provider unreachable or timed out. | Retry with backoff. |
| 500 | 100HP launch failed | 100HP provider error. | Retry with backoff. |
| 500 | Internal error | Unexpected server fault. | Retry. Persisting? Contact support with the timestamp. |
Wallet codes (seamless responses)
Use these in the code field when your seamless endpoint returns "status": "error".
| Code | Meaning | When to return it |
|---|---|---|
| 2001 | Insufficient balance | Player cannot cover the debit. Do not use for a force: true rollback. |
| 1006 | Player not found | ext_player_id unknown on your side. |
| 5001 | Seamless error | Generic failure. Appixa fails the transaction. |
Troubleshooting
Every launch returns 403 GGR balance low
Your prepaid balance has run out. Top it up; the next launch succeeds straight away. This gate applies only to /api/game/launch — /player/balance and /player/deposit keep working so you can always inspect and fund the account.
Seamless signature never matches
Almost always a raw-body problem. Sign timestamp + raw_body using the bytes exactly as received. If your framework parses JSON before you see it, mount a raw body parser on the wallet route.
Launch fails and the player id looks fine
Check the characters. The game provider accepts only lowercase letters and digits in the player id, up to 40 characters. An id like player_001 or Player001 is rejected. Map your internal ids to a compliant form once and store the mapping — the player id identifies the wallet, so it must stay stable.
A game is missing from the list
Either it is not enabled for your account, or it does not support your currency. The catalogue syncs daily, so newly released titles appear within 24 hours.
Duplicate debits after a timeout
Retries are expected. Appixa deduplicates on txn_id, and your seamless endpoint must do the same — store txn_id with a unique constraint and replay the stored result.
Balances drift from your own records
Reconcile by round_id, not by individual transaction. A round can contain a bet, several wins, and a refund or cancellation; only the full set nets out correctly.