Appearance
Endpoints
The hizi RGS platform provides all endpoints. The initial login() call returns the backendURL and token. The SDK sends game operations as JSON POST bodies to backendURL. Login and refresh use GET on their supplied URLs. Do not append operation names to backendURL.
login
Exchange the short-lived launch token for a session token and the URLs for all later calls. The game's launch URL provides the launch token. The response also carries the operator's platform settings (gameSettings). Your frontend must follow these settings, such as stake limits, enabled features, UI behaviour, and regulatory flags.
Request:
typescript
await login({ loginURL, launchToken });Response: IConnectReply
typescript
{
token: string; // Session token for subsequent API calls
backendURL: string; // URL for game API calls
refreshURL: string; // URL for refreshing the session
logoutURL: string; // Logout URL
webSocketURL?: string; // WebSocket URL (optional)
balance?: IBalanceReply; // Player balance
gameSettings?: IGameSettings; // Operator platform settings - read this!
tokenData?: ITokenData; // Token metadata (playerId, gameId, language, currency, mode, currencyMultiplier, gameRound, …)
freePlaysAvailable?: IFreePlayInfo[]; // Available free plays
featuresAvailable?: { game: string; type: string; rtp: number; value: number; description?: string }[];
operatorProtocol?: number; // Operator protocol version in use (informational)
}Read gameSettings before showing the game
gameSettings values differ by operator and by jurisdiction. These values must control your UI. Your frontend must clamp and validate stakes. Your frontend must also respect every enabled and disabled flag. The engine validates financial requests, but it does not implement frontend controls or disclosures. Apply all frontend requirements explicitly.
Typical fields to apply:
- Use
minStake,maxStake, andmaxPackageStaketo clamp the stake selector and buy-feature prices. - Use
customStakes,defaultStakeIndex, andforceDefaultStaketo override the game's own stake list. - Use
maxExposureto cap the maximum win that you show or allow. - Use
autoplayEnabled,autoplayLossLimitRequired, andautoplayShowTotalStaketo control the autoplay UI. - Use
gambleEnabled,turboEnabled,stopEnabled,partialCollectEnabled, andpackageBuyEnabledto enable or disable feature buttons. - Use
displayRTP,displayXRTP,showExactRTP,displayWinOdds, anddisplayJackpotOddsto control the disclosure of RTP and odds. - Use these fields to control presentation:
forceOrientation,disableFullScreenMobile,hideCompanyLogo,displayClock,abbreviateAmounts,displayNetPosition,hideDemoBalance, anddisplaySessionTimer. - Use
historyURL,homeURL,topupURL,lossLimitURL,homeEnabled, andredirectTargetfor external navigation. - If
reportAnimationEndistrue, callreportAnimationEndafter every win animation. - Use
operatorHandlesErrors,translateErrors,preventRedirect, andrefreshDisabledto handle errors and sessions.
See IGameSettings for the full field list. Operators may also supply extra fields beyond the typed ones. The index signature [key: string]: unknown exposes these extra fields.
Stake limits precedence
Operator limits replace the corresponding game defaults. A nonempty customStakes list replaces the default ladder. The current engine custom-list path skips its separate min/max checks. Filter the selected ladder against effective player and jurisdiction limits yourself. See stakes and money.
SDK login and refresh copy a fixed list of eleven top-level keys and discard the rest of the raw reply. tokenData is one of them, so playerId, operatorId, gameId, language, currency, currencyMultiplier, mode and any open gameRound all reach your frontend. Every other SDK operation returns the raw reply unchanged, so loadConfig gives back gameRoundInfo and amountToCollect straight after. Only gameVariables.rcElapsed and the singular freePlayInfo are lost for good. Do not replace login to recover them. See what login() does not return.
loadConfig
Load the game configuration including available stakes, RTP, and buy-features.
Request:
typescript
await loadConfig({ backendURL, token });Under the hood, this sends:
json
{
"op": "loadConfig",
"token": "<session-token>"
}Response: ILoadConfigReply
typescript
{
config: {
gameCode: string;
gameType?: string; // Game vertical; optional in older configurations
stakes?: number[]; // Available stake amounts (optional)
minStake?: number; // Effective minimum stake
maxStake?: number; // Effective maximum stake
calculatedMaxExposure?: number; // Effective monetary exposure limit
ladderMultipliers?: number[]; // Built-in ladder gamble configuration
ladderLives?: number;
rtp: number; // Return to player percentage
maxPayoutCap?: number; // Maximum payout cap (the engine does not set this on loadConfig)
maxPayout?: number; // Highest payout one round can accumulate, as a multiplier of stake
maxPayoutOdds?: number; // "1 in N" denominator for maxPayout (currently repeats maxWinOdds)
maxWin?: number; // Highest win one spin can pay, as a multiplier of stake
maxWinOdds?: number; // "1 in N" denominator for maxWin in ordinary play
maxWagerableWin?: number; // Maximum win amount eligible for wagering
minWagerableWin?: number; // Minimum win amount eligible for wagering
version: string; // Engine version
buyFeatures?: IBuyFeatureOption[]; // Available buy-features (with `id` added)
wagerFeatures?: string[]; // Relative wager features (e.g. ["card_red", "card_black", ...])
wagerStakeFeatures?: string[]; // Absolute cashout wager features (e.g. mines/frogger steps)
progressionCounters?: IProgressionCounterConfig[]; // Progression counter configs
progressionCounterValues?: Record<string, number>; // Current counter values (0.0–1.0)
[key: string]: unknown; // Additional fields from loadConfig.json
};
tokenData: ITokenData; // Updated token data
balance?: IBalanceReply; // Player balance
gameResult?: IGameResult; // Last game result (if resuming)
gameRoundInfo?: IGameRoundInfo; // Game round info (if resuming)
freePlaysAvailable?: IFreePlayInfo[]; // Available free plays
previousResults?: IGameResult[]; // Previous game results
amountToCollect?: number; // Uncollected amount
}Key fields:
| Field | Description |
|---|---|
config.gameType | The current game vertical, including fixed-odds types. Older configs can omit it. Validate it against your frontend. |
config.stakes | Array of available stake amounts, in minor currency units. Optional. Some games use custom stakes set by the operator. |
config.rtp | Return to player percentage (for example, 95 for 95%) |
config.maxPayout | The four certified figures a game states are maxPayout, maxPayoutOdds, maxWin and maxWinOdds. Print them on the rules screen. A figure the game does not have is absent, never 0. See ILoadConfigConfig |
config.buyFeatures | Available buy-feature options. See buy-features |
config.wagerFeatures | Available wager feature names (absent if the game has no wager features). See gamble-features |
config.progressionCounters | Progression counter configuration. See Progression Counters |
config.progressionCounterValues | Current counter values per player (0.0–1.0 range) |
gameResult | The last game result if the player has an incomplete round to resume |
placeBet
Place a bet to receive a game result. You can also continue an in-progress round with this endpoint.
Request:
typescript
// New spin
await placeBet({ backendURL, token, stake, config });
// Continue multi-step round
await placeBet({ backendURL, token, config });
// Buy feature
await placeBet({
backendURL,
token,
stake,
featureToBuy: featureId,
config,
});config is the object that loadConfig() returns (configResponse.result.config). Passing config enables automatic decompression of the scenario field. Passing config makes the SDK expand a minified scenario before it resolves. Without config, scenario stays minified. The option does nothing for a game that does not minify scenarios.
Under the hood:
json
{
"op": "placeBet",
"token": "<session-token>",
"stake": 100,
"featureToBuy": "freespin"
}Options: IPlaceBetOptions
| Field | Type | Required | Description |
|---|---|---|---|
backendURL | string | Yes | Backend URL from login response |
token | string | Yes | Session token |
stake | number | No | Stake amount. Required for the first call of a gameround. |
useTicket | boolean | No | Use a freeplay ticket |
useTicketFeatureType | string | No | Feature type of the ticket |
featureToBuy | string | No | Feature to buy (for example, 'freespin'). The engine derives the price from the stake. |
config | ILoadConfigConfig | No | Config from loadConfig(). Required to decompress the scenario field. |
playerChoiceIndex | number | No | Index of the selected option when engineData.playerChoice is set |
additionalData | Record<string, unknown> | No | Additional game-specific parameters |
Response: IPlaceBetReply
typescript
{
result: IGameResult; // The game result
tokenData: ITokenData; // Updated token data
balance?: IBalanceReply; // Updated balance
gameRoundInfo?: IGameRoundInfo; // Game round status
amountToCollect?: number; // Uncollected amount
}The key payload is result, an IGameResult:
typescript
{
// Your scenario data (from hizi engine generator)
scenario: Record<string, unknown>;
// Engine state
engineData: {
// entryIndex, scenarioInfo and spinInfo come from the fixed-odds (entries-DB)
// pipeline. Rules-engine games such as blackjack and crash omit those three.
// Every other field below, including inProgress and canCollect, applies to all games.
entryIndex?: number;
scenarioInfo?: IScenarioInfo;
spinInfo?: ISpinInfo[];
playerChoice?: TPlayerChoiceAward[];
currentFeature?: string;
nextFeature?: string;
progressionCounters?: Record<string, number>;
canCollect?: boolean;
inProgress: boolean;
buyFeatureId?: string; // Opening purchase identifier
baseStake?: number; // Base stake for the round, EXCLUDING buy-feature cost
bankedWin?: number; // Protected win multiplier, still unpaid
ladderState?: { baseWin: number; currentRungIndex: number; lives: number; previousRungIndex?: number };
version?: string;
};
// Cumulative win (multiplier of stake)
totalWin: number;
}TIP
If the round opened with featureToBuy, every reply for that round echoes back which feature it was. This includes every continuation placeBet call and the closing collect call. The SDK path is response.result.result.engineData.buyFeatureId after checking response.success. Use this field to attribute the round's total win to the bought feature. You do not need to track the feature on the client side. The field is absent (undefined) for a round that did not open by buying a feature. Older engine versions also duplicated this field at the reply root as buyFeatureId. That copy no longer exists. Read the field from engineData instead.
Decision logic after presentation and required timing:
| State | Action |
|---|---|
| Closed round with consistent terminal engine flags | Enable a new player-requested round. |
engineData.canCollect === true | Offer collect. Offer only permitted choices or explicit continuation. |
Nonempty engineData.playerChoice | Wait for a player choice. Preserve its original index. |
engineData.inProgress === true, without either condition above | Follow the vertical's continuation contract. |
| Open round without a legal action, or contradictory state | Recover before another request. |
The checked slot controller implements this order. A positive totalWin does not authorize collect. An inProgress flag does not authorize an automatic gamble.
Games without a collect phase normally settle automatically. Render their final balance and result. Do not collect a second time after settlement.
collect
Collect when the vertical offers cashout. Fixed-odds games signal this with engineData.canCollect. Crash uses its live bet hashes. Ordinary slot rounds without a collect phase settle automatically.
A partial collect during an open gamble banks a protected amount. It keeps the round open and does not immediately credit the wallet. See gamble and banking.
Request:
typescript
// Collect full amount
await collect({ backendURL, token });
// Bank part of an open gamble win, when permitted
await collect({ backendURL, token, amount });Under the hood:
json
{
"op": "collect",
"token": "<session-token>",
"collectAmount": 5000
}Options: ICollectOptions
| Field | Type | Required | Description |
|---|---|---|---|
backendURL | string | Yes | Backend URL from login response |
token | string | Yes | Session token |
amount | number | No | Amount to collect. If you omit this field, the engine collects the full available amount |
additionalData | Record<string, unknown> | No | Additional game-specific parameters (for example, the crash betHash from crashCollectData()) |
Response: ICollectReply
typescript
{
amountCredited?: number; // Actual credit, absent when only banking
amountToCollect: number; // Amount that was requested
balance?: IBalanceReply; // Updated balance
tokenData: ITokenData; // Updated token data
result?: IGameResult; // Updated state; can remain open after banking
gameRoundInfo?: IGameRoundInfo;
}collect has no config option. If its reply contains a compressed result, call decodeScenario(reply.result.result, config) after checking success.
Use the returned round status and engine flags to determine settlement. If they are missing, reload state. Do not treat amountToCollect on this reply as a guaranteed remaining balance. Its SDK contract describes the requested amount. Reload authoritative outstanding amounts before another partial collect.
Omit amount for full collection. Do not subtract a previously banked amount. A successful response alone does not prove that the round closed.
pfVerify
Replay any past round from its revealed seeds. If the round had player actions, replay those too. pfVerify reruns the exact game logic that the live round used. It returns the full result and the per-call RNG audit. You can compare this audit against what you recorded at round time.
This endpoint works only for provably-fair rounds. A provably-fair round runs with config.rng === 'pf'. The endpoint never touches the wallet, progression counters, or operator caps. It is pure compute.
Request:
typescript
import { pfVerify } from '@hizi.io/engine-sdk';
const reply = await pfVerify({
backendURL,
token, // current session token (auth + gameId routing)
serverSeed: pf.revealedServerSeed,
clientSeed: pf.clientSeed,
stake: 100, // the stake the round opened with
featureToBuy: 'freespin', // if the round opened on a buy feature
initialData: { /* … */ }, // blackjack side bets
bets: [ /* … */ ], // roulette / crash opening bets
playerNumbers: [ /* … */ ], // keno picks
actions: [
{ playerChoiceIndex: 0 }, // fixed-odds wager choice
{ pickPosition: 12 }, // mines pick
{ action: { kind: 'hit' } }, // blackjack action (TRulesAction shape)
],
});Under the hood, the SDK POSTs to backendURL:
json
{ "op": "pfVerify", "token": "<session-token>", "serverSeed": "…", "clientSeed": "…", "…": "…" }Options: IPfVerifyOptions
| Field | Type | Required | Description |
|---|---|---|---|
backendURL | string | Yes | Backend URL from the login response. |
token | string | Yes | Session token. The engine uses it only to resolve the gameId to the game config. The endpoint makes no wallet calls. |
serverSeed | string | Yes | The revealed server seed from the round's pf block. |
clientSeed | string | Yes | The client seed for that round. |
startNonce | number | No | Defaults to 0. Set it if your round opened mid-stream against an existing provably-fair session. |
stake | number | No | Required by games that need the stake to settle. Examples: blackjack settlement math, and Frogger or other fixed-odds wager games. |
featureToBuy | string | No | Mirror the round's opening featureToBuy value. Examples: the Mines mine count, or a Slot bonus buy. |
initialData | Record<string, unknown> | No | Mirror the round's opening payload. Currently this is blackjack's { sideBets }. |
bets | unknown[] | No | Roulette's IRouletteBet[] or Crash's ICrashBet[]. These are the round's opening bets. |
playerNumbers | number[] | No | Keno: the player numbers, if the round was opened with explicit picks. |
actions | IPfVerifyAction[] | No | Continuation actions, in order. The shape depends on the game. See the game's Provably Fair section under Games. |
Response: IPfVerifyReply
typescript
{
rngData: IRngDataEntry[]; // per-call audit, same shape as the live `pf.rngData`
steps: IGameResult[]; // per-step game outcomes, terminal step last (length 1 for one-shots)
}Two fields, two checks:
- Audit replay: compare
rngDataelement by element against the round's savedpf.rngData. Rows of type'int'must match exactly. Rows of type'double'carry the raw HMAC float and must match exactly. Use a deep-equal over the array. A swapped server seed produces different draws. A passing comparison proves the revealed seed is the seed that the round committed to. - Result replay: compare each
steps[i].scenarioandsteps[i].totalWinvalue to what the live round returned at the matching step. For one-shot games, this is juststeps[0]. For multi-step rounds (blackjack, Mines, Hi-Lo), this is the full per-step chain.
If both checks match, only (serverSeed, clientSeed, actions) determined the round. The engine could not have rigged the outcome.
engineData is preserved
Each step is a full IGameResult. steps[i].engineData carries the engine's per-step flow-control state: playerChoice (fixed-odds wager-feature offerings), inProgress, and nextFeature. Use this data to verify the round's flow, not just the final outcome. For example, confirm that a fixed-odds round showed the expected wager choices.
Defence-in-depth commit check
For an explicit commitment check on top of the audit replay, compute SHA-256(serverSeed) on the client. Compare the result to the serverSeedHash you saved before the round opened. This takes one crypto.subtle.digest call. The engine does not need to return this value.
Scope
pfVerify skips the operator-side post-processing that the live placeBet call runs. This includes progression counters, gameSettings.maxExposure caps, and supplemental stake debits. You cannot derive these from the provably-fair commitment. Verification covers only the RNG draws and the game logic that uses them.
Crash
Crash player-cashout timing depends on the wall clock, not on the seed. pfVerify for Crash returns only the committed scenario.maxMultiplier. This is the entire seed-derived surface. The operator audits bet settlements.
See Provably Fair for the underlying HMAC-SHA512 protocol. That page also explains how the protocol derives the seeds from (serverSeed, clientSeed, nonce).
refresh
Refresh an expired session token. Call this when API requests return SESSIONINVALID or NOTLOGGEDON errors.
Request:
typescript
await refresh(refreshURL);The initial login() response provides the refreshURL (result.refreshURL).
Response: IConnectReply
typescript
{
token: string; // New session token
backendURL: string; // Backend URL (may change)
refreshURL: string; // New refresh URL
logoutURL: string; // Logout URL
webSocketURL?: string; // WebSocket URL
balance?: IBalanceReply; // Player balance
gameSettings?: IGameSettings; // Platform settings
tokenData?: ITokenData; // Token metadata
freePlaysAvailable?: IFreePlayInfo[]; // Available free plays
}TIP
After a successful refresh, update your stored token, backendURL, and refreshURL with the new values from the response.
reportAnimationEnd
Notify the backend that the game animation has finished. Use this endpoint when the operator requires animation-end reporting (see gameSettings.reportAnimationEnd).
Request:
typescript
await reportAnimationEnd({ backendURL, token });updateBalance
Request an updated balance from the backend.
Request:
typescript
await updateBalance({ backendURL, token });