Boss HubBoss HubDocs
ChangelogStatus
Provider API/Free round campaigns

Free round campaigns

API · Operator → Boss Hub
POST/v1/freerounds

Creates a campaign that gives players free spins. You query a campaign’s state with GET /v1/freerounds/{id} and cancel its unused spins with DELETE /v1/freerounds/{id}. With /v1/freerounds/{id}/players you add, list and cancel individual players. Requires the promo module and the freerounds.write scope; this scope is not on by default, ask Boss Hub for it.

Headers

HeaderDescription
X-Operator-IdYour operator ID
X-TimestampUnix seconds. Rejected outside ±30 s
X-Signaturehex(HMAC_SHA256(secret, ts + "." + METHOD + "." + path?query + "." + body))
X-Request-IdRequired on POST and DELETE. The same ID and body return the first response, so no second campaign is created
X-Api-KeyOptional. Selects the right secret when you have several keys

Body parameters

namestringrequired
Campaign name, 2-120 characters.
gamesstring[]required
1-50 game codes. Each game must be live, support free rounds and belong to a provider you have access to; otherwise GAME_NOT_ELIGIBLE.
spinsintegerrequired
Spins per player, at least 1, at most the platform limit (200 by default).
bet_valuedecimal(18,2)required
Bet value per spin: above zero, at most two decimals. Its USD value cannot exceed the platform limit (5.00 USD by default). If the provider fixes the free round bet values (EGT), it must be one of them; otherwise BET_NOT_OFFERED, with the valid values in details.offered.
currencyISO 4217required
USD, EUR, TRY. An exchange rate must exist and every game must support it; otherwise 422 CURRENCY_NOT_SUPPORTED.
playersstring[]required
Player IDs on the operator side, 1-100,000, each at most 100 characters. A repeated ID becomes one grant. The grant of a player with no record yet is attached on their first launch.
site_idstring
Site code, e.g. site_mrd01, at most 64 characters.
template_idstring | null
Promotion template, at most 64 characters. Must be an enabled free round template; otherwise TEMPLATE_INVALID.
starts_atdate-time
Start time, in UTC with Z (2026-12-01T00:00:00Z); an offset such as +03:00 is rejected. Starts immediately if omitted.
valid_daysinteger
Validity in days: 1-365, default 7.
referencestring | null
Your own campaign ID, 1-100 characters: letters, digits, ., _, :, -. Unique per environment; retrying with the same reference never creates a second campaign.
End date: A campaign ends at the end of the UTC day (23:59:59.999Z) of starts_at + valid_days − 1 day: a start on 01.12 with 7 days means 07.12. When it ends or is cancelled, the campaign.finished webhook is sent. A campaign whose end is already past gets CAMPAIGN_IN_PAST.
Limits: The platform limits apply in every environment. Defaults: at most 5.00 USD per spin, at most 200 spins per player, at most 500 USD of free round winnings per grant (player × campaign), and a budget warning at 10,000 USD. Boss Hub can change these values. Going over a limit returns 422 VALIDATION_ERROR with details.reason. A win over the win cap pays only the remainder. If the estimated cost (bet_value × spins × players, converted to USD) exceeds the budget warning, the campaign returns as draft with approval_id; it opens once your Owner approves it in the portal.
POST /v1/freerounds HTTP/1.1
Host: staging.example.com
Content-Type: application/json
X-Operator-Id: op_meridyen
X-Timestamp: 1762344000
X-Signature: c4e88a…02df
X-Request-Id: req_01HF3R1A

{
  "name": "Aralık Hoş Geldin",
  "site_id": "site_mrd01",
  "games": ["sultans-ember"],
  "spins": 20,
  "bet_value": 0.20,
  "currency": "USD",
  "players": ["u_88213"],
  "starts_at": "2026-12-01T00:00:00Z",
  "valid_days": 7,
  "reference": "crm-dec-welcome-88213"
}
201Campaign created
{
  "id": "cmp_mrd_aralik",
  "name": "Aralık Hoş Geldin",
  "site_id": "site_mrd01",
  "template_id": null,
  "games": ["sultans-ember"],
  "spins": 20,
  "bet_value": 0.20,
  "currency": "USD",
  "players": ["u_88213"],
  "starts_at": "2026-12-01T00:00:00Z",
  "valid_days": 7,
  "reference": "crm-dec-welcome-88213",
  "status": "scheduled",
  "spins_used": 0,
  "won_total": 0.00
}
422Limit exceeded
{
  "code": "VALIDATION_ERROR",
  "message": "The submitted data is invalid.",
  "request_id": "req_01HF3R1B",
  "details": { "reason": "BET_OVER_LIMIT", "max_usd": "5.00" }
}

Campaign status

GET /v1/freerounds/{id} returns the campaign as it stands now. POST and GET responses contain the request body fields plus the following:

FieldTypeDescription
idstringCampaign ID. Used as campaign_id in callbacks.
statusstringdraft, scheduled, active, finished, cancelled.
spins_usedintegerSpins used.
won_totaldecimal(18,2)Total winnings, in the campaign currency (cancelled grants included).
approval_idstringOnly on a campaign over the budget warning: it returns as draft and the Owner’s approval is tracked with this ID.
already_processedbooleanOnly in the 200 response to a repeated request with the same reference and the same content: true.

Safe retries

If you send a reference, you can safely retry the same request after a timeout. The same reference with the same content returns the first campaign with 200 and already_processed: true; you get the same campaign even if it was cancelled in the meantime; approval_id is returned while the approval is still pending. The order of games and players, a repeated player and the way the amount is written (0.5, 0.50) do not count as different content. The same reference with different content returns 409 REFERENCE_CONFLICT and no campaign is created. References are separate for Test and Live. A request rejected by validation before the campaign is written does not use up its reference. One exception: if no player can be granted (PLAYERS_REQUIRED), the campaign stays as a draft (with your reference, if you sent one); a retry with the same reference returns that draft, and every retry without a reference leaves a new draft. Cancel the draft with DELETE. X-Request-Id records expire, a reference does not.

200Same reference: the first campaign
{
  "id": "cmp_mrd_aralik",
  "name": "Aralık Hoş Geldin",
  "site_id": "site_mrd01",
  "template_id": null,
  "games": ["sultans-ember"],
  "spins": 20,
  "bet_value": 0.20,
  "currency": "USD",
  "players": ["u_88213"],
  "starts_at": "2026-12-01T00:00:00Z",
  "valid_days": 7,
  "reference": "crm-dec-welcome-88213",
  "status": "scheduled",
  "spins_used": 0,
  "won_total": 0.00,
  "already_processed": true
}
409Same reference, different content
{
  "code": "REFERENCE_CONFLICT",
  "message": "A campaign with different content was already created with this reference.",
  "request_id": "req_01HF3R1C",
  "details": { "reference": "crm-dec-welcome-88213" }
}

Cancelling a campaign

DELETE /v1/freerounds/{id} cancels the campaign and every pending or active grant, and returns 204 with no body. If a player has an open round, the win of that round is still paid. The campaign.finished webhook is sent with status: cancelled. If the campaign started in the meantime, you get 409 CAMPAIGN_CHANGED: retry.

DELETE /v1/freerounds/cmp_mrd_aralik HTTP/1.1
Host: staging.example.com
X-Operator-Id: op_meridyen
X-Timestamp: 1762344300
X-Signature: 1fb2d7…6c9a
X-Request-Id: req_01HF3R2C

Player grants

Every player in a campaign has their own grant with its own status. Only an active grant gives spins; a free round bet on a grant in any other status is rejected by Boss Hub and never reaches your wallet.

StatusMeaning
pendingThe campaign is a draft or scheduled; it becomes active when the campaign starts
activeSpins can be used
usedThe spins are used up and the player has no open free round round
expiredThe grant’s expires_at or the campaign end has passed
cancelledThe player’s grant or the whole campaign was cancelled
failedThe grant was written but the provider rejected the player; the reason is in fail_reason
  • One grant per player per campaign. A player repeated in one request becomes one grant. Adding a player again is not an error: it returns exists with the existing grant’s status; a cancelled, expired or used grant is not renewed either. To give a player spins again, create a new campaign.
  • Players skipped at creation: a player whose currency none of the campaign’s games supports, or for whom the bet value is invalid in their currency, is not written to the campaign and is missing from the players array of the response; no reason is returned. Compare players with your request, or create the campaign and add the players with POST /v1/freerounds/{id}/players to get a result per player.

Adding players

POST /v1/freerounds/{id}/players adds players to a draft, scheduled or open campaign, up to 100 per request. The response is 200 with a result per player: added a new grant, exists the player already has a grant in this campaign (grants do not multiply, status is the existing grant’s status), failed no grant was given. failed reasons: CURRENCY_NOT_SUPPORTED none of the campaign’s games supports the player’s currency or there is no exchange rate, BET_INVALID the spin value rounds down to zero in the player’s currency, BET_NOT_OFFERED the converted value is not one of the provider’s bet values, PROVIDER_REJECTED: … the provider rejected the player. Top-level fields: added new grants written in this request, failed failures in this request, total valid grants now in the campaign (cancelled and failed excluded). A player with a record gets a grant in their own currency; a player with no record yet gets the campaign currency. The same X-Request-Id returns the first response.

playersstring[]required
Player IDs on the operator side, 1-100. A repeated ID counts once.
expires_atdate-time
End of the grants in this request, in UTC with Z. Must be in the future and no later than the campaign end; defaults to the campaign end.
POST /v1/freerounds/cmp_mrd_aralik/players HTTP/1.1
Host: staging.example.com
Content-Type: application/json
X-Operator-Id: op_meridyen
X-Timestamp: 1762344600
X-Signature: 7d41a0…b3e2
X-Request-Id: req_01HF3R3D

{
  "players": ["u_90412", "u_88213", "u_77120"],
  "expires_at": "2026-12-05T23:59:59Z"
}
200Players added
{
  "added": 1,
  "failed": 1,
  "total": 2,
  "items": [
    { "player_id": "u_90412", "result": "added", "status": "pending", "reason": null },
    { "player_id": "u_88213", "result": "exists", "status": "pending", "reason": null },
    { "player_id": "u_77120", "result": "failed", "status": null, "reason": "CURRENCY_NOT_SUPPORTED" }
  ]
}
Budget: When adding to a scheduled or open campaign pushes the estimated cost (players in the campaign + IDs in the request) over the platform budget warning, the request gets 409 BUDGET_WARNING; your Owner makes that addition in the portal. If the provider cannot add players to a scheduled or open campaign, you get 409 FREEROUND_ADD_PLAYERS_UNSUPPORTED. If the campaign was opened from draft in the meantime, you get 409 CAMPAIGN_CHANGED: retry.

Listing players

GET /v1/freerounds/{id}/players returns the campaign’s grants. For the next page, send the next_cursor value of the response as cursor; null means the list has ended.

statusstring
pending, active, used, expired, cancelled, failed.
player_idstring
A single player’s grant.
cursorstring
The next_cursor value of the previous response.
limitinteger
Page size: 1-200, default 50.
FieldTypeDescription
player_idstringPlayer ID on the operator side.
statusstringGrant status, see the table above.
currencyISO 4217Grant currency: the player’s currency, or the campaign’s if the player has no record yet.
spins_leftintegerSpins left.
won_totaldecimal(18,2)Total winnings from this grant, in currency.
expires_atdate-timeEnd of the grant: the campaign end unless the grant has its own.
cancelled_atdate-time | nullWhen it was cancelled.
fail_reasonstring | nullOnly failed: the provider’s reason.
GET /v1/freerounds/cmp_mrd_hosgeldin/players?player_id=u_88213 HTTP/1.1
Host: staging.example.com
X-Operator-Id: op_meridyen
X-Timestamp: 1762344700
X-Signature: 0c5e19…94af
200Player list
{
  "items": [
    {
      "player_id": "u_88213",
      "status": "active",
      "currency": "USD",
      "spins_left": 38,
      "won_total": 38.40,
      "expires_at": "2026-11-30T23:59:59.999Z",
      "cancelled_at": null,
      "fail_reason": null
    }
  ],
  "next_cursor": null
}

Cancelling a single player

DELETE /v1/freerounds/{id}/players/{player_id} cancels the player’s unused spins and returns 204. The win of an open round is still paid. Only a pending or active grant can be cancelled; others get 409 GRANT_CLOSED with the grant’s status in details.status. In a campaign whose grants are run by the provider, the cancellation is passed on to the provider; if the provider cannot cancel a single player you get 409 PROVIDER_CANCEL_UNSUPPORTED and can cancel the whole campaign instead.

DELETE /v1/freerounds/cmp_mrd_hosgeldin/players/u_88213 HTTP/1.1
Host: staging.example.com
X-Operator-Id: op_meridyen
X-Timestamp: 1762344800
X-Signature: 5a9d33…e170
X-Request-Id: req_01HF3R4E

Effect on the wallet

  • A free round bet arrives through bet with is_freeround: true, campaign_id set and amount: 0. Do not debit the balance. The spin value is in the freeround_bet_value field.
  • The win arrives through a normal win; credit it to the balance.
  • A spin is used up when the bet is OK and comes back when a rollback is OK. A rollback refunds no cash.
  • A bet on a cancelled or expired grant never reaches you: Boss Hub rejects it.

Error codes

CodeHTTPMeaning
INVALID_SIGNATURE401Signature could not be verified
TIMESTAMP_SKEW401X-Timestamp is outside ±30 s
IP_NOT_ALLOWED403IP is not on the allowlist
SCOPE_MISSING403The freerounds.write scope is missing
SCOPE_DENIED403The campaign or site_id is outside your key’s scope
MODULE_DISABLED403The promo module is disabled
NOT_FOUND404No such campaign, or it is in the other environment; the player has no grant in this campaign (cancelling a player); site_id is not yours (POST)
REQUEST_ID_CONFLICT409Same X-Request-Id, different body
REQUEST_IN_PROGRESS409The same X-Request-Id is still being processed
REFERENCE_CONFLICT409Same reference, different content
CAMPAIGN_CLOSED409Cancelling the campaign, adding players and cancelling a player: the campaign has finished or was already cancelled
CAMPAIGN_CHANGED409The campaign changed status in the meantime (started or opened); retry
GRANT_CLOSED409Cancelling a player: the grant is not pending or active
BUDGET_WARNING409Adding players: the estimated cost exceeds the budget warning
FREEROUND_ADD_PLAYERS_UNSUPPORTED409Adding players: the provider does not add players to an open campaign
PROVIDER_CANCEL_UNSUPPORTED409Cancelling a player: the provider does not support it
VALIDATION_ERROR422Missing or invalid field (details.fields), or a rule violation (details.reason: SPINS_OVER_LIMIT, BET_INVALID, BET_OVER_LIMIT, BET_NOT_OFFERED, GAME_NOT_ELIGIBLE, TEMPLATE_INVALID, CAMPAIGN_IN_PAST, PLAYERS_REQUIRED, EXPIRES_INVALID)
INVALID_CURSOR422Player list: cursor did not come from a previous response
CURRENCY_NOT_SUPPORTED422No exchange rate for the currency, or a game does not support it (details.game)
RATE_LIMITED429Rate limit exceeded
← PreviousTransaction feedNext →Jackpot pools
Free round campaigns · Documentation · Boss Hub