Appearance
Freeplays
Freeplays (free spins or tickets) are bets that an operator grants to a player. A player can spend a freeplay without a wallet debit. Each freeplay has a fixed stake, and optionally a fixed feature, set when the operator grants the package.
How It Works
Operators grant and revoke freeplay packages out-of-band, through the Operator API. The engine sends the currently available packages to your frontend as part of the normal session calls. There is no separate "claim" endpoint. Read freePlaysAvailable and spend a ticket with placeBet.
Reading Available Freeplays
freePlaysAvailable (an array of IFreePlayInfo) is included in:
login()/refresh():IConnectReply.freePlaysAvailableloadConfig():ILoadConfigReply.freePlaysAvailable
typescript
const response = await login({ loginURL, launchToken });
if (response.success) {
const freePlays = response.result.freePlaysAvailable ?? [];
}Not echoed by placeBet
IPlaceBetReply does not carry freePlaysAvailable. See Keeping the List in Sync to update your local copy after you spend a ticket.
Each entry groups tickets that share a stake, a currency, and (optionally) a feature. A player can hold several entries at the same time:
typescript
interface IFreePlayInfo {
currency: string; // ISO 4217 currency code
stake: number; // Fixed stake amount for each free play
count: number; // Number of free plays available
feature?: string; // Optional feature to enter directly (e.g. "freespin")
}The RGS wire entry also carries a package serial, unique per operator. The SDK type omits it. Read it from the raw reply when your ticket picker must identify one package of several.
Helpers
Two helper functions summarize the array. You do not need to reduce the array yourself:
typescript
import { getFreePlaysRemaining, getFreePlayStake } from '@hizi.io/engine-sdk';
const totalRemaining = getFreePlaysRemaining(freePlays); // sum of `count` across all entries
const firstStake = getFreePlayStake(freePlays); // first entry's `stake`, or undefined if noneTIP
getFreePlayStake only reads the first entry. If a player can hold packages at more than one stake or feature, do not rely on this helper alone. Iterate freePlaysAvailable yourself and build a picker.
Choosing a Freeplay
Sometimes more than one package is available, with different stakes, or a stake tied to a specific feature such as "freespin". Present the options so the player can pick which one to spend:
typescript
const options = freePlays.map((fp, i) => ({
value: i,
label: fp.feature ? `${fp.stake} · ${fp.feature} (${fp.count} left)` : `${fp.stake} (${fp.count} left)`,
}));Spending a Freeplay
Call placeBet with useTicket: true, the ticket's own stake, and (if set) its feature as useTicketFeatureType:
typescript
const freePlay = freePlays[selectedIndex]; // e.g. { currency: 'EUR', stake: 100, count: 5, feature: 'freespin' }
const response = await placeBet({
backendURL,
token: sessionToken,
stake: freePlay.stake,
useTicket: true,
useTicketFeatureType: freePlay.feature,
config,
});
if (response.success) {
const gameResult = response.result.result;
// Handle exactly like any other placeBet result - multi-step rounds,
// player choices, and wager/collect flows all still apply.
}Still a normal round
A freeplay spin returns a normal IGameResult. It runs through the full game flow (multi-step rounds, player choices, wager/collect). The only difference is that the engine does not debit the stake from the player's balance.
Keeping the List in Sync
The reply type does not guarantee an updated ticket list. Reload with loadConfig before the next ticket. Refresh also supplies the list when session renewal is required.
You can reduce a local count after a confirmed opening response for display. Never reduce it on a timeout or again for a continuation. Reconcile uncertain responses before another ticket request. The operator can grant or revoke tickets while the game is open.
Ticket controls and summary
Lock the ticket's stake, currency, and feature. Validate its availability and effective limits before the opening request. Never silently replace an invalid ticket with a paid bet.
Apply the same timing, input, rule, and recovery controls as paid play. A granted feature ticket uses useTicketFeatureType. It does not require a paid featureToBuy purchase. Confirm the applicable jurisdiction policy for promotional tickets separately.
Show the number of remaining tickets and the campaign win summary. Accumulate confirmed round winnings once per round identity. Do not add each cumulative feature result again.
Finish all awarded feature steps before displaying the completed campaign. Reconcile the final ticket list and balance. Require a clear player action before switching to paid play. Send the appropriate Operator Interface freeplay events.