SoftAggregator API documentation
Integrate 40,000+ casino games through one seamless-wallet API. This reference covers everything you need to list games, create players, launch games, award free rounds and handle wallet callbacks.
Introduction
The SoftAggregator API uses a seamless wallet model: your platform keeps player balances, and our API calls your callback URL for every balance check, bet and win. All API methods are sent as POST requests with a JSON body to a single endpoint.
Base URL
https://api.softaggregator.com/api/v1
Authentication
Every request includes your api_login and api_password (issued in your operator backend after approval). The method field selects the operation.
{
"api_login": "YOUR_API_LOGIN",
"api_password": "YOUR_API_PASSWORD",
"method": "getGameList"
}
Multiple websites on one account
You can run several websites or projects on a single SoftAggregator account. Add each site in your operator backend (API integration → Your other sites) and it is issued its own api_login, api_password, salt_key and its own callback URL. Your credit balance, game catalogue, pricing and billing stay shared on the one account.
| Shared across your sites | Per site |
|---|---|
| Credit balance, game catalogue, prices, invoicing, reporting | api_login / api_password / salt_key, callback URL, default player currency, players |
Each site also carries its own default player currency, set in the same screen. It is only a default: the currency field you send on createPlayer / getGame still wins, so per-player multi-currency keeps working exactly as it does today. Order of precedence: currency on the call → the currency already recorded for that player → the site's default → your account default.
Always call us with the keys of the site the player belongs to. Players are scoped to the site whose keys created them, so two sites may safely use the same user_username (e.g. 1042 on both) without ever touching each other's wallet: each bet and win is sent back to the callback URL of that site only, with the username exactly as you sent it.
Nothing changes if you run a single website — your existing keys and callback URL keep working as they are.
Currencies
Every money method — createPlayer, getGame, getGameDemo and addFreeRounds — accepts an optional currency field (ISO 4217 code, e.g. EUR, USD, BRL, INR, PKR). It sets the currency the player sees in the game — bet limits and balance.
| When | Behaviour |
|---|---|
currency omitted | The default currency of the site whose keys made the call is used — and your account default when that site has none. |
currency provided | Must be a currency enabled for your account — i.e. supported by the products you have enabled. A currency that is not supported makes the launch fail. |
| Per player | A player's currency is set the first time the player is created and does not change afterwards. Use a distinct user_username if you need the same person in another currency. |
Wallet callbacks (balance / debit / credit) carry the same currency, and amount is always in that currency's minor units (e.g. 2.50 → 250). The currencies available to you are provisioned by SoftAggregator — contact support or @mentionso to enable a new currency on your account.
getCurrencies below.getCurrenciesGet your currencies
Returns the currencies enabled for your account, so you can pick the right currency value programmatically instead of hard-coding it.
POST /api/v1
{ "api_login":"...", "api_password":"...", "method":"getCurrencies" }
→ {
"error": 0,
"response": {
"default": "PKR", // send this on getGame / createPlayer (or omit to use it)
"currencies": ["PKR","EUR"], // currencies your account may use
"settlement": "EUR" // currency your account is billed in
}
}
getGameListGet game list
Returns all games available to you. Use each game's id_hash when launching a game.
POST /api/v1
{ "api_login": "...", "api_password": "...", "method": "getGameList" }
→ {
"error": 0,
"response": [
{
"id": 483,
"name": "Game Name",
"game_type": "slots", // normalised category — use THIS to build your lobby
"provider": "pragmaticplay", // studio key, matches getProviderList
"provider_name": "Pragmatic Play",
"provider_logo": "https://cdn.imagesai.org/p/pragmaticplay.png",
"type": "Video Slots", // raw studio label — inconsistent, kept for compatibility
"category": "pragmaticplay", // raw studio code — kept for compatibility
"id_hash": "provider/GameName",
"freerounds_supported": true,
"play_for_fun_supported": true,
"image": "https://.../GameName.webp"
}
]
}
game_type, not type.
type and category are passed through from the game studios and are
not consistent: there are two dozen different labels for a handful of real categories, and
many crash games arrive labelled as slots. game_type is our normalised value and
is one of a fixed list: slots, live, crash,
table, instant, bingo, keno,
fishing, virtual, sportsbook, other.
The list will not change without notice. type and category are
unchanged, so existing integrations keep working.getProviderListGet providers
Returns the game studios available to you, with a display name and a logo. Each
provider value matches the provider field of every game in
getGameList, so you can build a provider filter with a straight one-to-one match.
Studios that publish under several codes (for example two Pragmatic Play feeds) are returned
once.
POST /api/v1
{ "api_login": "...", "api_password": "...", "method": "getProviderList" }
→ {
"error": 0,
"response": [
{
"provider": "pragmaticplay",
"name": "Pragmatic Play",
"logo": "https://cdn.imagesai.org/p/pragmaticplay.png",
"games": 3410
}
]
}
Logos are PNG, 400×160, served from our own CDN — no external hosts, nothing that can be filtered by a national ISP. They are cached for one day, so a logo we improve reaches your players without any change on your side.
createPlayerCreate player
Creates (or ensures) a player on the system before launching a game.
{ "api_login":"...", "api_password":"...", "method":"createPlayer",
"user_username":"player123", "user_password":"secret", "currency":"EUR" }
→ { "error": 0, "response": { ... } }
getGameLaunch game
Returns the URL to load the game in an iframe. Use the id_hash from getGameList as gameid.
| Field | Description |
|---|---|
user_username | Player username |
user_password | Player password |
gameid | Game id_hash |
lang | Language code, e.g. en |
currency | Optional — player currency (see Currencies). Defaults to your account currency. |
device | Optional — desktop (default) or mobile. Some game platforms refuse a launch that does not declare the device. |
country | Player country, ISO 3166 alpha-2 (e.g. ET, BR). Optional by default, required on accounts serving a restricted market — see the note below. user_country is accepted as an alias. |
homeurl | Return URL after the player exits |
play_for_fun | Optional — demo mode |
{ "api_login":"...", "api_password":"...", "method":"getGame",
"user_username":"player123", "user_password":"secret",
"gameid":"provider/GameName", "lang":"en", "currency":"EUR",
"country":"BR", "homeurl":"https://yourcasino.com" }
→ { "error": 0, "response": "https://game-launch-url..." }
method: "getGameDemo" with the same parameters. country is never required in demo mode.Restricted markets. Studios are not licensed everywhere, and a few of them exclude specific countries. We enforce that per player, from the country you send on the launch — we never guess it from an IP address. A game the player's country is not allowed to open is refused with Game not available in your country; everything else launches normally. If your account serves a market where studio restrictions apply, we switch country from optional to required and a launch without it is refused with country is required on getGame. Send it on every real-money launch and you are covered whatever we enable later.
addFreeRoundsFree rounds
Award free spins on a supported game (freerounds_supported: true in the game list).
{ "api_login":"...", "api_password":"...", "method":"addFreeRounds",
"user_username":"player123", "user_password":"secret",
"gameid":"provider/GameName", "freespins":10, "bet_level":0,
"valid_days":7, "currency":"EUR" }
Use getFreeRounds to list and deleteFreeRounds to remove them.
getPlayerHistoryPlayer history
Returns the round history of one of your players, newest first. One entry per game round (all legs of a round are merged). Amounts are integers in cents, in the currency the round was played in.
{ "api_login":"...", "api_password":"...", "method":"getPlayerHistory",
"user_username":"player123",
"limit":50, "offset":0,
"date_from":"2026-08-01 00:00:00", "date_to":"2026-08-01 23:59:59",
"gameid":"x6046f4988155b46", "round_id":"..." }
Only user_username is required. limit defaults to 50 (max 500); gameid is the id_hash from getGameList; date_from/date_to are UTC.
{ "error":0, "response":{
"user_username":"player123", "total":128, "limit":50, "offset":0,
"rounds":[
{ "round_id":"88c6807b55e7434c3e00aa50dc82240e",
"gameid":"x6046f4988155b46", "game_name":"Gates of Olympus 1000",
"currency":"USD", "bet":40, "win":58, "net":18,
"legs":2, "rolled_back":false,
"started_at":"2026-08-01 16:10:15", "ended_at":"2026-08-01 16:10:24" } ] } }
| Field | Meaning |
|---|---|
total | Rounds matching the filter (for paging), not the number returned |
bet / win | Total wagered / paid for the round, in cents |
net | win - bet: negative = player lost the round |
legs | Wallet movements merged into this round |
rolled_back | true if the round was cancelled/refunded |
Wallet callbacks
We call your callback URL (set in your operator backend — one per website, see Multiple websites) for every player balance change. Callbacks are sent as GET requests. Your endpoint must always return HTTP 200; communicate errors only via the JSON body. Balances are always integers in cents (e.g. €2.50 → 250).
action: balance
Sent at the start of a session to read the player's balance.
GET {your_callback}?username=player123¤cy=EUR&action=balance
&call_id=...×tamp=...&key=...
← { "error": 0, "balance": 250 }
action: debit / credit
debit subtracts a bet; credit adds a win. call_id is the unique transaction id — use it to de-duplicate. If funds are insufficient on a debit, return error: 1 with the current balance.
GET {your_callback}?username=player123¤cy=EUR&action=debit
&amount=100&type=bet&round_id=...&game_id=provider/GameName
&call_id=...×tamp=...&rb=0&key=...
← { "error": 0, "balance": 150 }
Two rules your handler must follow.
- Never reject a callback because of
type. Branch onactiononly.typeis informational and its set of values grows as we add providers — treat an unknown value as a normal bet or win. - A
creditcan arrive with no matchingdebit. Free spins, bonus rounds and jackpot payouts are wins withamountonly — there is no bet to match them against. De-duplicate oncall_idalone; requiring a prior debit means your players never get paid for free-spin wins.
| Field | Description |
|---|---|
amount | Amount in cents |
action | debit | credit | balance |
type | Informational, never reject on it. bet on a debit, win on a credit; bonus_fs marks a free spin (nothing to deduct on debit). Other values may appear. |
call_id | Unique transaction id (de-dup) |
round_id | Game round id |
game_id | Game id_hash |
rb | 1 = rollback (process like a normal debit/credit) |
Signature validation
Every callback carries timestamp and key. Validate that the timestamp is within 30 seconds and that the key matches:
key = md5(timestamp + your_salt_key)
Your salt_key is shown in your operator backend. If validation fails, return { "error": 2, "balance": 0 }.
Error codes
| Code | Meaning |
|---|---|
0 | Success |
1 | Insufficient balance (return current balance) |
2 | Processing error / invalid signature / player not found |
Need help integrating? Contact us on Telegram @mentionso or email [email protected].
API changes, new game providers and planned maintenance are announced on our Telegram channel — @casinogamessoftware. Worth following if you maintain this integration.