The two directions
| Integration | Direction | What it carries |
|---|---|---|
| Catalogue & launch | operator → provider | which games exist, and opening a game for a player |
| Wallet | provider → operator | validate session, balance, debit, credit, rollback |
Authentication: one scheme, both directions
X-Signature: hex(HMAC-SHA256(request_body, secret))
Sign the body exactly as sent.
timestamp goes inside the signed body, in milliseconds.
There is no separate header. Requests outside a 5 minute window are
rejected on both sides.
Every request carries operator and timestamp. Requests we
send you also carry request_id and currency.
Unknown operator, disabled operator, missing secret, wrong signature: all return
the same 401 INVALID_SIGNATURE.
What the provider exposes
operator → providerBase URL: https://api-games.greenstudios.com.br/v1
{"operator":"test-operator", "timestamp":1755680000000,
"currency":"BRL", "limit":100, "offset":0}
{
"status": "ok",
"operator": "test-operator",
"count": 1,
"total": 60,
"offset": 0,
"currencies": ["BRL", "USD"],
"games": [
{
"game_id": "ronda-online",
"name": "Ronda Online",
"studio": "greenstudios",
"category": "LIVE",
"demo": true, "mobile": true, "desktop": true, "freebet": true,
"image_url": "https://greenstudios.com.br/thumbnails/300x300/ronda-online-thumb.webp",
"currency": "BRL",
"min_bet": …, "max_bet": …, "max_win": …
}
]
}
count is the size of this page, total is the
whole catalogue. A total larger than count means there is
more: ask for the next page with offset.
freebet says whether the game accepts free rounds granted by the
operator.
The three limits
They appear only when you send currency, hold for that
currency, and are in integer cents.
| Field | Meaning |
|---|---|
min_bet | smallest accepted stake |
max_bet | largest accepted stake |
max_win | payout ceiling; a bigger prize is paid at the ceiling |
A missing limit field means the limit is unknown, not that there is none.
image_url is absent when the game has no artwork.
currencies is the set of currencies enabled for you.
Returns the URL to load in an iframe or redirect.
{
"operator": "test-operator", "timestamp": 1755680000000,
"game_id": "ronda-online",
"player_id": "618004",
"token": "your-session-token",
"currency": "BRL",
"language": "pt",
"demo": false, "mobile": true,
"lobby_url": "https://your-site/lobby",
"deposit_url": "https://your-site/deposit"
}
| Field | Required | Notes |
|---|---|---|
game_id | yes | must be in your catalogue |
token | unless demo | sent on every wallet call |
player_id | unless demo | your player identifier |
currency | recommended | the session currency |
language | no | ISO 639-1. Default: en |
demo, mobile | no | demo needs neither token nor player |
{"status":"ok", "url":"https://…/?gameId=…&casinoId=…&session=…&lang=…"}
The URL carries a session id. Your token and
player_id are not in it: we take them server to server and
exchange them for that id. Open the URL within 15
minutes.
Errors: MISSING_GAME_ID, MISSING_TOKEN,
MISSING_PLAYER_ID (400), GAME_NOT_FOUND (404).
Currencies and languages
All money is integer cents. 1500 means 15.00 in the session currency.
A monetary field in currency units, or in floating point, is invalid.
Currencies
29 currencies are supported. The set enabled for one operator comes back in
currencies from POST /v1/games.
Languages
pt, en and es. Any other value, and an absent
language, falls back to en.
What the operator exposes
provider → operatorFive routes.
POST {wallet_url}/session token → player, balance, currency
POST {wallet_url}/balance balance
POST {wallet_url}/bet debit
POST {wallet_url}/win credit
POST {wallet_url}/rollback refund
Response format
{"status":"ok", "balance":98500, "currency":"BRL",
"player_id":"618004", "transaction_id":"…"}
{"status":"error", "error_code":"INSUFFICIENT_FUNDS", "error_message":"…"}
balance is integer cents, after the operation, and is
required on success.
currency is the currency of the account. Send it on every
response.
The requests we send
| Route | Fields |
|---|---|
/session | token |
/balance | player_id, token |
/bet | player_id, token, amount, transaction_id, round_id |
/win | player_id, token, amount, transaction_id, bet_id, round_id, type |
/rollback | player_id, token, amount, transaction_id, bet_id, round_id, external_bet_id |
Plus timestamp, request_id and currency on every
one, and game_id when the game is known.
Error codes
| Code | Meaning | We retry? |
|---|---|---|
INSUFFICIENT_FUNDS | not enough balance | no |
TOKEN_EXPIRED | session expired | no |
TOKEN_INVALID | session never existed | no |
PLAYER_NOT_FOUND | unknown player | no |
BET_NOT_FOUND | the referenced bet does not exist | no |
CURRENCY_MISMATCH | wrong currency for this account | no |
INTERNAL_ERROR | your side failed | yes |
Any code we do not recognise is treated as retryable.
Identity and session
The token is the session identity.
The token is random.
It must not equal the player_id, be derived from it, or be your own site
session id. We refuse a launch whose token equals the player_id or is
shorter than 16 characters, with INVALID_TOKEN.
A new token for every /v1/launch.
A token belongs to one player.
If a second player arrives with a token already in use, we refuse the session and link nothing.
Your wallet attests the token owner.
Every /balance success response carries player_id: the player
that token belongs to. When it differs from the player of the session, we refuse and
the game does not open.
An expired token refuses every call.
/session, /balance, /bet,
/win and /rollback all return
TOKEN_EXPIRED.
The rules that actually break integrations
Idempotency
The same transaction_id must never debit twice. We
retry when we do not get a response.
Store the response, not only the id: the second call returns the same balance as the first.
Business refusals are not memorised.
A zero-amount credit is valid and must be accepted
It is the terminal event of a losing round.
Business errors are HTTP 200, not 5xx
Insufficient funds is a 200 with an error_code.
5xx is treated as a transport failure and retried.
Currency belongs to the session, not to the operator
It is fixed at launch and is sent on every wallet call.
Exactly one terminal event per round
Every round ends exactly once, with a win or with a zero-amount credit.
Optional capabilities
Declared at provisioning.
| Capability | What it means |
|---|---|
freebet_mode: per_round | default. Free rounds notified one by one; each prize credited as it happens |
freebet_mode: aggregate | free rounds produce one credit at the end |
zero_win | you require the zero-amount credit to close a lost free round |
The two freebet modes are mutually exclusive.