# OpenAPI description of the hizi.io Operator API.
#
# This file is generated by hand from the prose documentation under
# /operator-api (docs.hizi.io/operator-api). The prose docs remain the
# normative source: where a shape below is marked as "inferred from the sample
# call", the documentation gave an example but no attribute table.
openapi: 3.0.3

info:
  title: hizi.io Operator API
  version: 1.8.9
  description: >-
    Server-to-server API between the hizi.io RGS (remote game server) and a
    casino operator (or aggregator) backend — game launch, player management,
    wallet transactions, free plays, discounts, backoffice reporting and health
    checks.


    ## Two directions in one document


    The protocol is bidirectional, and this document describes both halves.
    Use the `tags` to tell them apart:


    * **Operator → hizi.io** (tags `Operator → hizi.io`, `Free Plays`,
      `Backoffice`, `Health & Certified Hash`) — endpoints **hizi.io
      implements** and the operator calls. They live under the RGS base URL
      (`[api-endpoint-url]`, supplied by hizi.io support).
    * **hizi.io → Operator** (tag `hizi.io → Operator`) — endpoints the
      **operator implements** and hizi.io calls (the wallet/callback API). Each
      of those operations carries its own `servers` entry pointing at the
      operator's base URL, which is configured in the hizi.io backoffice.


    Only `getGameURL` is mandatory for the operator to *call*; on the callback
    side `getBalance` and `doTransactions` are mandatory to *implement*, while
    `logEvent`, `doGameRoundReport`, `doGameRoundResult` and
    `reportAnimationEnd` are optional and only sent when enabled for the
    operator account.


    ## Transport rules


    * HTTPS only — plain HTTP is **not** supported.
    * `GET` calls pass their parameters as URL-encoded query parameters, `POST`
      calls send a JSON body with `Content-Type: application/json`.
    * Character set is always UTF-8; responses are `application/json`.
    * IP whitelisting may be in place in both directions.
    * CORS policies may apply.


    ## Amounts are always 1/100 of the base currency


    Every monetary value — bets, wins, balances, stakes, discounts — is an
    **integer expressed as 1/100 of the base currency** (a fixed ×100 factor),
    never a decimal: €10.00 is `1000`, €1.50 is `150`. This is a fixed 1/100
    convention, **not** the currency's ISO minor unit (JPY and CLP have none, a
    few currencies use 1/1000) — hizi always expects 1/100 of the base
    currency. See the `Amount` schema.


    ## Authentication


    Every call in both directions is authenticated with three request headers —
    `X-H-AUTH-ID`, `X-H-AUTH-SIG` and `X-H-TIMESTAMP` — modelled here as three
    `apiKey` security schemes that must all be present. The signature is a
    base64-encoded SHA-256 HMAC over a `#`-joined list of request fields; the
    exact field list differs per operation and is given in each operation's
    description. See the `hiziAuthSignature` security scheme for the algorithm.


    ## Errors


    A call is successful only if it returns HTTP 200 **and** carries no
    `X-H-ERROR-ID` response header. A 200 with `X-H-ERROR-ID` present must be
    treated exactly like a 400. On a non-200 response the body may additionally
    carry an `errorCode` object. The full error-code catalogue is in the
    `ErrorCode.id` description and, machine-readable, in the top-level
    `x-error-codes` extension.


    ## Not covered by this document


    * **Appendix A — Events.** The browser-side `postMessage` protocol between
      the game iframe and the operator lobby (`spin`, `turbo`, `roundStarted`,
      `balance`, …) is not an HTTP API and therefore cannot be expressed in
      OpenAPI. It is documented separately at
      https://docs.hizi.io/operator-api/appendix-a-events.
    * **Appendix C — Game settings** are not endpoints; they are modelled as the
      `GameSettings` and `GameVariables` schemas, passed to `getGameURL`.
    * **Appendix E — Discounts** has no endpoint of its own; it is the
      `DiscountInfo` schema returned alongside a balance by `getBalance` and
      `doTransactions`.
    * **Endpoints documented as "work in progress"** have no published request or
      response shape and are deliberately omitted rather than guessed at:
      `getGameHealth`, `getGameRoundState`, `forceCloseGameRound`,
      `backoffice/getGameCriticalFiles`, `registerFreeroundTemplate`,
      `getFreeroundTemplate`, `removeFreeroundTemplate`. Contact hizi.io
      support for details.
  contact:
    name: hizi.io support
    url: https://docs.hizi.io/operator-api/
externalDocs:
  description: Operator API documentation (normative source for this spec)
  url: https://docs.hizi.io/operator-api/

# Machine-readable version of the error-code catalogue in
# https://docs.hizi.io/response-codes. These values appear in the
# X-H-ERROR-ID response header and in the `id` field of an errorCode object.
x-error-codes:
  - name: SUCCESS
    id: 0
    description: Call was successful.
  - name: PLAYERNOTFOUND
    id: 1
    description: Player not found.
  - name: PLAYERBLOCKED
    id: 2
    description: Player is blocked.
  - name: BALANCETOOLOW
    id: 10
    description: Balance not sufficient to make a debit.
  - name: BETLIMITREACHED
    id: 11
    description: Bet limit reached, the bet cannot be accepted.
  - name: GAMEROUNDALREADYSTARTED
    id: 12
    description: Gameround is already started (a second initial bet with a
      different transaction hash was received).
  - name: GAMEROUNDNOTACTIVE
    id: 13
    description: Gameround hash is known but the round has already been closed.
  - name: GAMEROUNDNOTFOUND
    id: 14
    description: Gameround hash not found (unless an initial debit with
      isFirstDebit true is received).
  - name: GAMEROUNDNOTSTARTED
    id: 15
    description: A follow-up debit or a credit was received for a known gameround
      whose initial debit never arrived.
  - name: TRANSACTIONCOUNTWRONG
    id: 16
    description: transactionCount does not match the number of transactions passed
      in a doTransactions call.
  - name: GAMEROUNDPLAYERMISMATCH
    id: 17
    description: The gameround hash does not belong to the passed playerId.
  - name: CURRENCYINVALID
    id: 18
    description: The currency passed is not valid or not available for that player
      or game.
  - name: INVALIDPARAMETER
    id: 19
    description: An invalid value for a parameter was received.
  - name: TRANSACTIONAFTEREND
    id: 20
    description: A debit or credit was received after an end transaction inside
      the same gameTransaction list.
  - name: GAMEROUNDISLOCKED
    id: 21
    description: The gameround is locked or a credit call is being retried. After
      24 hours the round is marked as stranded and needs manual
      reconciliation.
  - name: GAMEROUNDISCLOSING
    id: 22
    description: The gameround is being closed but the operator has not accepted
      the end call yet. The player can start new games.
  - name: SPINNOTAVAILABLE
    id: 23
    description: Requested spin type (free or bonus) is not available — handle
      like a low balance error.
  - name: DEBITSHOULDBECANCELLED
    id: 24
    description: A debit failed and should be cancelled (internal EveryMatrix usage).
  - name: COOLOFFPERIODACTIVE
    id: 25
    description: Player is in a cool-off period.
  - name: TRANSACTIONSARERETRYING
    id: 26
    description: New transactions were received while previous ones are still
      pending (internal error).
  - name: ENDTRANSACTIONALREADYRECEIVED
    id: 27
    description: New transactions were received but the end transaction was
      already processed and is being retried (internal error).
  - name: REGIONISBLOCKED
    id: 28
    description: Request was blocked for legal reasons.
  - name: NOTLOGGEDON
    id: 29
    description: Player is not logged in at the casino.
  - name: RESPONSIBLEGAMINGLIMITREACHED
    id: 30
    description: Bet limit reached.
  - name: RESPONSIBLESGAMINGSESSIONEXPIRED
    id: 31
    description: Session limit reached or session expired.
  - name: PLAYERNOAUTHENTICATED
    id: 32
    description: Player is not known at the casino.
  - name: DISCOUNTNOTELIGIBLE
    id: 33
    description: The discount submitted in doTransactions is not eligible.
  - name: GENERICBETERROR
    id: 34
    description: Unexpected error on a debit transaction.
  - name: GENERICWINERROR
    id: 35
    description: Unexpected error on a credit transaction.
  - name: OPERATIONBEINGPROCESSED
    id: 36
    description: Another transaction is being processed.
  - name: TRANSACTIONALREADYEXISTS
    id: 37
    description: Transaction was already processed.
  - name: REALITYCHECK
    id: 38
    description: Reality check triggered by the operator.
  - name: SIGNATUREWRONG
    id: 1001
    description: Signature is incorrect.
  - name: PAYLOADNOTJSON
    id: 1002
    description: Payload received is not valid JSON.
  - name: WRONGHEADERS
    id: 1003
    description: Header fields missing or wrong.
  - name: PARAMETERMISSING
    id: 1004
    description: A mandatory parameter was missing in the request.
  - name: INVALIDGAMEMODE
    id: 1005
    description: An invalid game mode was passed.
  - name: GAMECODENOTFOUND
    id: 1006
    description: Game code not found or not available.
  - name: GAMECONFIGNOTSUPPORTED
    id: 1007
    description: The requested game config is not supported.
  - name: SESSIONINVALID
    id: 1008
    description: The session is expired or invalid — the game should be reloaded
      using a new getGameURL launch URL.
  - name: RNGFAILURE
    id: 1009
    description: The RNG failed.
  - name: NETWORKERROR
    id: 1010
    description: A general network error occurred.
  - name: DATASTRUCTUREWRONG
    id: 1200
    description: Payload sent is wrong.
  - name: RATELIMITEXCEEDED
    id: 1300
    description: A rate limit was exceeded (usually reported with HTTP 429).
  - name: UNEXPECTED
    id: -1
    description: An unexpected error that could not be specified in detail.

servers:
  - url: https://{apiEndpointUrl}
    description: >-
      hizi.io RGS endpoint, used for every Operator → hizi.io call (including
      the backoffice, free-play and health endpoints). The concrete host is
      supplied by hizi.io support and differs per environment.
    variables:
      apiEndpointUrl:
        default: api-endpoint-url
        description: >-
          The `[api-endpoint-url]` placeholder used throughout the prose docs.
          Contact hizi.io support for the value that applies to your account.

# All three signing headers are required on every call, in both directions.
security:
  - hiziAuthId: []
    hiziAuthSignature: []
    hiziTimestamp: []

tags:
  - name: Operator → hizi.io
    description: >-
      Calls the operator makes into the hizi.io RGS to launch games and to
      query or correct player and gameround state. Only `getGameURL` has to be
      implemented; the rest are optional but give the operator more control.
    externalDocs:
      url: https://docs.hizi.io/operator-api/operator-to-hizi
  - name: hizi.io → Operator
    description: >-
      Callbacks the hizi.io RGS makes into the operator backend — the wallet
      API. The operator implements these as a server; the base URL is
      configured in the hizi.io backoffice. `getBalance` and `doTransactions`
      are mandatory, the rest are optional and have to be enabled per operator
      account.
    externalDocs:
      url: https://docs.hizi.io/operator-api/hizi-to-operator
  - name: Discounts
    description: >-
      Operator-funded live discounts. There is no dedicated endpoint: the
      operator returns a `DiscountInfo` object alongside any balance
      (`getBalance`, `doTransactions`), and hizi.io then sends
      `discountAmount` / `discountCampaignId` on subsequent debit
      transactions. A debit whose discount is no longer eligible must be
      rejected with error id 33 (DISCOUNTNOTELIGIBLE).
    externalDocs:
      url: https://docs.hizi.io/operator-api/appendix-e-discounts
  - name: Free Plays
    description: >-
      Grant, revoke and query packages of free plays (free spins) for a player,
      game and stake. Has to be enabled for the operator account.
    externalDocs:
      url: https://docs.hizi.io/operator-api/appendix-d-free-plays
  - name: Backoffice
    description: >-
      Optional read-only calls for game lists, game configuration, currencies
      and statistics. The same information is available in the hizi.io operator
      backoffice.
    externalDocs:
      url: https://docs.hizi.io/operator-api/appendix-b-backoffice
  - name: Health & Certified Hash
    description: >-
      Unauthenticated-style monitoring and regulatory endpoints for game engine
      and RNG health and for retrieving certified file hashes / game versions.
    externalDocs:
      url: https://docs.hizi.io/operator-api/appendix-f-health

paths:
  # ---------------------------------------------------------------------------
  # Operator → hizi.io
  # ---------------------------------------------------------------------------
  /getGameURL:
    post:
      tags: [Operator → hizi.io]
      operationId: getGameURL
      summary: Get a game launch URL for a player and game
      description: >-
        Creates the sessions needed to play and returns a URL the player should
        be directed to (typically in an iframe). The returned URL may only be
        called **once and within 60 seconds**; if the game has to be reloaded,
        or the URL was not used in time, request a new one.


        If the `playerId` is unknown it is registered with an implicit
        `registerPlayer` call. If `playerdetails` are passed, the existing
        record is updated with those values.


        Games can be launched in three modes: `REAL` (real money, the operator
        wallet API is used for debits and credits), `DEMO` (fun mode — no calls
        to the operator API are made and rounds are not recorded) and `HISTORY`
        (replay of a previously played real-money round given in `gameround`).


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `playerId` (if present), `currency`, `gameCode`,
        `mode`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GetGameURLRequest'
            examples:
              realMoney:
                summary: Start a real-money game
                value:
                  playerId: sampleplayer
                  currency: GBP
                  gameCode: testgame
                  language: en
                  mode: real
                  playerdetails:
                    country: GB
                  profile: jurisidiction-de,customProfileName
                  options:
                    homeURL: https://myhome.com/example
              demo:
                summary: Start a demo game
                value:
                  currency: GBP
                  gameCode: testgame
                  language: en
                  mode: demo
      responses:
        '200':
          description: Launch URL created.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetGameURLResponse'
              example:
                url: http://testgame?token=f264e73f-5aa4-4858-bf6e-3d02bcfa5e2d
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '451':
          $ref: '#/components/responses/UnavailableForLegalReasons'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /registerPlayer:
    post:
      tags: [Operator → hizi.io]
      operationId: registerPlayer
      summary: Register (or update) a player in the hizi.io database
      description: >-
        Optional — an unknown `playerId` in a `getGameURL` call triggers this
        implicitly. Idempotent: repeated calls update the information linked to
        the player account. No personal data is linked, only the operator-side
        player ID; submitting `country`, `gender` and `birthDate` is
        recommended because free-play templates can target them.


        If free-play templates are associated with any of the submitted
        `playerdetails`, they are granted when a new record is created
        (`isNew` = true). Stored information can be read back with
        `getPlayerDetails`.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `playerId`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RegisterPlayerRequest'
            example:
              playerId: sampleplayer
              playerdetails:
                displayName: TheBoss
                country: GB
                affiliate: Affilate No. 1
                promotionCode: promonewyear2020
                gender: F
                birthDate: '1985-05-10'
                custom Attribute 1: some information
      responses:
        '200':
          description: Player was created or updated.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RegisterPlayerResponse'
              example:
                isNew: true
                internalPlayerId: acfbf7980c4d4add8688c944c9c2a22a
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /getPlayerDetails:
    get:
      tags: [Operator → hizi.io]
      operationId: getPlayerDetails
      summary: Query the information stored for a player
      description: >-
        Returns everything stored for a player ID by earlier `registerPlayer`
        or `getGameURL` calls, plus whether the player is currently blocked in
        the hizi.io database.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `playerId`.
      parameters:
        - $ref: '#/components/parameters/playerIdQuery'
      responses:
        '200':
          description: Stored player details.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetPlayerDetailsResponse'
              example:
                isBlocked: false
                playerdetails:
                  displayName: TheBoss
                  country: GB
                  promotionCode: promonewyear2020
                  gender: F
                  birthDate: '1985-05-10'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /setPlayerBlock:
    post:
      tags: [Operator → hizi.io]
      operationId: setPlayerBlock
      summary: Block or unblock a player for all hizi.io games
      description: >-
        Recommended whenever the operator blocks a player on its own side. The
        resulting state can be read back with `getPlayerDetails`.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `playerId`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetPlayerBlockRequest'
            example:
              playerId: sampleplayer
              isBlocked: true
              blockReason: Credit card fraud
      responses:
        '200':
          $ref: '#/components/responses/Acknowledged'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /getOpenGameRounds:
    get:
      tags: [Operator → hizi.io]
      operationId: getOpenGameRounds
      summary: List open real-money gamerounds for a player
      description: >-
        Returns the hashes of the player's currently open real-money
        gamerounds. Each hash can be passed to `getGameRoundDetails` for the
        full round.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `playerId`.
      parameters:
        - $ref: '#/components/parameters/playerIdQuery'
      responses:
        '200':
          description: List of open gameround hashes.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GameRoundListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /getOpenGameRoundsByGame:
    get:
      tags: [Operator → hizi.io]
      operationId: getOpenGameRoundsByGame
      summary: List open real-money gamerounds for a game
      description: >-
        Returns the hashes of all currently open real-money gamerounds for a
        game code. Useful before `reconcileTransactions` or `voidGameRound`.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `gameCode`.
      parameters:
        - $ref: '#/components/parameters/gameCodeQuery'
      responses:
        '200':
          description: List of open gameround hashes.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GameRoundListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /getGameRoundDetails:
    get:
      tags: [Operator → hizi.io]
      operationId: getGameRoundDetails
      summary: Get the details of a single gameround
      description: >-
        Returns the full gameround record, including its status and the list of
        debit / credit / void transactions it contains. This is the call to use
        to check whether a round is still open before reconciling or voiding
        it.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `playerId`, `gameRound`.
      parameters:
        - $ref: '#/components/parameters/playerIdQuery'
        - $ref: '#/components/parameters/gameRoundQuery'
      responses:
        '200':
          description: Gameround details.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetGameRoundDetailsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /getGameRoundReport:
    get:
      tags: [Operator → hizi.io]
      operationId: getGameRoundReport
      summary: List real-money gamerounds for a player and game
      description: >-
        Returns a summary record per gameround. History is limited to the
        maximum retention period (7 days), which is also the default for
        `daysBack`.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `playerId`, `gameCode`.
      parameters:
        - $ref: '#/components/parameters/playerIdQuery'
        - $ref: '#/components/parameters/gameCodeQuery'
        - name: daysBack
          in: query
          required: false
          description: >-
            Number of days to look back. Defaults to the maximum number of days
            stored (7).
          schema:
            type: number
        - name: currency
          in: query
          required: false
          description: Optional currency to filter for.
          schema:
            $ref: '#/components/schemas/Currency'
        - name: gameRound
          in: query
          required: false
          description: Optional gameround hash (or hash prefix) to filter for.
          schema:
            $ref: '#/components/schemas/Hash'
      responses:
        '200':
          description: List of gameround summaries.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetGameRoundReportResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /getGameRoundResult:
    get:
      tags: [Operator → hizi.io]
      operationId: getGameRoundResult
      summary: Get an HTML representation of a gameround result
      description: >-
        Returns the rendered result of a gameround, either as HTML markup or as
        a short-lived URL that displays it.


        The response is **`application/json` in both cases** (as for every
        other call): with `getURL=true` the JSON contains `resultURL`, a link
        that stays valid for 10 minutes; with `getURL` false or absent it
        contains the HTML markup in the `msg` string together with the
        `gameCode` of the round.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `playerId`, `gameRound`.
      parameters:
        - $ref: '#/components/parameters/playerIdQuery'
        - $ref: '#/components/parameters/gameRoundQuery'
        - name: getURL
          in: query
          required: false
          description: >-
            If true, return a `resultURL` valid for 10 minutes instead of the
            HTML markup. If false or omitted, the HTML markup is returned in
            `msg`.
          schema:
            type: boolean
      responses:
        '200':
          description: >-
            Gameround result, either as HTML markup in `msg` or as a link in
            `resultURL`.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetGameRoundResultResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /reconcileTransactions:
    post:
      tags: [Operator → hizi.io]
      operationId: reconcileTransactions
      summary: Replay all transactions of a closed gameround to the operator
      description: >-
        Asks hizi.io to resend every transaction of a real-money gameround as a
        `doTransactions` call to the operator. The transaction hashes are the
        same as in the original calls, so the operator's `doTransactions`
        endpoint must be idempotent on them. If the round is still open it is
        closed automatically.


        Round status can be checked with `getGameRoundDetails`; open rounds per
        game with `getOpenGameRoundsByGame`.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `gameRound`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReconcileTransactionsRequest'
            example:
              gameRound: 04d4e7fc-092b-459e-bd32-c41343b4af2a
      responses:
        '200':
          description: >-
            The round was replayed. The response mirrors what the operator
            answered to the replayed `doTransactions` call.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReconcileTransactionsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /voidGameRound:
    post:
      tags: [Operator → hizi.io]
      operationId: voidGameRound
      summary: Void the initial debit of an open gameround
      description: >-
        Reverts a previously submitted debit: the operator receives a
        `doTransactions` call carrying a `void` transaction for the initial
        debit. The call fails with an error if the gameround is not open, or if
        a credit was already processed successfully.


        Round status can be checked with `getGameRoundDetails`; open rounds per
        game with `getOpenGameRoundsByGame`.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `gameRound`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VoidGameRoundRequest'
            example:
              gameRound: 04d4e7fc-092b-459e-bd32-c41343b4af2a
      responses:
        '200':
          $ref: '#/components/responses/Acknowledged'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  # ---------------------------------------------------------------------------
  # Free plays (Appendix D) — operator → hizi.io
  # ---------------------------------------------------------------------------
  /assignPlayerFreerounds:
    post:
      tags: [Free Plays]
      operationId: assignPlayerFreerounds
      summary: Assign a package of free plays for a game and stake to a player
      description: >-
        Grants `count` free plays for one game at one stake and currency. The
        operator supplies a unique `serial` per package; calls with the same
        serial are idempotent, so nothing linked to a serial is assigned twice.
        The serial is sent back to the operator in the debit transaction when
        the free play is consumed (`amount` = 0, `freePlayUsed` = true).


        No further operator-side work is needed for free plays beyond accepting
        debit transactions with an amount of zero and a known serial.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `playerId`, `gameCode`, `serial`, `currency`, `stake`,
        `count`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AssignPlayerFreeroundsRequest'
            example:
              playerId: 141ad0ffab554ba8b9022db6dfdc05ad
              gameCode: game-oddoreven
              serial: unique-value
              currency: EUR
              stake: 100
              count: 4
              expires: '2020-12-07T00:00:00Z'
      responses:
        '200':
          description: The free-play package was created.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AssignPlayerFreeroundsResponse'
              example:
                serial: unique-value
                internalid: 4596dbe02be2483598f261b7f8c853cd
                count: 4
                used: 0
                expires: '2020-12-07T00:00:00.000Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /revokePlayerFreerounds:
    post:
      tags: [Free Plays]
      operationId: revokePlayerFreerounds
      summary: Revoke a previously granted free-play package
      description: >-
        Deletes a free-play package that was granted with
        `assignPlayerFreerounds`. Idempotent — repeated calls return the same
        result. `gameCode` and `playerId` have to match the original grant.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `playerId`, `gameCode`, `serial`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RevokePlayerFreeroundsRequest'
            example:
              playerId: 141ad0ffab554ba8b9022db6dfdc05ad
              gameCode: game-oddoreven
              serial: dbf8907c-8002-4bdc-8804-3b1ec00d21be
      responses:
        '200':
          description: The package was revoked.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RevokePlayerFreeroundsResponse'
              example:
                serialRevoked: dbf8907c-8002-4bdc-8804-3b1ec00d21be
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /getPlayerFreerounds:
    get:
      tags: [Free Plays]
      operationId: getPlayerFreerounds
      summary: Query the free-play packages available to a player
      description: >-
        Returns the packages still available for a player, game and currency.
        Packages that are fully used or expired are not reported.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `playerId`, `gameCode`, `currency`.
      parameters:
        - $ref: '#/components/parameters/playerIdQuery'
        - $ref: '#/components/parameters/gameCodeQuery'
        - $ref: '#/components/parameters/currencyQuery'
      responses:
        '200':
          description: Available free-play packages.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetPlayerFreeroundsResponse'
              example:
                tickets:
                  - serial: unique-value
                    currency: EUR
                    stake: 100
                    availableTickets: 4
                  - serial: dbf8907c-8002-4bdc-8804-3b1ec00d21be
                    currency: EUR
                    stake: 500
                    availableTickets: 1
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  # ---------------------------------------------------------------------------
  # Backoffice (Appendix B) — operator → hizi.io
  # ---------------------------------------------------------------------------
  /backoffice/getGameList:
    get:
      tags: [Backoffice]
      operationId: getGameList
      summary: Get the list of games available to the operator
      description: >-
        Returns the games available to the account. The object keys are the
        game codes used in `getGameURL` and the other calls.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`.
      responses:
        '200':
          description: List of known games.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetGameListResponse'
              example:
                gameList:
                  27ac1acbefb9496baec0192289f144b9: Testgame 1
                  7ca75731adea48b08cec72f0aac04d62: Testgame 2
                extendedGameList:
                  0220f223d0754509bb2ad025d0efd719:
                    name: fbc207ffee11469a8010f7a6be00160f
                    studio: hizi.io Games
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /backoffice/getGameConfiguration:
    get:
      tags: [Backoffice]
      operationId: getGameConfiguration
      summary: Get the configuration of a game for a currency
      description: >-
        Returns the game's configuration for one currency — supported stakes,
        expected RTP, maximum win / payout odds and exposure limits.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `gameCode`, `currency`.
      parameters:
        - $ref: '#/components/parameters/gameCodeQuery'
        - $ref: '#/components/parameters/currencyQuery'
      responses:
        '200':
          description: Game configuration.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetGameConfigurationResponse'
              example:
                config:
                  stakes: [10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000]
                  currency: EUR
                  currencyMultiplier: 1
                  expectedRtp: 96.19
                  oneHundredXOdds: 5522
                  maxWinOdds: 103680
                  maxPayout: 1000
                  maxPayoutOdds: 2488327800
                  maxExposure: 10000000
                  calculatedMaxExposure: 10000000
                  operator: internal_testoperator
                gameCode: game-bells
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /backoffice/getCurrencyList:
    get:
      tags: [Backoffice]
      operationId: getCurrencyList
      summary: Get the list of known currencies and their multipliers
      description: >-
        Returns the currencies available to the account, mapped to the
        multiplier applied to base-currency values such as `maxStake`,
        `minStake` and `maxExposure`.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`.
      responses:
        '200':
          description: List of known currencies.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetCurrencyListResponse'
              example:
                currencyList:
                  EUR: 1
                  PLN: 5
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /backoffice/getStats:
    post:
      tags: [Backoffice]
      operationId: getStats
      summary: Query statistical data on a daily basis
      description: >-
        Returns bet / win / RTP records grouped by the requested properties.
        Values are reported both in the local currency and converted to EUR
        using that day's exchange rate. Unless `showTotals` is true, records
        are grouped by day.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GetStatsRequest'
            example:
              groupBy: [operator, currency]
              filterBy:
                dateFrom: '2020-09-01'
                dateToIncluding: '2020-09-20'
      responses:
        '200':
          description: Statistical records.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetStatsResponse'
              example:
                records:
                  - DAY: '2020-09-18'
                    currency: EUR
                    games: 6526
                    player: 3
                    bet: '6526.00'
                    win: '6640.00'
                    betInEuro: '6526.00'
                    winInEuro: '6640.00'
                    RTP: 101.75
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /backoffice/getTicketStats:
    post:
      tags: [Backoffice]
      operationId: getTicketStats
      summary: Query statistical data about tickets / free plays
      description: >-
        Same as `getStats`, but for ticket (free-play) usage, and with a
        smaller set of `groupBy` values.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GetTicketStatsRequest'
            example:
              groupBy: [game, currency]
              filterBy:
                dateFrom: '2020-09-01'
                dateToIncluding: '2020-09-20'
      responses:
        '200':
          description: Statistical records for tickets.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetTicketStatsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  # ---------------------------------------------------------------------------
  # hizi.io → Operator (implemented by the operator)
  # ---------------------------------------------------------------------------
  /getBalance:
    get:
      tags: [hizi.io → Operator, Discounts]
      operationId: getBalance
      summary: (Operator implements) Get a player's balance
      description: >-
        Called by hizi.io to read a player's balance. One or two balances may
        be returned; one of them has to be `real`, a `bonus` balance is
        optional and only required by some jurisdictions. When a bonus balance
        is returned, `useBonusFirst` should be returned as well so the game can
        display which funds are being used where regulation requires it.


        If `gameCode` is supplied, return the free plays / tickets allocated to
        that player and game as well (see Appendix D).


        On error, do **not** answer with HTTP 200 — return a 4xx/5xx together
        with an `errorCode` object.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `playerId`, `currency`, `gameCode` (if present).
      servers:
        - url: '{operatorApiBaseUrl}'
          description: >-
            Base URL of the operator's own API, configured in the hizi.io
            backoffice.
          variables:
            operatorApiBaseUrl:
              default: https://operator.example.com
              description: Operator-supplied base URL for the hizi.io callbacks.
      parameters:
        - $ref: '#/components/parameters/playerIdQuery'
        - $ref: '#/components/parameters/currencyQuery'
        - name: gameCode
          in: query
          required: false
          description: >-
            ID of the game (same code as in `getGameURL`). If present, return
            free plays and/or tickets allocated to that game and player.
          schema:
            type: string
        - $ref: '#/components/parameters/externalSessionIdQuery'
      responses:
        '200':
          description: The player's balance(s).
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetBalanceResponse'
              example:
                useBonusFirst: true
                balances:
                  - type: real
                    amount: 12000
                    currency: EUR
                  - type: bonus
                    amount: 8010
                    currency: EUR
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /doTransactions:
    post:
      tags: [hizi.io → Operator, Discounts]
      operationId: doTransactions
      summary: (Operator implements) Process one or more gameround transactions
      description: >-
        The wallet call. One or more transactions belonging to a single
        gameround are submitted; the operator applies them and returns the
        resulting balance(s). **Every transaction must be processed
        idempotently on its `hash`.**


        Rules:


        * Process transactions in the order received. A `void` may arrive
          *before* the debit it refers to — in that case the debit must still
          be treated as voided when it arrives.
        * If a debit fails, subsequent credits in the same request (and in
          following requests) must be declined. **If at least one operation
          fails, the HTTP status must not be 200.**
        * If the *initial* debit (`isFirstDebit` = true) fails, the round is
          closed and no further debits/credits may be accepted.
        * If a *follow-up* debit (`isFirstDebit` = false) fails, the round stays
          open — the game may continue with a smaller stake.
        * A second initial debit for the same gameround must be answered with
          error id 12 (GAMEROUNDALREADYSTARTED).
        * Debits/credits for a closed round that were not processed before must
          be answered with an error; `void` calls are processed regardless of
          round state. Transactions for an already voided round must be
          answered with HTTP 400.
        * You will never receive a debit after an `end` in the same call.
        * `transactionCount` must equal the length of `transactions`; otherwise
          reject with HTTP 400 and error id 16.


        Retry behaviour on the hizi.io side: a 4xx or 5xx on a call containing
        a **debit** is not retried and the round is marked cancelled (a `void`
        is sent in the 5xx/network case). A call containing a **credit** but no
        debit is retried on every error condition until it succeeds or the
        round is stranded — so **a credit must never fail with a 5xx**. If a
        credit is answered with a non-repeatable error, earlier debits are
        voided and the round is treated as voided; if an earlier credit had
        already succeeded, nothing is voided and the round is treated as
        crashed and has to be reconciled.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `playerId`, `gameCode`, `gameRound`, then — in exactly
        the order the transactions appear in the payload — each transaction's
        `hash` followed by its `amount` (the amount is omitted when there is
        none or it is 0, e.g. for `end`, `void`, or a debit paid with a
        ticket).
      servers:
        - url: '{operatorApiBaseUrl}'
          description: >-
            Base URL of the operator's own API, configured in the hizi.io
            backoffice.
          variables:
            operatorApiBaseUrl:
              default: https://operator.example.com
              description: Operator-supplied base URL for the hizi.io callbacks.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DoTransactionsRequest'
            examples:
              oneShotNoWin:
                summary: Single-shot round without a win (debit + end)
                value:
                  playerId: sampleplayer
                  gameCode: testgame
                  gameRound: 04d4e7fc-092b-459e-bd32-c41343b4af2a
                  transactions:
                    - type: debit
                      isFirstDebit: true
                      currency: EUR
                      hash: 13536977-5cfa-430b-a019-18430d61b48f
                      amount: 200
                      timestamp: '2020-01-21T14:48:04Z'
                      reason: spin
                    - type: end
                      hash: b92f866a-5fe9-4ac1-ae0d-9f403dfe1a12
                      timestamp: '2020-01-21T14:48:04Z'
                  transactionCount: 2
              oneShotWithWin:
                summary: Single-shot round with a win (debit + credit + end)
                value:
                  playerId: sampleplayer
                  gameCode: testgame
                  gameRound: 04d4e7fc-092b-459e-bd32-c41343b4af2a
                  transactions:
                    - type: debit
                      isFirstDebit: true
                      amount: 200
                      currency: EUR
                      hash: 13536977-5cfa-430b-a019-18430d61b48f
                      timestamp: '2020-01-21T14:48:04Z'
                      reason: spin
                    - type: credit
                      amount: 150
                      currency: EUR
                      hash: 4fac9a5d-3662-4f85-88ad-8301681d4b61
                      timestamp: '2020-01-21T14:48:04Z'
                      reason: 3 symbols in line 1
                    - type: end
                      hash: b92f866a-5fe9-4ac1-ae0d-9f403dfe1a12
                      timestamp: '2020-01-21T14:48:04Z'
                  transactionCount: 3
              freePlayDebit:
                summary: Debit paid with a free play (amount 0)
                value:
                  playerId: sampleplayer
                  gameCode: testgame
                  gameRound: 04d4e7fc-092b-459e-bd32-c41343b4af2a
                  transactions:
                    - hash: 9385ec9642034ed89ef974d0fffbdff7
                      type: debit
                      isFirstDebit: true
                      amount: 0
                      currency: EUR
                      freePlayStake: 100
                      freePlayUsed: true
                      serial: f089ec54-8e3b-40b0-92c3-e614402f5358
                  transactionCount: 1
      responses:
        '200':
          description: >-
            All submitted transactions were processed. The balances reflect the
            state *after* processing.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DoTransactionsResponse'
              example:
                balances:
                  - type: real
                    amount: 950
                    currency: EUR
                hashesProcessed:
                  - hash: 13536977-5cfa-430b-a019-18430d61b48f
                  - hash: 4fac9a5d-3662-4f85-88ad-8301681d4b61
                  - hash: b92f866a-5fe9-4ac1-ae0d-9f403dfe1a12
        '400':
          description: >-
            At least one transaction failed (insufficient funds, round already
            started, discount not eligible, `transactionCount` mismatch,
            transaction for an already voided round, …). The body must carry
            `errorCode` and `hashesProcessed`; hashes missing from
            `hashesProcessed` are treated as not processed.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DoTransactionsErrorResponse'
              example:
                errorCode:
                  id: 10
                  msg: balance too low
                  clientmsg: You do not have enough funds to place this bet.
                hashesProcessed:
                  - hash: 13536977-5cfa-430b-a019-18430d61b48f
                    errorCode:
                      id: 10
                      msg: balance too low
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /logEvent:
    post:
      tags: [hizi.io → Operator]
      operationId: logEvent
      summary: (Operator implements, optional) Receive a game event
      description: >-
        Informs the operator about events that happen during play — big wins,
        free spins won, bonus level entered and similar. An event is always
        bound to a player and optionally to a gameround. Can be switched on and
        off in the hizi.io backoffice.


        Storing this data is the operator's responsibility; exposing it to the
        operator helpdesk is recommended, as it may carry information about big
        wins or big stakes. See Appendix A for the event catalogue.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `playerId`, `gameround` (if present in the payload).
      servers:
        - url: '{operatorApiBaseUrl}'
          description: >-
            Base URL of the operator's own API, configured in the hizi.io
            backoffice.
          variables:
            operatorApiBaseUrl:
              default: https://operator.example.com
              description: Operator-supplied base URL for the hizi.io callbacks.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LogEventRequest'
            example:
              playerId: sampleplayer
              gameRound: 04d4e7fc-092b-459e-bd32-c41343b4af2a
              eventType: BONUSROUND
              eventData:
                credit:
                  amount: 950
                  currency: EUR
                freespins: 10
              reason: Player got 10 freespins
      responses:
        '200':
          $ref: '#/components/responses/Acknowledged'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '501':
          $ref: '#/components/responses/NotImplemented'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /doGameRoundReport:
    post:
      tags: [hizi.io → Operator]
      operationId: doGameRoundReport
      summary: (Operator implements, optional) Receive the details of a closed round
      description: >-
        Sends the most important details of a gameround **after the round has
        been closed**, for reporting and historical purposes. The operator has
        to request that this endpoint be enabled; otherwise nothing is sent.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`.
      servers:
        - url: '{operatorApiBaseUrl}'
          description: >-
            Base URL of the operator's own API, configured in the hizi.io
            backoffice.
          variables:
            operatorApiBaseUrl:
              default: https://operator.example.com
              description: Operator-supplied base URL for the hizi.io callbacks.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DoGameRoundReportRequest'
            example:
              gameRoundDetail:
                hash: 789b5513cb1a499dba181c6091c57d36
                mode: REAL
                game: bg-olympus-gold-96
                operator: internal_testoperator
                status: closed
                player: acfbf7980c4d4add8688c944c9c2a22a
                language: EN
                currency: EUR
                environment: gmzp
                timestamp: '2025-07-08T13:22:01.874+00:00'
                baseStake: 10
                totalBetAmount: 10
                totalPayedAmount: 10
                totalWinAmount: 15
                dataStored: {}
                ticketUsed: false
                balanceAfter: 9075
                transactions:
                  - hash: 789b5513cb1a499dba181c6091c57d36
                    type: debit
                    amount: 10
                    payed: 10
                    isFirstDebit: true
                    currency: EUR
                    timestamp: '2025-07-08T13:21:44.599Z'
                    balanceAfter: 9060
                  - hash: 3ef71295e3924915aec018ee32d10e17
                    type: credit
                    amount: 15
                    payed: 15
                    currency: EUR
                    timestamp: '2025-07-08T13:22:01.555Z'
                    balanceAfter: 9075
                  - hash: 637310141d1c4fdf8dc8579e0b8dec21
                    type: end
                    currency: EUR
                    timestamp: '2025-07-08T13:22:01.855Z'
                    balanceAfter: 9075
      responses:
        '200':
          $ref: '#/components/responses/Acknowledged'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '501':
          $ref: '#/components/responses/NotImplemented'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /doGameRoundResult:
    post:
      tags: [hizi.io → Operator]
      operationId: doGameRoundResult
      summary: (Operator implements, optional) Receive the HTML result of a closed round
      description: >-
        Sends a rendered HTML representation of a gameround result **after the
        round has been closed**. The `msg` field is BASE64-encoded HTML, decode
        it with e.g. `Buffer.from(body.msg, 'base64').toString('utf-8')`. The
        operator has to request that this endpoint be enabled; otherwise
        nothing is sent.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `gameRound`, `gameCode`, `playerId`, `msg`.
      servers:
        - url: '{operatorApiBaseUrl}'
          description: >-
            Base URL of the operator's own API, configured in the hizi.io
            backoffice.
          variables:
            operatorApiBaseUrl:
              default: https://operator.example.com
              description: Operator-supplied base URL for the hizi.io callbacks.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DoGameRoundResultRequest'
            example:
              gameRound: 789b5513cb1a499dba181c6091c57d36
              playerId: acfbf7980c4d4add8688c944c9c2a22a
              gameCode: bg-olympus-gold-96
              msg: PGh0bWw+Li4uPC9odG1sPg==
      responses:
        '200':
          $ref: '#/components/responses/Acknowledged'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '501':
          $ref: '#/components/responses/NotImplemented'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /reportAnimationEnd:
    post:
      tags: [hizi.io → Operator]
      operationId: reportAnimationEnd
      summary: (Operator implements, optional) Receive the end-of-animation signal
      description: >-
        Sent once the animation of the current gameround has finished, the
        spin / play button is active again and the player can start a new game.
        **This call should never fail and must always return HTTP 200.**


        Use case: a round can be closed immediately in a single
        `doTransactions` call while client animations are still running, so
        from the player's point of view the game is still going. Where game
        start / end events have to be reported to a regulator's safe server
        (mostly German regulation), this call provides the correct end event.


        The operator has to request that this endpoint be enabled (see the
        `reportAnimationEnd` game setting in Appendix C); otherwise nothing is
        sent.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`, `gameRound`.
      servers:
        - url: '{operatorApiBaseUrl}'
          description: >-
            Base URL of the operator's own API, configured in the hizi.io
            backoffice.
          variables:
            operatorApiBaseUrl:
              default: https://operator.example.com
              description: Operator-supplied base URL for the hizi.io callbacks.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReportAnimationEndRequest'
            example:
              gameRound: 8a73676f4e5c4b4d8507b526f48b4f4d
      responses:
        '200':
          $ref: '#/components/responses/Acknowledged'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '501':
          $ref: '#/components/responses/NotImplemented'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /ping:
    get:
      tags: [hizi.io → Operator]
      operationId: ping
      summary: (Operator implements) Availability check of the operator service
      description: >-
        Called at any time to verify that the operator service is operational;
        it must answer HTTP 200. Optional performance data may be returned for
        extended monitoring. Repeated errors on this call are reported in the
        hizi.io backoffice.


        **Fields to sign (in order):** `secret`, `X-H-AUTH-ID`,
        `X-H-TIMESTAMP`.
      servers:
        - url: '{operatorApiBaseUrl}'
          description: >-
            Base URL of the operator's own API, configured in the hizi.io
            backoffice.
          variables:
            operatorApiBaseUrl:
              default: https://operator.example.com
              description: Operator-supplied base URL for the hizi.io callbacks.
      responses:
        '200':
          description: The operator service is operational.
          headers:
            X-H-ERROR-ID:
              $ref: '#/components/headers/X-H-ERROR-ID'
            X-H-ERROR-MSG:
              $ref: '#/components/headers/X-H-ERROR-MSG'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PingResponse'
              example:
                performanceData:
                  status: operational
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '4XX':
          $ref: '#/components/responses/OtherClientError'
        '5XX':
          $ref: '#/components/responses/ServerError'

  # ---------------------------------------------------------------------------
  # Health & certified hashes (Appendix F)
  # ---------------------------------------------------------------------------
  /gameapi/{gameCode}/health:
    get:
      tags: [Health & Certified Hash]
      operationId: getGameEngineHealth
      summary: Health of a game engine
      description: >-
        Returns HTTP 200 and a JSON body when the game engine for `gameCode` is
        healthy. The response body shape is not specified in the
        documentation — treat the HTTP status as the signal.
      security: []
      parameters:
        - $ref: '#/components/parameters/gameCodePath'
      responses:
        '200':
          description: The game engine is healthy. JSON body, shape unspecified.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnspecifiedJsonResponse'
        '404':
          $ref: '#/components/responses/NotFound'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /gameapi/{gameCode}/certifiedfiles:
    get:
      tags: [Health & Certified Hash]
      operationId: getGameEngineCertifiedFiles
      summary: Certified file hashes / version of a game engine
      description: >-
        Returns the certified file hashes and game version for `gameCode`, used
        for regulatory verification. Returns HTTP 200 and a JSON body on
        success; the body shape is not specified in the documentation.
      security: []
      parameters:
        - $ref: '#/components/parameters/gameCodePath'
      responses:
        '200':
          description: Certified file hashes. JSON body, shape unspecified.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnspecifiedJsonResponse'
        '404':
          $ref: '#/components/responses/NotFound'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /rngFortuna/health:
    get:
      tags: [Health & Certified Hash]
      operationId: getRngHealth
      summary: Health of the Fortuna RNG service
      description: >-
        Returns HTTP 200 and a JSON body when the RNG service is healthy. The
        response body shape is not specified in the documentation.
      security: []
      responses:
        '200':
          description: The RNG is healthy. JSON body, shape unspecified.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnspecifiedJsonResponse'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /rngFortuna/certifiedfiles:
    get:
      tags: [Health & Certified Hash]
      operationId: getRngCertifiedFiles
      summary: Certified file hashes of the Fortuna RNG service
      description: >-
        Returns the certified file hashes of the RNG service for regulatory
        verification. HTTP 200 and a JSON body on success; the body shape is
        not specified in the documentation.
      security: []
      responses:
        '200':
          description: Certified file hashes. JSON body, shape unspecified.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnspecifiedJsonResponse'
        '5XX':
          $ref: '#/components/responses/ServerError'

  /game-swiss-info.json:
    get:
      tags: [Health & Certified Hash]
      operationId: getSwissGameInfo
      summary: Game information document for Swiss regulation
      description: >-
        Static JSON document required by Swiss regulation. It is served from a
        **separate host** (`[swiss-endpoint-url]`), not from the RGS API
        endpoint, and has to be enabled by hizi.io support. The document shape
        is not specified in the documentation.
      security: []
      servers:
        - url: https://{swissEndpointUrl}
          description: >-
            Swiss regulation endpoint. Contact hizi.io support for the value of
            the `[swiss-endpoint-url]` placeholder.
          variables:
            swissEndpointUrl:
              default: swiss-endpoint-url
              description: Host serving the Swiss regulation document.
      responses:
        '200':
          description: Swiss game information document. Shape unspecified.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnspecifiedJsonResponse'
        '404':
          $ref: '#/components/responses/NotFound'
        '5XX':
          $ref: '#/components/responses/ServerError'

components:
  # ---------------------------------------------------------------------------
  # Security
  # ---------------------------------------------------------------------------
  securitySchemes:
    hiziAuthId:
      type: apiKey
      in: header
      name: X-H-AUTH-ID
      description: >-
        Operator ID. Sent on every request in both directions, and part of
        every signature.
    hiziAuthSignature:
      type: apiKey
      in: header
      name: X-H-AUTH-SIG
      description: >-
        Signature of the request, sent on every request in both directions.


        OpenAPI cannot express an HMAC-over-computed-string scheme natively, so
        this is modelled as an `apiKey` header. The value is **not** a static
        key: it is the **base64-encoded SHA-256 HMAC digest** of a defined,
        ordered set of request fields joined with a hash character (`#`).


        The first three fields are always the shared `secret`, the
        `X-H-AUTH-ID` header and the `X-H-TIMESTAMP` header; the remaining
        fields differ per operation and are listed in each operation's
        description under "Fields to sign". For `doTransactions` the per-
        transaction hashes (and non-zero amounts) are appended in exactly the
        order the transactions appear in the payload.


        The secret is retrieved by logging in to the hizi.io backoffice, which
        also provides sample implementations. Including the timestamp in the
        signature is what makes it safe to share the secret without ever
        sending it.
    hiziTimestamp:
      type: apiKey
      in: header
      name: X-H-TIMESTAMP
      description: >-
        ISO 8601 timestamp of the request (UTC recommended, e.g.
        `2020-01-21T14:48:04Z`). Sent on every request in both directions and
        always included in the signature.

  # ---------------------------------------------------------------------------
  # Reusable parameters
  # ---------------------------------------------------------------------------
  parameters:
    playerIdQuery:
      name: playerId
      in: query
      required: true
      description: ID that identifies the player at the operator site.
      schema:
        type: string
    currencyQuery:
      name: currency
      in: query
      required: true
      description: ISO 4217 currency code the call applies to.
      schema:
        $ref: '#/components/schemas/Currency'
    gameCodeQuery:
      name: gameCode
      in: query
      required: true
      description: >-
        Code that identifies the game, as returned by `getGameList` or passed
        to `getGameURL`.
      schema:
        type: string
    gameRoundQuery:
      name: gameRound
      in: query
      required: true
      description: >-
        Hash that identifies the gameround, as returned by e.g.
        `getOpenGameRounds` or `getGameRoundReport`.
      schema:
        $ref: '#/components/schemas/Hash'
    externalSessionIdQuery:
      name: externalSessionId
      in: query
      required: false
      description: Reference to the session ID passed in the `getGameURL` call.
      schema:
        type: string
    gameCodePath:
      name: gameCode
      in: path
      required: true
      description: Code that identifies the game to query.
      schema:
        type: string

  # ---------------------------------------------------------------------------
  # Reusable response headers
  # ---------------------------------------------------------------------------
  headers:
    X-H-ERROR-ID:
      description: >-
        Error ID that caused the call to fail (see the `x-error-codes`
        extension for the catalogue). **If this header is present the call has
        failed, even when the HTTP status is 200** — treat it exactly like a
        400.
      required: false
      schema:
        type: integer
    X-H-ERROR-MSG:
      description: Textual representation of the error that caused the call to fail.
      required: false
      schema:
        type: string

  # ---------------------------------------------------------------------------
  # Reusable responses
  # ---------------------------------------------------------------------------
  responses:
    Acknowledged:
      description: >-
        Success. The documented sample response is an empty JSON object; an
        `errorCode` object may be present but is ignored on a 200 unless
        `X-H-ERROR-ID` is also set.
      headers:
        X-H-ERROR-ID:
          $ref: '#/components/headers/X-H-ERROR-ID'
        X-H-ERROR-MSG:
          $ref: '#/components/headers/X-H-ERROR-MSG'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AcknowledgedResponse'
          example: {}
    BadRequest:
      description: >-
        The request could not be processed due to an error processing the
        request payload. **Do not retry.**
      headers:
        X-H-ERROR-ID:
          $ref: '#/components/headers/X-H-ERROR-ID'
        X-H-ERROR-MSG:
          $ref: '#/components/headers/X-H-ERROR-MSG'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            errorCode:
              id: 1
              msg: player id unknown
    Unauthorized:
      description: >-
        The signature of the request was wrong and no other authorisation
        could be performed (error id 1001). **Do not retry.**
      headers:
        X-H-ERROR-ID:
          $ref: '#/components/headers/X-H-ERROR-ID'
        X-H-ERROR-MSG:
          $ref: '#/components/headers/X-H-ERROR-MSG'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Forbidden:
      description: >-
        The call was authenticated but there is no permission to use this
        endpoint. **Do not retry.**
      headers:
        X-H-ERROR-ID:
          $ref: '#/components/headers/X-H-ERROR-ID'
        X-H-ERROR-MSG:
          $ref: '#/components/headers/X-H-ERROR-MSG'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: The endpoint was not found. **Do not retry.**
      headers:
        X-H-ERROR-ID:
          $ref: '#/components/headers/X-H-ERROR-ID'
        X-H-ERROR-MSG:
          $ref: '#/components/headers/X-H-ERROR-MSG'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    RateLimitExceeded:
      description: >-
        Rate limit exceeded (error id 1300). Retry after waiting; if it happens
        repeatedly, contact customer support.
      headers:
        X-H-ERROR-ID:
          $ref: '#/components/headers/X-H-ERROR-ID'
        X-H-ERROR-MSG:
          $ref: '#/components/headers/X-H-ERROR-MSG'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    UnavailableForLegalReasons:
      description: >-
        Content not available for legal reasons (error id 28, REGIONISBLOCKED).
        **Do not retry.**
      headers:
        X-H-ERROR-ID:
          $ref: '#/components/headers/X-H-ERROR-ID'
        X-H-ERROR-MSG:
          $ref: '#/components/headers/X-H-ERROR-MSG'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    OtherClientError:
      description: >-
        Some other, non-specific client error occurred (includes 404 endpoint
        not found, 405 wrong method and 451 blocked for legal reasons).
        **Do not retry.**
      headers:
        X-H-ERROR-ID:
          $ref: '#/components/headers/X-H-ERROR-ID'
        X-H-ERROR-MSG:
          $ref: '#/components/headers/X-H-ERROR-MSG'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotImplemented:
      description: >-
        The API call is not implemented — returned by operators that have not
        implemented an optional callback. **Do not retry.**
      headers:
        X-H-ERROR-ID:
          $ref: '#/components/headers/X-H-ERROR-ID'
        X-H-ERROR-MSG:
          $ref: '#/components/headers/X-H-ERROR-MSG'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    ServerError:
      description: >-
        An unexpected internal error occurred; treat it as temporary. The call
        should be retried unless an `X-H-DONOTRETRY` header is received. Note
        that a `doTransactions` call containing a debit is **not** retried by
        hizi.io — it is voided instead — so a credit must never answer 5xx.
      headers:
        X-H-ERROR-ID:
          $ref: '#/components/headers/X-H-ERROR-ID'
        X-H-ERROR-MSG:
          $ref: '#/components/headers/X-H-ERROR-MSG'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

  # ---------------------------------------------------------------------------
  # Schemas
  # ---------------------------------------------------------------------------
  schemas:
    # --- Basic types (data-types.md) ----------------------------------------
    Amount:
      type: integer
      minimum: 0
      description: >-
        A money value, always expressed as **1/100 of the base currency** — an
        integer, never a fraction or decimal (€12.00 = `1200`). This is a fixed
        ×100 factor, not necessarily the currency's ISO minor unit. An amount
        may be 0, e.g. when a granted free play is used instead of a stake.
      example: 1200
    Currency:
      type: string
      description: ISO 4217 three-letter currency code (uppercase).
      pattern: '^[A-Z]{3}$'
      example: EUR
    Language:
      type: string
      description: >-
        Language code, 2 or 5 letters (e.g. `en`, `en-US`). Fallbacks may apply
        if a game does not support the requested language.
      example: en
    Country:
      type: string
      description: ISO 3166-2 two-letter country code (uppercase).
      pattern: '^[A-Z]{2}$'
      example: GB
    Hash:
      type: string
      description: >-
        UUID identifying a gameround, transaction or other unique object. The
        documentation defines it as a 128-bit UUID in its 36-character form
        (`123e4567-e89b-12d3-a456-426655440000`), but samples elsewhere in the
        docs also use the 32-character form without dashes
        (`789b5513cb1a499dba181c6091c57d36`), so no strict pattern is enforced
        here.
      example: 123e4567-e89b-12d3-a456-426655440000
    Timestamp:
      type: string
      format: date-time
      description: ISO 8601 timestamp; UTC is recommended.
      example: '2020-01-21T14:48:04Z'
    JsonData:
      type: object
      description: Any valid JSON object.
      additionalProperties: true
    UnspecifiedJsonResponse:
      type: object
      description: >-
        A JSON response whose shape is not specified in the documentation.
      additionalProperties: true

    # --- Object types (data-types.md) ---------------------------------------
    PlayerBalance:
      type: object
      description: The balance of a player for one balance type and currency.
      required: [type, amount, currency]
      properties:
        type:
          type: string
          description: Which pot of money this balance describes.
          enum: [real, bonus, demo]
        amount:
          allOf:
            - $ref: '#/components/schemas/Amount'
          description: Balance in 1/100 of the base currency, no fractions.
        currency:
          $ref: '#/components/schemas/Currency'
      example:
        type: real
        amount: 1200
        currency: EUR
    GameTransaction:
      type: object
      description: >-
        A single transaction belonging to a gameround. The gameround hash is
        always carried in the request or response body around it, not in the
        transaction itself. Which attributes apply depends on `type`.
      required: [type]
      properties:
        type:
          type: string
          description: >-
            * `debit` — the player placed a wager. With `isFirstDebit` true
              this starts a new gameround. It is the operator's responsibility
              to decide whether real or bonus money is used.

            * `credit` — a win to be added to the player's account.

            * `void` — a debit is to be voided. The void may arrive *before*
              the debit it refers to; in that case the debit must still end up
              voided (nothing deducted). The transaction to void is identified
              by its `hash`.

            * `end` — the gameround is finished. Always sent when a round
              closes, including after a void.
          enum: [debit, credit, void, end]
        isFirstDebit:
          type: boolean
          description: >-
            Only meaningful for `debit` (ignore it on other types). True marks
            the first bet that starts a new gameround. Note that some samples
            in the prose docs show this as the string `"true"`.
        amount:
          allOf:
            - $ref: '#/components/schemas/Amount'
          description: >-
            Value of the transaction in 1/100 of the base currency. Only for
            `debit` and `credit`. May be 0 when a granted free play is used.
        discountAmount:
          allOf:
            - $ref: '#/components/schemas/Amount'
          description: >-
            Optional discount to be applied to `amount`, only on `debit`. The
            **original** stake is sent as `amount`, and the operator deducts
            `discountAmount` when debiting the wallet: `amount` 100 with
            `discountAmount` 10 debits 90. See Appendix E.
        discountCampaignId:
          type: string
          description: >-
            Optional campaign ID of the discount being applied, only on
            `debit`. This is the `campaignId` the operator sent in
            `discountInfo`.
        currency:
          allOf:
            - $ref: '#/components/schemas/Currency'
          description: >-
            Currency the transaction is performed in. Only for `debit` and
            `credit`.
        hash:
          allOf:
            - $ref: '#/components/schemas/Hash'
          description: >-
            Identifies the transaction. Present on `debit`, `credit` and
            `void`. **All calls have to be idempotent on this value.**
        timestamp:
          allOf:
            - $ref: '#/components/schemas/Timestamp'
          description: >-
            Timestamp of the transaction. Only transmitted when reporting
            transactions back to the operator.
        reason:
          type: string
          description: Optional reason for this transaction.
        freePlayStake:
          allOf:
            - $ref: '#/components/schemas/Amount'
          description: >-
            When a free play is used (`amount` = 0), the stake the gameround is
            played at. See Appendix D.
        freePlayUsed:
          type: boolean
          description: Set to true when a free play is used instead of a debited amount.
        serial:
          type: string
          description: >-
            Serial of the free-play package used — the serial supplied in the
            `assignPlayerFreerounds` call.
    ErrorCode:
      type: object
      description: >-
        Error information returned as the result of an API call. On a 200
        response this object is ignored unless an `X-H-ERROR-ID` header is
        present.
      required: [id]
      properties:
        id:
          type: integer
          description: >-
            ID of the error that occurred; 0 means no error. The catalogue (also
            available machine-readable in the top-level `x-error-codes`
            extension) is:


            `0` SUCCESS · `1` PLAYERNOTFOUND · `2` PLAYERBLOCKED ·
            `10` BALANCETOOLOW · `11` BETLIMITREACHED ·
            `12` GAMEROUNDALREADYSTARTED · `13` GAMEROUNDNOTACTIVE ·
            `14` GAMEROUNDNOTFOUND · `15` GAMEROUNDNOTSTARTED ·
            `16` TRANSACTIONCOUNTWRONG · `17` GAMEROUNDPLAYERMISMATCH ·
            `18` CURRENCYINVALID · `19` INVALIDPARAMETER ·
            `20` TRANSACTIONAFTEREND · `21` GAMEROUNDISLOCKED ·
            `22` GAMEROUNDISCLOSING · `23` SPINNOTAVAILABLE ·
            `24` DEBITSHOULDBECANCELLED · `25` COOLOFFPERIODACTIVE ·
            `26` TRANSACTIONSARERETRYING · `27` ENDTRANSACTIONALREADYRECEIVED ·
            `28` REGIONISBLOCKED · `29` NOTLOGGEDON ·
            `30` RESPONSIBLEGAMINGLIMITREACHED ·
            `31` RESPONSIBLESGAMINGSESSIONEXPIRED · `32` PLAYERNOAUTHENTICATED ·
            `33` DISCOUNTNOTELIGIBLE · `34` GENERICBETERROR ·
            `35` GENERICWINERROR · `36` OPERATIONBEINGPROCESSED ·
            `37` TRANSACTIONALREADYEXISTS · `38` REALITYCHECK ·
            `1001` SIGNATUREWRONG · `1002` PAYLOADNOTJSON · `1003` WRONGHEADERS ·
            `1004` PARAMETERMISSING · `1005` INVALIDGAMEMODE ·
            `1006` GAMECODENOTFOUND · `1007` GAMECONFIGNOTSUPPORTED ·
            `1008` SESSIONINVALID · `1009` RNGFAILURE · `1010` NETWORKERROR ·
            `1200` DATASTRUCTUREWRONG · `1300` RATELIMITEXCEEDED ·
            `-1` UNEXPECTED.
          example: 1
        msg:
          type: string
          description: >-
            Optional explanation. Not displayed in the client; useful in log
            files.
          example: player id unknown
        clientmsg:
          type: string
          description: >-
            Optional message to be displayed in the game client. Shown without
            further interpretation.
          example: You reached your daily betting limit!
    ErrorResponse:
      type: object
      description: >-
        Body of a failed call. `errorCode` carries the machine-readable reason;
        the same ID is usually also sent in the `X-H-ERROR-ID` header.
      properties:
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
      additionalProperties: true
    AcknowledgedResponse:
      type: object
      description: >-
        Body of a successful call that returns no data. The documented samples
        are an empty JSON object.
      properties:
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
      additionalProperties: true
    ClientMessage:
      type: object
      description: >-
        Triggers a dialog to be rendered in the game client — e.g. a switch
        from bonus to real money, a betting-limit warning or a reality check.
        Additional flags let the operator interrupt autoplay.
      required: [title, msg]
      properties:
        title:
          type: string
          description: Title of the message box.
        msg:
          type: string
          description: Message to be displayed in the message box in the game client.
        buttons:
          type: array
          description: >-
            Optional buttons to display. If absent, a standard "OK" button that
            just closes the dialog is shown.
          items:
            $ref: '#/components/schemas/ClientMessageButton'
        stopAutoPlay:
          type: boolean
          description: >-
            If true, any running autoplay is interrupted and has to be resumed
            by the player manually.
      example:
        title: Reality check
        msg: You are now playing for 60 minutes, do you want to continue ?
        stopAutoPlay: true
        buttons:
          - label: 'Yes'
          - label: 'No'
            url: http://gotolobby
    ClientMessageButton:
      type: object
      description: A button inside a `clientMessage` dialog.
      required: [label]
      properties:
        label:
          type: string
          description: Text on the button.
        url:
          type: string
          description: >-
            URL to open when the button is clicked. If absent, the button just
            closes the dialog.
    Ticket:
      type: object
      description: >-
        A package of tickets / free plays available to a player. See
        Appendix D.
      required: [serial, stake, currency, availableTickets]
      properties:
        serial:
          type: string
          description: Serial assigned to this package; unique per operator.
        stake:
          allOf:
            - $ref: '#/components/schemas/Amount'
          description: >-
            Stake the free play is valid for (positive, non-zero, no
            fractions).
        currency:
          $ref: '#/components/schemas/Currency'
        availableTickets:
          type: number
          description: Number of free plays still available (positive, non-zero integer).
      example:
        serial: unique-value
        currency: EUR
        stake: 100
        availableTickets: 4
    DiscountInfo:
      type: object
      description: >-
        Operator-funded discount available to the player. May be returned in
        any call that returns a `balances` object (`getBalance`,
        `doTransactions`); hizi.io then applies it on subsequent debits by
        sending `discountAmount` and `discountCampaignId`. The operator remains
        responsible for checking eligibility and must reject a debit whose
        discount is no longer eligible with error id 33
        (DISCOUNTNOTELIGIBLE) — debiting nothing and starting no round. This
        feature has to be activated by hizi.io support.
      required: [text, currency, discounts, campaignId]
      properties:
        text:
          type: string
          description: >-
            Text to display in the frontend; should be localised for the
            session.
          example: happy hour
        currency:
          $ref: '#/components/schemas/Currency'
        discounts:
          type: array
          description: Absolute discount per stake.
          items:
            $ref: '#/components/schemas/DiscountEntry'
        campaignId:
          type: string
          description: >-
            Campaign ID identifying the discount. It is sent back as
            `discountCampaignId` on debit transactions.
        validFrom:
          allOf:
            - $ref: '#/components/schemas/Timestamp'
          description: >-
            When the campaign becomes valid. Defaults to the current date if
            absent.
        validTo:
          allOf:
            - $ref: '#/components/schemas/Timestamp'
          description: >-
            When the campaign stops being valid (inclusive). Valid forever if
            absent.
    DiscountEntry:
      type: object
      description: The discount granted for one specific stake.
      required: [stake, absoluteDiscountAmount]
      properties:
        stake:
          allOf:
            - $ref: '#/components/schemas/Amount'
          description: Stake in 1/100 of the base currency.
        absoluteDiscountAmount:
          allOf:
            - $ref: '#/components/schemas/Amount'
          description: >-
            Discount in 1/100 of the base currency applied to the stake above.
        spinsRemaining:
          type: number
          description: Remaining number of discounted spins; informational only.
    HashProcessed:
      type: object
      description: >-
        The processing result of one submitted transaction hash. Hashes that
        are **not** in the `hashesProcessed` list are assumed not to have been
        processed and are treated as an error.
      required: [hash]
      properties:
        hash:
          allOf:
            - $ref: '#/components/schemas/Hash'
          description: A transaction hash that was submitted in the call.
        errorCode:
          allOf:
            - $ref: '#/components/schemas/ErrorCode'
          description: >-
            Result of processing this transaction. **If absent, processing was
            successful.** Any `errorCode` returned here is logged by hizi.io.
    PlayerDetails:
      type: object
      description: >-
        One-level JSON object of information stored for a player. No personal
        data is linked to a name — everything hangs off the operator-side
        player ID. Any attribute name may be used (each only once); the
        properties listed here are the predefined ones. All attributes can be
        used to target a group of players when granting free plays.
      additionalProperties: true
      properties:
        displayName:
          type: string
          maxLength: 80
          description: Player's nickname; may be displayed in some games.
          example: TheBoss
        country:
          allOf:
            - $ref: '#/components/schemas/Country'
          description: >-
            Country of residence of the player. May have legislative
            implications.
        affiliate:
          type: string
          maxLength: 80
          description: >-
            Code/channel identifying the affiliate the player came from.
            Players can be awarded free rounds based on it.
        promotionCode:
          type: string
          maxLength: 80
          description: >-
            Code allowing the operator to grant a bonus to the player. Players
            can be awarded free rounds based on it.
        gender:
          type: string
          description: Player's gender.
          enum: [M, F, O]
        birthDate:
          type: string
          description: Player's birth date, format `YYYY-MM-DD`.
          example: '1978-05-12'
        isTestPlayer:
          type: boolean
          default: false
          description: True when the player should be excluded from billing reports.

    # --- Game settings (Appendix C) -----------------------------------------
    GameSettings:
      type: object
      description: >-
        Game settings passed in the `options` parameter of `getGameURL`, which
        override anything defined by the `profile`. `maxExposure`, `maxStake`
        and `minStake` are integers in 1/100 of the **base** currency and are
        multiplied by the launch currency's multiplier before being applied;
        `customStakes` are in 1/100 of the **game** currency, so no multiplier
        is applied. The last group of properties is documented only in the
        jurisdiction-profile table of Appendix C, not in the settings list.
      additionalProperties: true
      properties:
        autoplayEnabled:
          type: boolean
          default: true
        autoplayLossLimitRequired:
          type: boolean
          default: false
        displayCoins:
          type: boolean
          default: true
        displayJackpotOdds:
          type: boolean
          default: false
        displayRTP:
          type: boolean
          default: false
        displayXRTP:
          type: boolean
          default: false
        displayClock:
          type: boolean
          default: true
          description: If false, no clock is shown in games.
        forceOrientation:
          type: string
          default: none
          enum: [none, landscape, portrait]
        gambleEnabled:
          type: boolean
          default: true
        historyURL:
          type: string
        homeURL:
          type: string
        loadMsg:
          type: string
        maxExposure:
          type: number
          description: Maximum exposure, in 1/100 of the base currency.
        maxStake:
          type: number
          description: Maximum stake, in 1/100 of the base currency.
        minStake:
          type: number
          description: Minimum stake, in 1/100 of the base currency.
        minLoadTime:
          type: number
          description: In milliseconds.
        minSpinTime:
          type: number
          description: In milliseconds.
        multipleInstancesAllowed:
          type: boolean
          default: false
        rcDisplayWinLoss:
          type: boolean
          default: false
          description: Reality check — display win/loss.
        rcEnabled:
          type: boolean
          default: false
          description: Reality check enabled.
        rcInterval:
          type: number
          description: Reality check interval, in milliseconds.
        stopEnabled:
          type: boolean
          default: true
        topupURL:
          type: string
        turboEnabled:
          type: boolean
          default: true
        homeEnabled:
          type: boolean
          default: true
          description: If false, no home button is displayed.
        redirectTarget:
          type: string
          default: top
          enum: [self, parent, top]
          description: Redirect target for the "Home" and "History" buttons.
        dynamicMinSpinTime:
          type: boolean
          default: false
          description: >-
            For German jurisdiction — make spin time dynamic to hit the average
            spin time.
        defaultStakeIndex:
          type: number
          description: Index of the default stake (starts at 0).
        operatorHandlesErrors:
          type: boolean
          default: false
          description: >-
            If set, the game generates no error popups; all errors are sent to
            the embedding frame with `postMessage`.
        customStakes:
          type: array
          description: >-
            Custom stakes available in the game, in 1/100 of the **game**
            currency (no multiplier applied). Has to be activated for the
            operator account — contact hizi.io support.
          items:
            type: number
          example: [10000, 20000, 100000, 200000]
        abbreviateAmounts:
          type: boolean
          default: true
          description: >-
            Abbreviate large amounts in balance/stake display (`1M` instead of
            `1,000,000`). Should only be used for social casinos.
        showExactRTP:
          type: boolean
          default: false
          description: Show the exact (non-rounded) RTP in the frontend rules.
        packageBuyEnabled:
          type: boolean
          default: true
          description: >-
            Enables the feature package buy in games that support it. If false
            it is unavailable for the session.
        displaySessionTimer:
          type: boolean
          default: false
          description: Tell the game to display a session timer (e.g. for UKGC).
        skipWinsEqualToOrLessThanStake:
          type: boolean
          default: false
          description: Do not celebrate small wins (e.g. for UKGC).
        reportAnimationEnd:
          type: boolean
          default: false
          description: Enable the `reportAnimationEnd` callback for this session.
        launcherType:
          type: boolean
          description: >-
            Used for special integrations; only use after contacting hizi.io
            support. (Appendix C types this as boolean even though the name
            suggests a string value — confirm with support before using it.)
        autoplayShowTotalStake:
          type: boolean
          default: false
          description: >-
            Show the total stake in the autoplay popup; used for Brazilian
            legislation.
        translateErrors:
          type: boolean
          default: false
          description: >-
            Use a standard translation for unexpected errors; used for
            Brazilian legislation.
        currencyToDisplay:
          type: string
          description: >-
            String to use when displaying the currency; mostly for custom
            currencies / sweepstakes.
        displayNetPosition:
          type: boolean
          description: >-
            Used by the `jurisdiction-ukgc` and `jurisdiction-caon` profiles.
            Documented only in the profile table.
        forceDefaultStake:
          type: boolean
          description: >-
            Used by the `jurisdiction-nl` profile. Documented only in the
            profile table.
        maxPackageStake:
          type: number
          description: >-
            Maximum package-buy stake, used by the `jurisdiction-gr` profile.
            Documented only in the profile table.
        sessionTimeoutInSeconds:
          type: number
          description: >-
            Used by the `jurisdiction-br` and `jurisdiction-caon` profiles.
            Documented only in the profile table.
        hideDemoBalance:
          type: boolean
          description: >-
            Used by the `jurisdiction-br` profile. Documented only in the
            profile table.
        displayPaytableOnEnterGame:
          type: boolean
          description: >-
            Used by the `jurisdiction-br` profile. Documented only in the
            profile table.
        doNotStoreSettings:
          type: array
          description: >-
            Settings the game must not persist between sessions (the
            `jurisdiction-br` profile sets `['autoplay']`). Documented only in
            the profile table.
          items:
            type: string
    GameVariables:
      type: object
      description: >-
        Game variables passed in the `variables` parameter of `getGameURL` and
        forwarded to the game client. Only known variables are accepted —
        anything else raises an invalid-parameter error, hence
        `additionalProperties: false`.
      additionalProperties: false
      properties:
        rcElapsed:
          type: number
          description: Time already elapsed, for reality-check enabled games.

    # --- Gameround shapes ---------------------------------------------------
    GameRound:
      type: object
      description: >-
        Details of a gameround as returned by `getGameRoundDetails` and
        `reconcileTransactions`. The documentation describes this as free-form
        JSON data listing the fields below, so additional properties are
        possible.
      additionalProperties: true
      properties:
        hash:
          $ref: '#/components/schemas/Hash'
        player:
          type: string
          description: Player the round belongs to.
        mode:
          type: string
          description: Mode the round was played in (`REAL`, `DEMO`, `HISTORY`).
        game:
          type: string
          description: Game code of the round.
        operator:
          type: string
        status:
          type: string
          description: >-
            Status of the round (e.g. `closed`). Used to decide whether a round
            can still be reconciled or voided.
        language:
          $ref: '#/components/schemas/Language'
        currency:
          $ref: '#/components/schemas/Currency'
        environment:
          type: string
        timestamp:
          $ref: '#/components/schemas/Timestamp'
        totalBetAmount:
          $ref: '#/components/schemas/Amount'
        totalWinAmount:
          $ref: '#/components/schemas/Amount'
        transactions:
          type: array
          description: The debits, credits and voids that make up the round.
          items:
            $ref: '#/components/schemas/GameTransaction'
    GameRoundSummary:
      type: object
      description: >-
        One record of the `getGameRoundReport` list. The documentation names
        the fields but not their types.
      additionalProperties: true
      properties:
        hash:
          $ref: '#/components/schemas/Hash'
        mode:
          type: string
        game:
          type: string
        status:
          type: string
        timestamp:
          $ref: '#/components/schemas/Timestamp'
        bet:
          description: >-
            Total bet of the round. The documentation does not state the type;
            expect an `amount` (integer, 1/100 of the base currency).
        win:
          description: >-
            Total win of the round. The documentation does not state the type;
            expect an `amount` (integer, 1/100 of the base currency).
    GameRoundReportDetail:
      type: object
      description: >-
        Payload of the `doGameRoundReport` callback. The documentation types
        this as free-form JSON data, so the properties below are inferred from
        the documented sample call and additional properties are possible.
      additionalProperties: true
      properties:
        hash:
          $ref: '#/components/schemas/Hash'
        mode:
          type: string
        game:
          type: string
        operator:
          type: string
        status:
          type: string
        player:
          type: string
        language:
          $ref: '#/components/schemas/Language'
        currency:
          $ref: '#/components/schemas/Currency'
        environment:
          type: string
        timestamp:
          $ref: '#/components/schemas/Timestamp'
        baseStake:
          $ref: '#/components/schemas/Amount'
        totalBetAmount:
          $ref: '#/components/schemas/Amount'
        totalPayedAmount:
          $ref: '#/components/schemas/Amount'
        totalWinAmount:
          $ref: '#/components/schemas/Amount'
        dataStored:
          $ref: '#/components/schemas/JsonData'
        ticketUsed:
          type: boolean
        balanceAfter:
          allOf:
            - $ref: '#/components/schemas/Amount'
          description: Player balance after the round was settled.
        transactions:
          type: array
          description: >-
            The round's transactions. In addition to the `gameTransaction`
            fields, the sample shows `payed` and `balanceAfter` per
            transaction.
          items:
            allOf:
              - $ref: '#/components/schemas/GameTransaction'
              - type: object
                properties:
                  payed:
                    allOf:
                      - $ref: '#/components/schemas/Amount'
                    description: >-
                      Amount actually paid, in 1/100 of the base currency
                      (inferred from the sample call).
                  balanceAfter:
                    allOf:
                      - $ref: '#/components/schemas/Amount'
                    description: >-
                      Player balance after this transaction (inferred from the
                      sample call).
    TicketInformation:
      type: object
      description: >-
        Sent in `doTransactions` when a package of tickets / free plays is
        finished within that gameround. Has to be activated with the
        `addSerialToTransactions` operator setting.
      properties:
        serial:
          type: string
          description: Serial of the ticket / free-play package.
        stake:
          type: number
          description: Stake the package was granted for.
        won:
          type: number
          description: Total amount won from the package.
        isFinal:
          type: boolean
          description: True when this was the last ticket of the package.
        used:
          type: number
          description: Number of tickets used from the package.

    # --- Operator → hizi.io request/response bodies -------------------------
    GetGameURLRequest:
      type: object
      required: [currency, gameCode, language, mode]
      properties:
        playerId:
          type: string
          description: >-
            ID that identifies the player at the operator site. Mandatory for
            `mode` REAL and HISTORY. It **should be omitted** for DEMO unless
            there is a strong reason to persist demo balances.
        currency:
          allOf:
            - $ref: '#/components/schemas/Currency'
          description: Currency the game should be started in.
        gameCode:
          type: string
          description: Game ID of the game to be started.
        language:
          $ref: '#/components/schemas/Language'
        mode:
          type: string
          description: >-
            Mode to launch in: `REAL`, `DEMO` or `HISTORY`. (No `enum` is
            declared here because the documented sample payloads use the
            lowercase forms `real` and `demo`.)
          example: REAL
        gameround:
          allOf:
            - $ref: '#/components/schemas/Hash'
          description: >-
            Gameround to replay. Only used, and then mandatory, when
            `mode` = HISTORY. Only real-money rounds can be replayed.
        playerdetails:
          allOf:
            - $ref: '#/components/schemas/PlayerDetails'
          description: >-
            Optional player information; the stored record is updated with
            these values. Same structure as in `registerPlayer`.
        profile:
          type: string
          description: >-
            A predefined set of game parameters. If absent, default settings
            are used. Multiple profiles can be combined by separating them with
            commas. Jurisdiction profiles documented in Appendix C:
            `jurisdiction-mga`, `jurisdiction-ukgc`, `jurisdiction-agcc`,
            `jurisdiction-de`, `jurisdiction-gr`, `jurisdiction-bg`,
            `jurisdiction-nl`, `jurisdiction-se`, `jurisdiction-lt`,
            `jurisdiction-br`, `jurisdiction-caon` — plus any custom profile
            created in the backoffice.
          example: jurisdiction-de,customProfileName
        options:
          allOf:
            - $ref: '#/components/schemas/GameSettings'
          description: >-
            Additional game settings for this launch. These override any
            setting defined by the `profile`.
        variables:
          allOf:
            - $ref: '#/components/schemas/GameVariables'
          description: Optional game variables forwarded to the game client.
        externalSessionId:
          type: string
          description: >-
            Optional reference to an operator session, proxied back in
            `doTransactions` and `getBalance` calls.
    GetGameURLResponse:
      type: object
      required: [url]
      properties:
        url:
          type: string
          description: >-
            URL the player should be redirected to. Valid for a single call
            within 60 seconds.
        isNew:
          type: boolean
          description: >-
            True if the player was not known before, false if the record was
            updated. Only returned for `mode` = REAL.
        internalPlayerId:
          type: string
          maxLength: 32
          description: >-
            The internal hizi.io player ID, for reference. Only returned for
            `mode` = REAL.
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
    RegisterPlayerRequest:
      type: object
      required: [playerId]
      properties:
        playerId:
          type: string
          description: ID that identifies the player at the operator site.
        playerdetails:
          allOf:
            - $ref: '#/components/schemas/PlayerDetails'
          description: Player information to be stored.
    RegisterPlayerResponse:
      type: object
      description: >-
        Both `isNew` and `internalPlayerId` are marked mandatory in the
        documented attribute table, even though the documented sample response
        shows `isNew` alone.
      required: [isNew, internalPlayerId]
      properties:
        isNew:
          type: boolean
          description: >-
            True if the player was not known before, false if the information
            was updated.
        internalPlayerId:
          type: string
          maxLength: 32
          description: The internal hizi.io player ID, for reference.
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
    GetPlayerDetailsResponse:
      type: object
      required: [playerdetails, isBlocked]
      properties:
        playerdetails:
          $ref: '#/components/schemas/PlayerDetails'
        isBlocked:
          type: boolean
          description: >-
            True if the player is blocked in the hizi.io database by a
            `setPlayerBlock` call.
        blockReason:
          type: string
          description: >-
            Optional reason for the block, if one was passed to
            `setPlayerBlock`.
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
    SetPlayerBlockRequest:
      type: object
      required: [playerId, isBlocked]
      properties:
        playerId:
          type: string
          description: ID that identifies the player at the operator site.
        isBlocked:
          type: boolean
          description: Whether the player should be blocked (true) or unblocked (false).
        blockReason:
          type: string
          description: Optional reason why this player is blocked.
    GameRoundListResponse:
      type: object
      required: [gameRoundList]
      properties:
        gameRoundList:
          type: array
          description: >-
            Hashes of the open gamerounds. Each can be passed to
            `getGameRoundDetails`.
          items:
            $ref: '#/components/schemas/Hash'
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
    GetGameRoundDetailsResponse:
      type: object
      required: [gameRound]
      properties:
        gameRound:
          $ref: '#/components/schemas/GameRound'
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
    GetGameRoundReportResponse:
      type: object
      required: [gameRounds]
      properties:
        gameRounds:
          type: array
          description: One summary record per gameround.
          items:
            $ref: '#/components/schemas/GameRoundSummary'
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
    GetGameRoundResultResponse:
      type: object
      description: >-
        Which fields are present depends on the `getURL` request parameter:
        `resultURL` when it is true, `msg` plus `gameCode` when it is false or
        absent.
      properties:
        resultURL:
          type: string
          description: >-
            URL that displays the game result, valid for 10 minutes. Returned
            when `getURL` = true.
        msg:
          type: string
          description: >-
            HTML markup that can be used to display the game result. Returned
            when `getURL` is false or absent.
        gameCode:
          type: string
          description: >-
            Game code of the requested gameround. Returned when `getURL` is
            false or absent.
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
    ReconcileTransactionsRequest:
      type: object
      required: [gameRound]
      properties:
        gameRound:
          allOf:
            - $ref: '#/components/schemas/Hash'
          description: >-
            Hash of the gameround to replay, as returned by e.g.
            `getOpenGameRounds` or `getGameRoundReport`.
    ReconcileTransactionsResponse:
      type: object
      required: [gameRound, hashesProcessed, balance, currency, mode]
      properties:
        gameRound:
          $ref: '#/components/schemas/GameRound'
        hashesProcessed:
          type: array
          description: >-
            The transaction hashes and their processing result, as answered by
            the operator to the replayed `doTransactions` call. Also returned
            when an error occurred; hashes missing from this list are treated
            as not processed.
          items:
            $ref: '#/components/schemas/HashProcessed'
        balance:
          type: number
          description: Balance returned by the operator, in 1/100 of the base currency.
        currency:
          $ref: '#/components/schemas/Currency'
        mode:
          type: string
          description: Mode of the round; only `REAL` is supported here.
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
    VoidGameRoundRequest:
      type: object
      required: [gameRound]
      properties:
        gameRound:
          allOf:
            - $ref: '#/components/schemas/Hash'
          description: >-
            Hash of the gameround whose initial debit should be voided, as
            returned by e.g. `getOpenGameRounds` or `getGameRoundReport`.

    # --- Free plays ---------------------------------------------------------
    AssignPlayerFreeroundsRequest:
      type: object
      required: [playerId, gameCode, serial, currency, stake, count]
      properties:
        playerId:
          type: string
          description: ID that identifies the player at the operator site.
        gameCode:
          type: string
          description: Game ID the free plays should be granted for.
        serial:
          type: string
          description: >-
            Unique identifier of this package — the call is idempotent on it.
            The operator generates it; a UUID is recommended. It is sent back
            as a reference in later debit transactions that consume the free
            play.
        currency:
          $ref: '#/components/schemas/Currency'
        stake:
          allOf:
            - $ref: '#/components/schemas/Amount'
          description: In-game stake the free plays are granted for.
        count:
          type: number
          description: Number of free plays granted in this package.
        expires:
          allOf:
            - $ref: '#/components/schemas/Timestamp'
          description: >-
            Expiry date of the package. Defaults to 180 days after the call if
            absent.
    AssignPlayerFreeroundsResponse:
      type: object
      description: >-
        The documented attribute table lists only `serial` (and the optional
        `errorCode`); `internalid`, `count`, `used` and `expires` are inferred
        from the documented sample response.
      required: [serial]
      properties:
        serial:
          type: string
          description: Serial of the created package (the same one the operator passed).
        internalid:
          type: string
          description: Internal hizi.io ID of the package (from the sample response).
        count:
          type: number
          description: Number of free plays in the package (from the sample response).
        used:
          type: number
          description: >-
            Number of free plays already used from the package (from the sample
            response).
        expires:
          allOf:
            - $ref: '#/components/schemas/Timestamp'
          description: Expiry date of the package (from the sample response).
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
    RevokePlayerFreeroundsRequest:
      type: object
      required: [playerId, gameCode, serial]
      properties:
        playerId:
          type: string
          description: ID that identifies the player at the operator site.
        gameCode:
          type: string
          description: >-
            Game ID the free plays were granted for. Must be supplied so the
            correct package is addressed.
        serial:
          type: string
          description: >-
            Serial of the package. Must have been used in a previous
            `assignPlayerFreerounds` call with the same `gameCode` and
            `playerId`.
    RevokePlayerFreeroundsResponse:
      type: object
      required: [serialRevoked]
      properties:
        serialRevoked:
          type: string
          description: >-
            The revoked serial; identical to the serial passed in the request
            body.
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
    GetPlayerFreeroundsResponse:
      type: object
      required: [tickets]
      properties:
        tickets:
          type: array
          description: >-
            Available packages with serial, currency, stake and count
            information. Used or expired packages are not reported.
          items:
            $ref: '#/components/schemas/Ticket'
        errorCode:
          $ref: '#/components/schemas/ErrorCode'

    # --- Backoffice ---------------------------------------------------------
    GetGameListResponse:
      type: object
      required: [gameList, extendedGameList]
      properties:
        gameList:
          type: object
          deprecated: true
          description: >-
            Game code → game name. **Deprecated — use `extendedGameList`.**
          additionalProperties:
            type: string
        extendedGameList:
          type: object
          description: Game code → name and studio of the game.
          additionalProperties:
            $ref: '#/components/schemas/ExtendedGameListEntry'
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
    ExtendedGameListEntry:
      type: object
      description: >-
        One entry of `extendedGameList`. The properties are inferred from the
        documented sample response.
      additionalProperties: true
      properties:
        name:
          type: string
        studio:
          type: string
    GetGameConfigurationResponse:
      type: object
      required: [config]
      properties:
        config:
          $ref: '#/components/schemas/GameConfiguration'
        gameCode:
          type: string
          description: >-
            The game code the configuration belongs to. Present in the
            documented sample response, though not listed in the attribute
            table.
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
    GameConfiguration:
      type: object
      description: >-
        Configuration of a game for one currency. The documentation types this
        as free-form JSON data, so the properties below are inferred from the
        documented sample response and additional properties are possible.
      additionalProperties: true
      properties:
        stakes:
          type: array
          description: Available stakes, in 1/100 of the game currency.
          items:
            type: number
        currency:
          $ref: '#/components/schemas/Currency'
        currencyMultiplier:
          type: number
          description: >-
            Multiplier applied to base-currency values (such as `maxStake`) for
            this currency.
        expectedRtp:
          type: number
          description: Expected RTP of the game, in percent.
        oneHundredXOdds:
          type: number
        maxWinOdds:
          type: number
        maxPayout:
          type: number
        maxPayoutOdds:
          type: number
        maxExposure:
          type: number
        calculatedMaxExposure:
          type: number
        operator:
          type: string
    GetCurrencyListResponse:
      type: object
      required: [currencyList]
      properties:
        currencyList:
          type: object
          description: Currency code → multiplier.
          additionalProperties:
            type: number
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
    StatsFilter:
      type: object
      description: >-
        Date range to restrict the statistics to. Both values use the format
        `YYYY-MM-DD`.
      additionalProperties: true
      properties:
        dateFrom:
          type: string
          example: '2020-09-01'
        dateToIncluding:
          type: string
          example: '2020-09-20'
    GetStatsRequest:
      type: object
      required: [groupBy]
      properties:
        groupBy:
          type: array
          description: Properties the result should be grouped by.
          items:
            type: string
            enum: [operator, game, currency, stake, mode, environment, language]
        filterBy:
          $ref: '#/components/schemas/StatsFilter'
        showTotals:
          type: boolean
          default: false
          description: >-
            If true, totals are returned instead of records grouped by day.
        playerId:
          type: string
          description: >-
            If given, only statistics for this operator-side player ID are
            returned.
    GetStatsResponse:
      type: object
      required: [records]
      properties:
        records:
          type: array
          description: Statistical records.
          items:
            $ref: '#/components/schemas/StatsRecord'
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
    StatsRecord:
      type: object
      description: >-
        One statistics record. The documentation types these as free-form JSON
        data, so the properties below are inferred from the documented sample
        response — note that `bet` / `win` appear there as **decimal strings**
        in the local currency, not as 1/100 integers, and that the returned
        keys depend on the requested `groupBy`.
      additionalProperties: true
      properties:
        DAY:
          type: string
          description: Day the record covers, `YYYY-MM-DD`. Absent when `showTotals` is true.
        currency:
          $ref: '#/components/schemas/Currency'
        games:
          type: number
          description: Number of gamerounds.
        player:
          type: number
          description: Number of distinct players.
        bet:
          type: string
          description: Total bet in the local currency, as a decimal string.
        win:
          type: string
          description: Total win in the local currency, as a decimal string.
        betInEuro:
          type: string
          description: >-
            Total bet converted to EUR with that day's exchange rate, as a
            decimal string.
        winInEuro:
          type: string
          description: >-
            Total win converted to EUR with that day's exchange rate, as a
            decimal string.
        RTP:
          type: number
          description: Return to player, in percent.
    GetTicketStatsRequest:
      type: object
      required: [groupBy]
      properties:
        groupBy:
          type: array
          description: Properties the result should be grouped by.
          items:
            type: string
            enum: [operator, game, currency, stake]
        filterBy:
          $ref: '#/components/schemas/StatsFilter'
        showTotals:
          type: boolean
          default: false
          description: >-
            If true, totals are returned instead of records grouped by day.
        playerId:
          type: string
          description: >-
            If given, only statistics for this operator-side player ID are
            returned.
    GetTicketStatsResponse:
      type: object
      required: [records]
      properties:
        records:
          type: array
          description: >-
            Statistical records for tickets. The documentation gives no sample,
            so the record shape is free-form JSON data.
          items:
            $ref: '#/components/schemas/JsonData'
        errorCode:
          $ref: '#/components/schemas/ErrorCode'

    # --- hizi.io → Operator request/response bodies -------------------------
    BalanceResponseBase:
      type: object
      description: >-
        The common part of every operator response that reports a balance.
        `balances` is mandatory in the successful case — the schemas that build
        on this one add it to `required` where the documentation demands it.
      properties:
        balances:
          type: array
          description: >-
            One or two entries: one has to be `real`, a `bonus` balance is
            optional.
          minItems: 1
          maxItems: 2
          items:
            $ref: '#/components/schemas/PlayerBalance'
        discountInfo:
          allOf:
            - $ref: '#/components/schemas/DiscountInfo'
          description: Optional discount information; see Appendix E.
        useBonusFirst:
          type: boolean
          default: true
          description: >-
            True if bonus funds are to be used first, false if real money has
            to be used first. Informational only — it is displayed in the game
            frontend where regulation requires it. (Some documented samples
            show this as the string `"true"`.)
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
        clientMessage:
          allOf:
            - $ref: '#/components/schemas/ClientMessage'
          description: >-
            Defines a message box to be displayed in the game client. May also
            be sent when an error occurred.
    GetBalanceResponse:
      allOf:
        - $ref: '#/components/schemas/BalanceResponseBase'
        - type: object
          required: [balances]
    HashesProcessedContainer:
      type: object
      description: >-
        Carries the per-transaction processing results of a `doTransactions`
        call.
      properties:
        hashesProcessed:
          type: array
          description: >-
            The submitted transaction hashes and the result of processing each.
            **Must also be returned when an error occurred** — any hash missing
            from this list is assumed not to have been processed and is treated
            as an error.
          items:
            $ref: '#/components/schemas/HashProcessed'
    DoTransactionsRequest:
      type: object
      required: [playerId, gameCode, gameRound, transactions, transactionCount]
      properties:
        playerId:
          type: string
          description: ID that identifies the player at the operator site.
        gameCode:
          type: string
          description: >-
            ID of the game being played; the same code as in the `getGameURL`
            call.
        gameRound:
          allOf:
            - $ref: '#/components/schemas/Hash'
          description: The gameround that brackets all transactions in this call.
        transactions:
          type: array
          description: >-
            The transactions to be performed, at least one. Process them in the
            order given.
          minItems: 1
          items:
            $ref: '#/components/schemas/GameTransaction'
        transactionCount:
          type: number
          description: >-
            Number of transactions in `transactions`. If it differs from the
            array length, reject the call with HTTP 400 and error id 16.
        serialUsed:
          type: string
          description: >-
            Serial of the ticket / free-play package used for the first deposit
            of this gameround. Only set when this call carries no first-deposit
            transaction (otherwise the serial is in the transaction's `serial`
            field). Informational, to link credit and end transactions sent in
            separate calls to the serial used. Has to be activated with the
            `addSerialToTransactions` operator setting.
        ticketInformation:
          allOf:
            - $ref: '#/components/schemas/TicketInformation'
          description: >-
            Present when a package of tickets / free plays is finished in this
            gameround. Has to be activated with the `addSerialToTransactions`
            operator setting.
        externalSessionId:
          type: string
          description: >-
            Reference to the session ID passed in `getGameURL`. Only sent when
            it was originally passed to the RGS **and** the session is still
            active — a round auto-closed by the RGS janitor has no active
            session, so no `externalSessionId` is sent. **Process the call
            whether or not it is present.**
    DoTransactionsResponse:
      description: >-
        Successful `doTransactions` response. Both the balances after
        processing and the per-transaction results are mandatory.
      allOf:
        - $ref: '#/components/schemas/BalanceResponseBase'
        - $ref: '#/components/schemas/HashesProcessedContainer'
        - type: object
          required: [balances, hashesProcessed]
    DoTransactionsErrorResponse:
      description: >-
        Failed `doTransactions` response. `errorCode` becomes mandatory — it is
        used for the error message displayed in the game frontend — and
        `hashesProcessed` must still be returned so hizi.io can tell which
        transactions were applied. `balances` is not required here: the
        documented attribute table marks it mandatory, but the documented error
        samples omit it.
      allOf:
        - $ref: '#/components/schemas/BalanceResponseBase'
        - $ref: '#/components/schemas/HashesProcessedContainer'
        - type: object
          required: [errorCode, hashesProcessed]
    LogEventRequest:
      type: object
      required: [playerId, eventType]
      properties:
        playerId:
          type: string
          description: ID that identifies the player at the operator site.
        gameRound:
          allOf:
            - $ref: '#/components/schemas/Hash'
          description: Gameround the event is bound to, if any.
        eventType:
          type: string
          description: String describing the event.
          example: BONUSROUND
        eventData:
          allOf:
            - $ref: '#/components/schemas/JsonData'
          description: >-
            JSON structure describing the event; see Appendix A for the
            possible values.
        reason:
          type: string
          description: Optional reason for this call.
    DoGameRoundReportRequest:
      type: object
      required: [gameRoundDetail]
      properties:
        gameRoundDetail:
          $ref: '#/components/schemas/GameRoundReportDetail'
    DoGameRoundResultRequest:
      type: object
      required: [gameCode, msg, gameRound, playerId]
      properties:
        gameCode:
          type: string
          description: Game code of the round.
        msg:
          type: string
          format: byte
          description: >-
            BASE64-encoded HTML that can be used to display the gameround
            result, e.g.
            `Buffer.from(body.msg, 'base64').toString('utf-8')`.
        gameRound:
          allOf:
            - $ref: '#/components/schemas/Hash'
          description: ID of the gameround.
        playerId:
          type: string
          description: ID that identifies the player at the operator site.
    ReportAnimationEndRequest:
      type: object
      required: [gameRound]
      properties:
        gameRound:
          allOf:
            - $ref: '#/components/schemas/Hash'
          description: Gameround to be reported as finished.
    PingResponse:
      type: object
      properties:
        performanceData:
          allOf:
            - $ref: '#/components/schemas/JsonData'
          description: Additional data that can be used for monitoring.
        errorCode:
          $ref: '#/components/schemas/ErrorCode'
