Grant Free Bets
Grant promotional free bets to a player
Grant a promotional free-bet reward to a player. The full grant → bet → win → rollback flow, and exactly what your wallet must return at each step, lives on Freebets & Rewards — this page is the endpoint reference.
Authentication
This endpoint is authenticated by IP whitelist — your server's outbound IP must match the address registered during onboarding. The X-Marbles-Signature header is not required and is ignored if sent.
Endpoint
POST https://api.marbles.xyz/game/free-bets/rewardsStaging: https://api.stagingmarbles.io/game/free-bets/rewards.
Request
{
"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 free bet as an integer at the currency's wire precision (USD: 500000 = $5.00) — the same scale as the amount on the /bet callback. A free bet only covers a bet whose wire amount is ≤ this integer, so an under-scaled grant (e.g. cents) makes every free bet unusable. See Currencies |
currency | string | Yes | ISO 4217 currency code — must match the currency the player bets in |
quantity | integer | No | Number of free bets to grant (default: 1, max: 100) |
expiresAt | string | Yes* | ISO 8601 expiration datetime. Must be in the future. |
startTime | string | No | ISO 8601 datetime when the free bets become available (for scheduled campaigns) |
gameIds | array | No | Restrict the free bets to specific games (omit for all games) |
prepaidId | string | No | Grant from a prepaid template instead of specifying the values inline. When set, currency, expiresAt and the applicable games always come from the template and cannot be overridden per grant; amount and quantity also come from the template when it defines them — your request values apply only where the template leaves them unset |
* expiresAt is required unless you grant from a prepaid template (prepaidId), in which case the template's expiry is used.
expiresAt must be in the future
An expiresAt in the past is rejected with 400 Bad Request — expiresAt must be in the future. Send an explicit UTC offset so there is no ambiguity; a common cause of a rejected grant is re-sending a saved request whose expiresAt has since passed. The response echoes this value back as endTime on the reward object, and as expiresAt on each entry in freeBets[].
Player must exist
The player must have launched at least one game before you can grant them free bets — the endpoint does not create players. Granting to an unknown player returns the 404 below.
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": {},
"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"]
}
]
}The reward status is "granted" when the free bets are immediately available, or "scheduled" if startTime is in the future (the startTime field only appears on scheduled grants).
Error Responses
All errors use the {"error", "details"} shape (the 409 additionally carries a numeric code):
| Error | HTTP Code | Description |
|---|---|---|
Validation error | 400 | Missing or invalid request fields — details names the field |
Bad request | 400 | Malformed JSON body |
Invalid operator id | 400 | Unknown operatorId |
Invalid Integration IP | 400 | The source IP could not be resolved to your integration |
Player not found | 404 | details: player with ID <id> not found — the player has never launched a game |
REWARD_ALREADY_EXISTS | 409 | A reward with this clientRewardId already exists for this player (code: 9003) |
Internal server error | 500 | Unexpected failure. One specific case to know: details reading free bets are not enabled for operator <operatorId> means the feature is toggled off for your account — free bets are on by default for new operators, so if you see that string, contact us to enable it |
Prepaid Templates
If we have configured prepaid free-bet templates for you, list them with:
GET https://api.marbles.xyz/game/free-bets/prepaids?operatorId=your-operator-id| Query parameter | Required | Description |
|---|---|---|
operatorId | Yes | Your operator ID — omitting it returns 400 operatorId query parameter is required |
gameId | No | Only return templates applicable to this game |
The response is a JSON array of currently-active templates:
[
{
"id": "3f2b1c44-...",
"name": "Welcome pack",
"description": "5 x $1 free bets",
"amount": 100000,
"currency": "USD",
"quantity": 5,
"gameIds": [],
"expiresAt": "2026-12-31T23:59:59Z"
}
]The returned id is the value you pass as prepaidId when granting. Templates are configured by 155 — contact us to set one up.