Operator integration guide

This is how a casino (an operator) connects to JJgamesph. Your server calls our API to launch players; our server calls your wallet endpoint for every bet and result. Players load the game from our servers in an iframe on your site.

Overview

  1. A player opens Drop Ball in your lobby. Your backend calls POST /api/v1/launch with the player, their current balance and your own session id.
  2. We return a launch_url. Show it in an iframe (or redirect to it).
  3. When the player confirms a bet, we send bet.placed to your callback URL. The bet only counts if you answer 2xx.
  4. When the round is drawn we send round.win or round.lose for each bet. These are retried until you confirm them.
Amounts are strings with two decimals in the player's currency ("150.00"). Drop Ball uses whole pesos, so the decimals are always .00.

Signing requests

Every request you make to us carries three headers:

HeaderValue
X-Operator-CodeYour operator code, e.g. nowarcasino
X-TimestampCurrent Unix time in seconds. Requests more than 5 minutes off are refused.
X-Signaturehex(HMAC-SHA256(secret, timestamp + "." + METHOD + "." + path_with_query + "." + raw_body))
// PHP
$ts   = (string) time();
$body = json_encode($payload);
$sig  = hash_hmac('sha256', $ts . '.POST./api/v1/launch.' . $body, $secret);

A bad or missing signature returns 401 with {"ok":false,"error":{"code":"INVALID_SIGNATURE"}}.

Launch a player

POST/api/v1/launch
FieldDescription
game_code"dropball"
player.idYour player id. Sent back to you in every callback.
player.nameShown in the game header (optional).
balanceThe player's playable balance right now, in whole units.
session_refYour own session id. Sent back as session_uuid in every callback so you can find the session.
currencyDefaults to your operator currency (PHP).
lobby_urlWhere the game's LOBBY button goes.
{
  "game_code": "dropball",
  "player": { "id": "10482", "name": "juan88" },
  "balance": 2500,
  "session_ref": "9f1c2d3e-…",
  "lobby_url": "https://bountyph.com/lobby"
}

// 200
{ "ok": true, "data": {
  "token": "q8…",
  "launch_url": "https://jjgamesph.com/play/?token=q8…",
  "expires_in_idle_minutes": 120 } }

The session stays open while the player is active and closes after 120 idle minutes.

Push a balance

POST/api/v1/sessions/{token}/balance

Send {"balance": 3000} when the player's balance changes outside the game (a deposit, another game). The game updates immediately. POST /api/v1/sessions/{token}/close ends a session.

List games

GET/api/v1/games

Returns each game's code, name, category (category and category_name), whether the play-money demo is on (demo_enabled), limits, paytable, RTP and thumbnail URL, ready for your catalog.

Rounds and tickets

GET/api/v1/rounds?limit=50&before={round_id}
GET/api/v1/rounds/{round_id}
GET/api/v1/tickets?player_id=&round_id=&limit=

A ticket is one confirmed bet (one bet.placed). Tickets carry the stake per card, the win and the status: accepted, settled, rejected or refunded. Use them to reconcile with your ledger.

Credits and charges

Your account is prepaid. You buy credits from us, and each day we charge a fee on that day's play: by default 10% of GGR. GGR is stakes minus wins on settled tickets. The fee terms in your contract are shown in fee_basis and fee_rate_percent.

GET/api/v1/billing
{
  "credit_balance": 98950.00,
  "available_credit": 98570.00,
  "fee_basis": "ggr", "fee_rate_percent": 10,
  "carry_forward_loss": 0.00,
  "today": { "date": "2026-09-26", "bets": 1, "turnover": 21000, "payout": 17200, "ggr": 3800, "estimated_charge": 380.00 },
  "state": "ok",                 // ok · low · empty
  "bets_blocked": false
}
GET/api/v1/billing/statements?limit=60

One row per charged day, newest first: period, bets, turnover, payout, ggr, carry_in / carry_out (loss carried forward), fee_rate_percent, amount (the charge, negative) and balance_after.

GET/api/v1/billing/ledger?limit=100

Every credit movement: topup (credits you bought, with our payment reference), adjustment (a correction, with the reason) and charge.

Wallet callbacks

We POST JSON to the callback URL you gave us, with these headers:

HeaderValue
X-Provider-Signaturehex(HMAC-SHA256(secret, raw_body)). Reject the call if it doesn't match.
X-Provider-TimestampUnix seconds when the call was signed.
X-Provider-Codeperya
{
  "event": "bet.placed",
  "session_uuid": "9f1c2d3e-…",      // your session_ref
  "round_ref": "DB100245-8812",      // one per ticket
  "amount": "150.00",
  "currency": "PHP",
  "idempotency_key": "perya-bet-8812",
  "player_id": "10482",
  "game_code": "dropball",
  "round_id": 100245,
  "ticket_id": 8812,
  "timestamp": "2026-09-25T14:03:11.204Z"
}

Events

EventWhat to doDelivery
bet.placedDebit amount. Refuse with a 4xx if the player can't afford it.Live, the player is waiting (8 s timeout)
round.winCredit win_amount (stake included) and close the round.Queued, retried until 2xx
round.loseClose the round with no payout (win_amount is "0.00").Queued, retried until 2xx
round.refundGive back amount if you applied the matching bet.placed; otherwise just acknowledge.Queued, retried until 2xx

Each confirmed bet is its own round on your side (round_ref is unique per ticket), so a player adding to their bet during the same Drop Ball round sends a second bet.placed with a new round_ref.

Your responses

Retries

Queued callbacks retry after 5 s, 15 s, 30 s, 1 min, 2 min, 5 min, 10 min, 30 min, then hourly, up to 40 attempts. A 4xx other than 408 or 429 stops the retries and the callback shows as failed in our back office, where our team can resend it.

Verifying results

Each round is announced with seed_hash = SHA-256(server_seed) before betting opens. After the draw, GET /api/v1/rounds/{id} returns the server_seed, the six cards in table order and the three balls. To recompute ball n (1, 2, 3):

for i = 0, 1, 2, …
  h = HMAC-SHA256(key = server_seed, message = "{round_id}:{n}:{i}")
  v = first 4 bytes of h, unsigned big-endian
  if v < 4294967292: ball n lands on cards[v mod 6]

The last step throws away the 4 highest values so every card is exactly equally likely.

NowarCasino adapter

The callback body above uses the same fields as NowarCasino's generic provider endpoint (/v1/provider/{code}/callback). A ready-to-install adapter, PeryaGameProviderAdapter.php, and step-by-step install notes are in the provider repository under integrations/casinosite/.