Freebets & Rewards
Create promotional freebets and rewards for players
Freebets allow operators to create promotional rewards that give players free bets on 155.io games. When a player uses a freebet, no real money is deducted from their balance, but any winnings are credited as real money.
Free bets reuse the standard wallet callbacks — /bet, /win, /rollback — with two flags (isFree: true and a rewardUuid). The Contract rules section below is the authoritative, code-verified statement of the full grant → bet → win → rollback sequence and exactly what your wallet must return at each step. If you only read one section, read that one.
Enablement
Free bets are enabled by default for all new operators — there is nothing to request, and you can start granting them immediately. (Legacy operators predating this policy may still need the feature toggled; if your grant calls return FREEBETS_NOT_ENABLED, contact us to switch it on.)
Player Must Exist
The player must have launched at least one game before you can grant them freebets. The grant endpoint does not create players — if the player hasn't opened a game yet, the API will return a 404 PLAYER_NOT_FOUND error.
Contract rules: the flow & what to return
A free bet runs through the same four callbacks as a real-money bet — grant → /bet → /win → /rollback — but with amount: 0 on the bet leg and an isFree: true flag carried throughout. This section states, for each step, exactly what your wallet must do and return. These rules are verified against the 155 bet registry (direct-integration/bet_registry.go).
| Step | What 155 sends | What your wallet must do | What you return |
|---|---|---|---|
| 1. Grant | You call POST /game/free-bets/rewards | Record the reward against the player; you'll see your clientRewardId echoed back later as rewardUuid | 201 Created with the reward + free-bet payload |
2. /bet | isFree: true, amount: 0, rewardUuid | Do not move the balance. Recognise the rewardUuid | SUCCESS if the rewardUuid is known; BONUS_ERROR if it is not |
3. /win | isFree: true, referenceTransactionId = the bet's transactionId | On a win, credit the full amount (real money). On a loss, the amount is 0 — credit nothing | SUCCESS |
4. /rollback | isFree: true (fires only before /win) | Reverse the bet leg. There is nothing to refund (the bet debited 0) | SUCCESS (or DUPLICATE_TRANSACTION_ERROR for a replay) |
Unknown rewardUuid → return BONUS_ERROR (never UNKNOWN_ERROR)
When you receive a free /bet whose rewardUuid you do not recognise, return BONUS_ERROR — not UNKNOWN_ERROR. 155 maps BONUS_ERROR to a bonus failure and cleanly rejects the bet; any other status (including UNKNOWN_ERROR) is treated as a wallet fault and is not what 155 expects here. This is the single most common free-bet integration mistake. See the full enum in Error codes.
Zero debit — a free /bet must not touch the balance
A free /bet arrives with isFree: true and amount: 0. It must not debit (or credit) the player. Returning INSUFFICIENT_BALANCE_ERROR on a free bet is wrong — there is nothing to debit. Just return SUCCESS (or BONUS_ERROR if the rewardUuid is unknown).
Free-bet winnings are REAL money
A free bet is free to place, but what it pays out is real. On a winning /win, credit the full amount to the player's real balance. On a losing /win the amount is 0 — credit nothing. Never invent a phantom credit for a losing free bet, and never withhold a real win because the bet was free.
Rollback of a settled / duplicate free bet → return SUCCESS
A /rollback for a free bet — including one you have already settled or already rolled back — must return SUCCESS (balance unchanged). 155 accepts a rollback response of SUCCESS or DUPLICATE_TRANSACTION_ERROR; any other status is treated as a failure. So a settled/duplicate free-bet rollback is forgiving: silently dedupe and answer SUCCESS. See /rollback for the shared idempotency rule across all bets.
Cross-references: the per-callback request/response shapes live on /bet, /win, and /rollback; the canonical status enum lives in Error codes.
Integration Options
Direct integration operators can grant freebets to players via the 155.io API and receive freebet transactions through their operator API. Free bets are enabled by default — see Enablement above. For the authoritative grant → bet → win → rollback behaviour, see Contract rules.
Granting Freebets
Use the 155.io API to grant freebets to players:
POST /game/free-bets/rewardsRequest
{
"clientPlayerId": "player-123",
"operatorId": "your-operator-id",
"clientRewardId": "promo-winter-2025-001",
"amount": 500000,
"currency": "USD",
"quantity": 3,
"expiresAt": "2025-12-31T23:59:59Z",
"gameIds": ["game-uuid-1", "game-uuid-2"]
}| Field | Type | Required | Description |
|---|---|---|---|
clientPlayerId | string | Yes | The player's unique identifier in your system |
operatorId | string | Yes | Your operator ID |
clientRewardId | string | Yes | Your unique identifier for tracking this reward |
amount | integer | Yes | Value per freebet with 5-digit precision (e.g., 500000 = $5.00) |
currency | string | Yes | ISO 4217 currency code |
quantity | integer | No | Number of freebets to grant (default: 1, max: 100) |
expiresAt | string | No | ISO 8601 expiration datetime |
startTime | string | No | ISO 8601 datetime when freebets become available (for scheduled campaigns) |
gameIds | array | No | Restrict freebets to specific games (omit for all games) |
Success Response
HTTP/1.1 201 Created
{
"reward": {
"type": "Reward",
"clientRewardId": "promo-winter-2025-001",
"clientPlayerId": "player-123",
"operatorId": "your-operator-id",
"amount": 500000,
"currency": "USD",
"quantity": 3,
"applicableGames": ["game-uuid-1", "game-uuid-2"],
"meta": {},
"startTime": null,
"endTime": "2025-12-31T23:59:59Z",
"status": "granted",
"createdAt": "2025-06-15T14:30:00Z"
},
"freeBets": [
{
"id": "fb-001",
"amount": 500000,
"currency": "USD",
"status": "claimable",
"createdAt": "2025-06-15T14:30:00Z",
"expiresAt": "2025-12-31T23:59:59Z",
"applicableGames": ["game-uuid-1", "game-uuid-2"]
},
{
"id": "fb-002",
"amount": 500000,
"currency": "USD",
"status": "claimable",
"createdAt": "2025-06-15T14:30:00Z",
"expiresAt": "2025-12-31T23:59:59Z",
"applicableGames": ["game-uuid-1", "game-uuid-2"]
},
{
"id": "fb-003",
"amount": 500000,
"currency": "USD",
"status": "claimable",
"createdAt": "2025-06-15T14:30:00Z",
"expiresAt": "2025-12-31T23:59:59Z",
"applicableGames": ["game-uuid-1", "game-uuid-2"]
}
]
}Reward Status
The reward status will be "granted" when freebets are immediately available, or "scheduled" if startTime is in the future.
Error Responses
| HTTP Code | Error Code | Description |
|---|---|---|
| 400 | VALIDATION_ERROR | Invalid request (missing fields, invalid format) |
| 400 | FREEBETS_NOT_ENABLED | Freebets not enabled for your operator (only legacy accounts predating the default-on policy — contact us to toggle it on) |
| 404 | PLAYER_NOT_FOUND | Player does not exist |
| 409 | REWARD_ALREADY_EXISTS | A reward with this clientRewardId already exists |
| 500 | UNKNOWN_ERROR | Internal server error |
Client Reward ID
The clientRewardId is returned in bet transactions as rewardUuid, allowing you to track which promotion a freebet belongs to.
Important: Each clientRewardId is tied to one player. If you want to grant freebets to 100 players in a campaign, you make 100 API calls. You can use the same clientRewardId pattern (e.g., winter-promo-{playerId}) or unique IDs per grant.
Each free bet settles independently — not aggregated
A grant is a pool of independent free-bet credits, not a slots-style free-spins session. Each free bet is its own /bet (isFree: true, amount: 0, with rewardUuid) followed by its own per-round /win (paid as real money, or amount: 0 for a loss). So N free bets = N /bet + N /win callbacks — we do not send a single batched win, and the number of wins won't necessarily equal the granted quantity (unused free bets are cancelled/expired, not settled). Group a grant's settlements by the shared rewardUuid.
Cancelling Freebets
Cancel unused freebets for a specific reward:
POST /game/free-bets/rewards/cancel{
"clientRewardId": "promo-winter-2025-001",
"operatorId": "your-operator-id",
"reason": "Player requested cancellation"
}Cancel Response
{
"reward": {
"type": "Reward",
"clientRewardId": "promo-winter-2025-001",
"clientPlayerId": "player-123",
"operatorId": "your-operator-id",
"amount": 500000,
"currency": "USD",
"quantity": 2,
"applicableGames": [],
"meta": {},
"status": "cancelled",
"createdAt": "2025-06-15T14:30:00Z",
"cancelledAt": "2025-07-01T10:00:00Z"
},
"freeBets": [
{
"id": "fb-001",
"amount": 500000,
"currency": "USD",
"status": "cancelled",
"createdAt": "2025-06-15T14:30:00Z",
"expiresAt": "2025-12-31T23:59:59Z",
"applicableGames": []
},
{
"id": "fb-002",
"amount": 500000,
"currency": "USD",
"status": "cancelled",
"createdAt": "2025-06-15T14:30:00Z",
"expiresAt": "2025-12-31T23:59:59Z",
"applicableGames": []
}
]
}Cancel Rules
- Only freebets with status
claimableorclaimedcan be cancelled - If any freebet from the reward has been
used, the entire reward cannot be cancelled - Cancellation is per-reward (per-player), not campaign-wide
- Scheduled rewards (where
startTimeis in the future) can be cancelled before the freebets are generated. The reward is transitioned tocancelledand aREWARD_NOT_STARTEDresponse is returned with nofreeBetspayload.
Cancel Error Responses
| HTTP Code | Error Code | Code | Description |
|---|---|---|---|
| 404 | REWARD_NOT_FOUND | 9001 | No reward exists for the provided clientRewardId |
| 409 | REWARD_CANNOT_BE_CANCELLED | 9006 | At least one freebet has already been used — the reward cannot be cancelled |
| 409 | REWARD_NOT_STARTED | 9007 | Reward was scheduled for a future startTime; freebets hadn't been generated yet. The reward has been transitioned to cancelled — treat this as a successful cancellation |
| 500 | UNKNOWN_ERROR | — | Internal server error |
Freebet Statuses
| Status | Description |
|---|---|
claimable | Available for the player to use |
claimed | Player has selected but not yet placed a bet |
used | Freebet was used to place a bet |
expired | Freebet expired before being used |
cancelled | Freebet was cancelled by operator |
Scheduling Freebets
Use startTime to schedule freebets for a future date (e.g., for upcoming promotions):
{
"clientPlayerId": "player-123",
"operatorId": "your-operator-id",
"clientRewardId": "new-years-2026-player123",
"amount": 1000000,
"currency": "USD",
"quantity": 5,
"startTime": "2026-01-01T00:00:00Z",
"expiresAt": "2026-01-31T23:59:59Z"
}- Freebets are created immediately but only become
claimableafterstartTime - Players won't see the freebets until
startTime - You can cancel scheduled freebets before they become active
Receiving Freebet Transactions
When a player uses a freebet, you'll receive modified bet/win/rollback requests:
Bet Request (Freebet)
{
"requestId": "8df0475e-5069-483a-8205-f6089997abc9",
"transactionId": "ea0240f5-483d-434b-a8d4-04dabf61cde3",
"clientPlayerId": "player-123",
"roundId": "17cc81fd-df13-4ca4-857d-de0f766dc372",
"gameId": "f1c0b104-f29d-44a9-ae93-e8afcbe3feb9",
"amount": 0,
"currency": "USD",
"isFree": true,
"rewardUuid": "promo-winter-2025-001",
"meta": { ... }
}Key differences for freebets:
amountis0(no real money deducted)isFreeistruerewardUuidcontains yourclientRewardIdfor tracking
Win/Loss Request (Freebet)
{
"requestId": "...",
"transactionId": "...",
"referenceTransactionId": "ea0240f5-483d-434b-a8d4-04dabf61cde3",
"amount": 1500000,
"currency": "USD",
"isFree": true,
"roundClosed": true
}- For wins:
amountcontains the winnings to credit (this is real money) - For losses:
amountis0(nothing to credit or deduct) isFreeistrueto indicate this was a freebet result
Rollback Request (Freebet)
{
"requestId": "...",
"transactionId": "...",
"referenceTransactionId": "ea0240f5-483d-434b-a8d4-04dabf61cde3",
"isFree": true,
"roundClosed": true
}isFreeistrueto indicate the original bet was a freebet- No amount to refund (freebet had
amount: 0) - Fires only before the freebet's
/win(win or loss) callback. To reverse a granted-but-unused reward, use Cancelling Freebets, not/rollback.
Example Flow
- You grant a $5 freebet to player-123 via the API
- Player launches a 155.io game and sees the freebet in their inventory
- Player taps the freebet and selects "Use now" to activate it
- Player places a bet (freebet is applied)
- You receive a bet request with
amount: 0,isFree: true - Round completes - player wins $15
- You receive a win request with
amount: 1500000,isFree: true - You credit $15 to the player's real balance
Hub88 operators use Hub88's native freebet API and receive transactions through Hub88's standard integration.
Step 1: Request Prepaid Templates
Contact us to set up your freebet templates. We'll need:
| Information | Description |
|---|---|
| Operator ID | Your Hub88 operator_id |
| Bet value | Amount per free bet (e.g., $1, $5, $10) |
| Quantity | Number of free bets in the reward |
| Currency | Which currencies to support (e.g., USD, EUR) |
| Games | All 155.io games or specific games only |
For example, a template with bet value: $2 and quantity: 5 gives the player 5 free bets worth $2 each.
Once configured, you'll receive prepaid_uuid(s) that you can use to create rewards.
Step 2: Create Rewards via Hub88
Use Hub88's Operator API to grant freebets to your players:
POST /operator/generic/v2/freebet/rewards/create{
"operator_id": "your-operator-id",
"user": "player-123",
"prepaid_uuid": "uuid-from-step-1",
"start_time": "2025-01-01T00:00:00Z",
"end_time": "2025-12-31T23:59:59Z"
}See Hub88's Freebets API Documentation for full details.
Set Expiration Dates
Always set start_time and end_time when creating rewards. Otherwise, rewards will automatically expire after a few weeks.
How It Works
- You create a reward for a player via the appropriate API
- Player launches a 155.io game and sees the freebet in their inventory
- Player taps the freebet and selects "Use now" to activate it
- Player places a bet (the freebet is automatically applied, no real balance deducted)
- Winnings are credited as real money to the player's balance
Manual Activation Required
Freebets are not automatically used. Players must manually select a freebet from their inventory before placing a bet. This gives players control over when to use their promotional rewards. Only one freebet can be active at a time.
Need Help?
Free bets are enabled by default for new operators (see Enablement). Contact us to discuss promotional strategies, or if a legacy operator account still needs the feature toggled on.