# GENERATED by scripts/enrich.mjs from openapi.base.yaml. Edit the base file, then run `npm run build`.
openapi: 3.1.0
info:
  title: Snowball Casino Partner API
  version: 0.1.0
  summary: Transfer-wallet integration for operators.
  description: |
    Snowball Casino Partner API, **v0.1** (2026-10-06). Transfer-wallet model: you keep the player's real balance;
    to play, your server moves chips **in** to the Snowball wallet, and moves them **out** when the player is done.
    Snowball is only responsible for the integer chips inside the wallet.

    ## Conventions

    * **Money** is always an **integer number of chips** (no decimals, no currency conversion; what one chip is worth is
      your decision). `amount` must be a positive integer up to `9007199254740991`.
    * **Format**: JSON, UTF-8, `Content-Type: application/json`. Timestamps are **epoch milliseconds (UTC)**.
      Day boundaries (reports, daily limits) are **UTC+8**.
    * **Request size** at most 64 KB (batch player creation at most 200 players).
    * **Pagination** is cursor based: `?limit=1..200` (default 50) and `&cursor=<opaque>`. Responses are
      `{ "items": [...], "nextCursor": null | "..." }`, ordered by time ascending with a stable id tiebreak.
    * **Response headers**: every response carries `X-Trace-Id` (quote it when asking us for help) and rate-limit headers.
    * **IDs**: `playerId` is the Snowball id (always starts with `u_`, never changes). `externalPlayerId` is *your* player
      number (must **not** start with `u_`). Everywhere a path says `{playerId}` you may pass either; the server first
      matches `playerId` exactly, then `externalPlayerId`.
    * **Errors**: HTTP status plus `{ "error": "<code>", "message": "...", "traceId": "..." }`. Programs must only
      switch on `error`; `message` is an English hint and may change.

    ## Authentication (HMAC-SHA256)

    Every request to this API is signed. See the **Signing** section on the documentation home page for the exact
    algorithm, test vectors and Node / PHP code. Required headers: `X-Partner-Id`, `X-Key-Id`, `X-Timestamp`,
    `X-Nonce`, `X-Signature`.

    Server-side checks, in order: IP allow-list (403 `ip_not_allowed`, before the signature is looked at) → partner/key
    exists and is enabled → timestamp within 5 minutes → constant-time signature check → nonce recorded (5-minute TTL,
    a bad signature does not consume a nonce) → rate limit. Authentication failures are all answered `401` without
    saying which part failed, except `timestamp_out_of_window` and `replay`.

    ## Idempotency (transfers)

    `requestId` (1-64 chars, `[A-Za-z0-9_.:-]`) is unique **per partner, forever**.

    * Same `requestId` + same player / direction / amount → the **original result** again, `replayed: true`, HTTP 200,
      no money moves twice.
    * Same `requestId` + different parameters → `409 idempotency_conflict`, no money moves.
    * Timeout / 5xx / dropped connection: **retry with the same `requestId`**, or look it up with
      `GET /transfers/{requestId}` (`completed` or `not_found`; there is no visible "in progress" state, and the lookup is
      consistent within 60 seconds even in a crash window).
    * Business errors (`insufficient`, `seated`, ...) do **not** burn the `requestId`: once the condition is met you may
      retry with the same one. Only a successfully completed transfer locks its parameters.

    ## Rate limits

    | Scope | Default |
    |---|---|
    | Per partner, total | 50 req/s, burst 100 |
    | Transfers in / out | 20 req/s per partner; 5 req/s per player |
    | History / reports | 10 req/s per partner, `limit` at most 200 |
    | Launch exchange (browser) | 20 per minute per IP |

    Over the limit: `429 rate_limited` with `Retry-After` (seconds). Headers: `X-RateLimit-Limit`,
    `X-RateLimit-Remaining`, `X-RateLimit-Reset` (epoch seconds).

    ## Browser-side endpoint (not part of the 23, unsigned)

    `POST https://play.snowballcsn.app/api/launch/exchange` with `{ "launchToken": "lt_..." }` turns a launch token into a
    normal player session (`{ token, expiresAt, uid, partnerId }`). The Snowball web app calls it for you when the browser
    lands on the `launchUrl`; you normally never call it yourself.
  contact:
    name: Snowball Casino partner integration
  license:
    name: Proprietary
    identifier: LicenseRef-Proprietary
servers:
  - url: https://partner-api-sandbox.snowballcsn.app/v1
    description: Sandbox (fake chips, separate tenant, separate keys)
  - url: https://partner-api.snowballcsn.app/v1
    description: Production
security:
  - SnowballHmac: []
tags:
  - name: System
    description: Connectivity and clock check.
  - name: Players
    description: Create, find and suspend players. Explicit creation is optional; `POST /launch` with an `externalPlayerId` creates the player on first use.
  - name: Launch
    description: One-time launch tokens that put a player's browser straight into the game.
  - name: Wallet
    description: Move chips in and out, read the balance and look up transfers.
  - name: History
    description: Wallet ledger and per-round bets and net results.
  - name: Reports
    description: Reconciliation totals, realized RTP and rake.
  - name: Settings
    description: Operator-controlled limits, bot mode and Texas table types.
  - name: Webhook
    description: Optional signed event delivery from Snowball to you.
  - name: Sandbox
    description: Only exists on the sandbox host.
paths:
  /ping:
    get:
      x-endpoint-no: 1
      operationId: ping
      tags:
        - System
      summary: Check signature and clock
      description: |
        Verifies that your signature, key and IP allow-list work and returns the server clock.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/ping' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273601000' \
          -H 'X-Nonce: n01a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 51956529db7782e6d9eb83466cfc4a203bf81f6f4403c13f8ae105dba6bfd7a8'
        ```
      responses:
        "200":
          description: Signature accepted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PingResponse"
              example:
                ok: true
                serverTime: 1791273600123
                partnerId: 12
                env: sandbox
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273601000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n01a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 51956529db7782e6d9eb83466cfc4a203bf81f6f4403c13f8ae105dba6bfd7a8
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/ping' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273601000' \
              -H 'X-Nonce: n01a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 51956529db7782e6d9eb83466cfc4a203bf81f6f4403c13f8ae105dba6bfd7a8'
  /players:
    post:
      x-endpoint-no: 2
      operationId: createPlayer
      tags:
        - Players
      summary: Create a player (optional)
      description: |
        Pre-registers a player. You do not need this: `POST /launch` with an `externalPlayerId` creates the player on
        first use.

        * `externalPlayerId` is unique inside your partner account and is the idempotency key: creating the same one again
          returns the existing player with `created: false` (`requestId` is only logged).
        * `username` omitted or `null` → generated as your partner prefix plus a 4-6 digit sequence (for example `ab0001`).
          A custom `username` must match `^[a-z0-9_]{3,24}$`, must carry your partner prefix (enforced) and must be
          unique site-wide (`409 username_taken`).
        * `displayName` (at most 16 chars) is shown at the table; defaults to the username. `lang` is stored only.
        * **No password is issued.** Partner players cannot use the normal login (`403 partner_login_only`); they only
          enter through launch tokens.
        * Daily bonus is **off** by default for partner players (you can turn it on in `PUT /limits`).

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/players' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273603000' \
          -H 'X-Nonce: n03a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: e7b47520385f39612fedeca8a3243d0c3170ab5fe6f4208f51b47bc30dd4ee7c' \
          -H 'Content-Type: application/json' \
          --data-raw '{"requestId":"reg-88421","externalPlayerId":"ops-user-88421","username":null,"displayName":"Alex","lang":"en","validDays":null}'
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePlayerRequest"
            example:
              requestId: reg-88421
              externalPlayerId: ops-user-88421
              username: null
              displayName: Alex
              lang: en
              validDays: null
      responses:
        "201":
          description: Player created (or already existing, see `created`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreatePlayerResponse"
              example:
                created: true
                playerId: u_9f3c1a2b7d4e5f6a8b90
                username: ab0001
                externalPlayerId: ops-user-88421
                status: active
                balance: 0
                createdAt: 1791273600000
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          description: |
            `insufficient`, `seated`, `idempotency_conflict`, `username_taken`, `player_suspended`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                username_taken:
                  summary: Username already used
                  value:
                    error: username_taken
                    message: username is already taken
                    traceId: tr_8c1e0f7a52
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "415":
          $ref: "#/components/responses/UnsupportedMediaType"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273603000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n03a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: e7b47520385f39612fedeca8a3243d0c3170ab5fe6f4208f51b47bc30dd4ee7c
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/players' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273603000' \
              -H 'X-Nonce: n03a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: e7b47520385f39612fedeca8a3243d0c3170ab5fe6f4208f51b47bc30dd4ee7c' \
              -H 'Content-Type: application/json' \
              --data-raw '{"requestId":"reg-88421","externalPlayerId":"ops-user-88421","username":null,"displayName":"Alex","lang":"en","validDays":null}'
    get:
      x-endpoint-no: 5
      operationId: listPlayers
      tags:
        - Players
      summary: List and search players
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273602000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n02a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 85993b95bf001154ad7d49f44e6eb689e97632df83a0f4713eda8c93a77a0cae
        - name: q
          in: query
          description: Substring match on username / display name.
          schema:
            type: string
            maxLength: 64
        - name: externalPlayerId
          in: query
          description: Exact match.
          schema:
            type: string
        - name: status
          in: query
          schema:
            $ref: "#/components/schemas/PlayerStatus"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Cursor"
      responses:
        "200":
          description: A page of players.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlayerPage"
              example:
                items:
                  - playerId: u_9f3c1a2b7d4e5f6a8b90
                    username: ab0001
                    externalPlayerId: ops-user-88421
                    displayName: Alex
                    status: active
                    balance: 1500000
                    seated: true
                    currentTable: newbie-show-6
                    lastLoginAt: 1791273500000
                    createdAt: 1791273000000
                nextCursor: null
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      description: |
        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/players?limit=50' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273602000' \
          -H 'X-Nonce: n02a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 85993b95bf001154ad7d49f44e6eb689e97632df83a0f4713eda8c93a77a0cae'
        ```
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/players?limit=50' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273602000' \
              -H 'X-Nonce: n02a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 85993b95bf001154ad7d49f44e6eb689e97632df83a0f4713eda8c93a77a0cae'
  /players/batch:
    post:
      x-endpoint-no: 3
      operationId: createPlayersBatch
      tags:
        - Players
      summary: Create up to 200 players
      description: |
        Send either `count` (auto-numbered usernames, optional `prefix`) or an explicit `players` list. Re-sending the same
        `batchId` returns the same result without creating anything new.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/players/batch' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273604000' \
          -H 'X-Nonce: n04a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 255db1d972344c9c08707a515bd48cd92917ee9da033bf5b90f3b273aa6f8b34' \
          -H 'Content-Type: application/json' \
          --data-raw '{"batchId":"batch-2026-10-06-a","players":[{"externalPlayerId":"ops-user-1001","displayName":"Mia"},{"externalPlayerId":"ops-user-1002"}]}'
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchRequest"
            examples:
              explicit:
                summary: Explicit list
                value:
                  batchId: batch-2026-10-06-a
                  players:
                    - externalPlayerId: ops-user-1001
                      displayName: Mia
                    - externalPlayerId: ops-user-1002
              counted:
                summary: Auto-numbered
                value:
                  batchId: batch-2026-10-06-b
                  count: 50
                  validDays: null
      responses:
        "200":
          description: The players of the batch (same body on replay).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchResponse"
              example:
                items:
                  - playerId: u_11aa22bb33cc44dd55ee
                    username: ab0002
                    externalPlayerId: ops-user-1001
                  - playerId: u_66ff77aa88bb99cc00dd
                    username: ab0003
                    externalPlayerId: ops-user-1002
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          description: |
            `insufficient`, `seated`, `idempotency_conflict`, `username_taken`, `player_suspended`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                idempotency_conflict:
                  summary: batchId reused with different content
                  value:
                    error: idempotency_conflict
                    message: batchId was already used with different parameters
                    traceId: tr_8c1e0f7a52
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "415":
          $ref: "#/components/responses/UnsupportedMediaType"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273604000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n04a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 255db1d972344c9c08707a515bd48cd92917ee9da033bf5b90f3b273aa6f8b34
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/players/batch' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273604000' \
              -H 'X-Nonce: n04a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 255db1d972344c9c08707a515bd48cd92917ee9da033bf5b90f3b273aa6f8b34' \
              -H 'Content-Type: application/json' \
              --data-raw '{"batchId":"batch-2026-10-06-a","players":[{"externalPlayerId":"ops-user-1001","displayName":"Mia"},{"externalPlayerId":"ops-user-1002"}]}'
  /players/{playerId}:
    get:
      x-endpoint-no: 4
      operationId: getPlayer
      tags:
        - Players
      summary: Player detail
      description: |
        Status, balance, whether the player is seated, and last login.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/players/u_9f3c1a2b7d4e5f6a8b90' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273605000' \
          -H 'X-Nonce: n05a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: d6b2af02790276126df194a282dbec064bbb199013f9b32ea0b1237b1e4443a4'
        ```
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273605000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n05a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: d6b2af02790276126df194a282dbec064bbb199013f9b32ea0b1237b1e4443a4
        - $ref: "#/components/parameters/PlayerRef"
      responses:
        "200":
          description: The player.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Player"
              example:
                playerId: u_9f3c1a2b7d4e5f6a8b90
                username: ab0001
                externalPlayerId: ops-user-88421
                displayName: Alex
                status: active
                balance: 1500000
                seated: true
                currentTable: newbie-show-6
                lastLoginAt: 1791273500000
                createdAt: 1791273000000
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/players/u_9f3c1a2b7d4e5f6a8b90' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273605000' \
              -H 'X-Nonce: n05a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: d6b2af02790276126df194a282dbec064bbb199013f9b32ea0b1237b1e4443a4'
  /players/{playerId}/status:
    post:
      x-endpoint-no: 6
      operationId: setPlayerStatus
      tags:
        - Players
      summary: Suspend or restore a player
      description: |
        Suspending also kicks the player off the table and closes the session. Naturally idempotent.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/players/u_9f3c1a2b7d4e5f6a8b90/status' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273606000' \
          -H 'X-Nonce: n06a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 55cbc4365c60c34c0932efc8a13a79494cacb3821ce6f55fcd5e6ab4547fdfe0' \
          -H 'Content-Type: application/json' \
          --data-raw '{"status":"suspended"}'
        ```
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273606000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n06a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 55cbc4365c60c34c0932efc8a13a79494cacb3821ce6f55fcd5e6ab4547fdfe0
        - $ref: "#/components/parameters/PlayerRef"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - status
              properties:
                status:
                  $ref: "#/components/schemas/PlayerStatus"
            example:
              status: suspended
      responses:
        "200":
          description: New status.
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                  - playerId
                  - status
                properties:
                  ok:
                    type: boolean
                  playerId:
                    type: string
                  status:
                    $ref: "#/components/schemas/PlayerStatus"
              example:
                ok: true
                playerId: u_9f3c1a2b7d4e5f6a8b90
                status: suspended
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/players/u_9f3c1a2b7d4e5f6a8b90/status' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273606000' \
              -H 'X-Nonce: n06a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 55cbc4365c60c34c0932efc8a13a79494cacb3821ce6f55fcd5e6ab4547fdfe0' \
              -H 'Content-Type: application/json' \
              --data-raw '{"status":"suspended"}'
  /players/{playerId}/kick:
    post:
      x-endpoint-no: 8
      operationId: kickPlayer
      tags:
        - Players
      summary: Force the player off the table
      description: |
        Immediately disconnects the player and stands them up. Chips that were on the table are returned to the wallet
        (shortly afterwards), after which they can be transferred out. Naturally idempotent. Use this when
        `transfer-out` answered `409 seated`.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/players/u_9f3c1a2b7d4e5f6a8b90/kick' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273607000' \
          -H 'X-Nonce: n07a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 72cf7ab9bc93fe5c45fa618c5aceb0e0c05627216bd11af9603d1d5afaaa8228'
        ```
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273607000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n07a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 72cf7ab9bc93fe5c45fa618c5aceb0e0c05627216bd11af9603d1d5afaaa8228
        - $ref: "#/components/parameters/PlayerRef"
      responses:
        "200":
          description: Kicked (also returned when the player was not connected).
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                  - playerId
                properties:
                  ok:
                    type: boolean
                  playerId:
                    type: string
                  wasSeated:
                    type: boolean
              example:
                ok: true
                playerId: u_9f3c1a2b7d4e5f6a8b90
                wasSeated: true
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/players/u_9f3c1a2b7d4e5f6a8b90/kick' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273607000' \
              -H 'X-Nonce: n07a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 72cf7ab9bc93fe5c45fa618c5aceb0e0c05627216bd11af9603d1d5afaaa8228'
  /launch:
    post:
      x-endpoint-no: 7
      operationId: launch
      tags:
        - Launch
      summary: Create a launch token and URL
      description: |
        Your backend calls this, then redirects the player's browser to `launchUrl`. Pass **either** `externalPlayerId` or
        `playerId`. With an `externalPlayerId` that does not exist yet, the player is **created automatically** (upsert;
        `displayName` and `lang` apply, rules as in `POST /players`); the same `externalPlayerId` always maps to the same
        player. No password is issued.

        Optional `game` (`texas` or `baccarat`): with it, the launch URL goes straight to table selection of that game;
        without it the player lands in the Snowball lobby. `tableId` jumps to a specific table.

        Token rules: **single use**, default 60 seconds (`ttlSeconds` 10-300), stored only as a SHA-256 hash. Reuse or
        expiry → `401 launch_token_invalid`. A suspended player → `403 banned`. A new session kicks an older one
        (`kicked: replaced`). Partner sessions last 12 hours without sliding (`sessionHours` in `PUT /limits`, 1-24).

        `returnUrl` must be on your allow-listed domains (else `400 bad_return_url`): the game then shows a "back" button
        and logout redirects there. The web app may be embedded in an iframe only from your allow-listed domains; a new
        window or full-page redirect is recommended.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/launch' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273608000' \
          -H 'X-Nonce: n08a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 12fb91fe97d76f39eb52683eb3dfe1c265050faeb2a90887381dc0b6a99a87c6' \
          -H 'Content-Type: application/json' \
          --data-raw '{"externalPlayerId":"ops-user-88421","displayName":"Alex","game":"baccarat","tableId":null,"lang":"en","returnUrl":"https://ops.example.com/lobby","ttlSeconds":60}'
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LaunchRequest"
            example:
              externalPlayerId: ops-user-88421
              displayName: Alex
              game: baccarat
              tableId: null
              lang: en
              returnUrl: https://ops.example.com/lobby
              ttlSeconds: 60
      responses:
        "200":
          description: Token and ready-to-use URL.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LaunchResponse"
              example:
                playerId: u_9f3c1a2b7d4e5f6a8b90
                created: true
                launchToken: lt_6b1f0c4e2a9d83f7b5c61e0a4d92f3b8c7e15a60d4f2b9e83c7a1d05f6e48b29
                launchUrl: https://play.snowballcsn.app/?launch=lt_6b1f0c4e2a9d83f7b5c61e0a4d92f3b8c7e15a60d4f2b9e83c7a1d05f6e48b29&game=baccarat&lang=en&ret=https%3A%2F%2Fops.example.com%2Flobby
                expiresAt: 1791273660000
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "415":
          $ref: "#/components/responses/UnsupportedMediaType"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273608000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n08a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 12fb91fe97d76f39eb52683eb3dfe1c265050faeb2a90887381dc0b6a99a87c6
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/launch' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273608000' \
              -H 'X-Nonce: n08a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 12fb91fe97d76f39eb52683eb3dfe1c265050faeb2a90887381dc0b6a99a87c6' \
              -H 'Content-Type: application/json' \
              --data-raw '{"externalPlayerId":"ops-user-88421","displayName":"Alex","game":"baccarat","tableId":null,"lang":"en","returnUrl":"https://ops.example.com/lobby","ttlSeconds":60}'
  /players/{playerId}/balance:
    get:
      x-endpoint-no: 9
      operationId: getBalance
      tags:
        - Wallet
      summary: Wallet balance
      description: |
        `balance` is the wallet only; chips sitting on a table are not part of it. `seated` is also `true` while a
        baccarat player has unsettled bets.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/players/u_9f3c1a2b7d4e5f6a8b90/balance' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273609000' \
          -H 'X-Nonce: n09a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 6dfa8dc6bbc738eb2e59f38fdb928a3c32590505abf9176d1d0b689a08a975aa'
        ```
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273609000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n09a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 6dfa8dc6bbc738eb2e59f38fdb928a3c32590505abf9176d1d0b689a08a975aa
        - $ref: "#/components/parameters/PlayerRef"
      responses:
        "200":
          description: Balance.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Balance"
              example:
                playerId: u_9f3c1a2b7d4e5f6a8b90
                balance: 1500000
                seated: true
                currentTable: newbie-show-6
                asOf: 1791273600123
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/players/u_9f3c1a2b7d4e5f6a8b90/balance' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273609000' \
              -H 'X-Nonce: n09a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 6dfa8dc6bbc738eb2e59f38fdb928a3c32590505abf9176d1d0b689a08a975aa'
  /players/{playerId}/transfer-in:
    post:
      x-endpoint-no: 10
      operationId: transferIn
      tags:
        - Wallet
      summary: Transfer chips in (you → Snowball)
      description: |
        Credits the player's wallet. Allowed while the player is seated (the chips are available for the next buy-in / bet).
        Idempotent by `requestId`, see the idempotency section in the API description. The transaction id is
        deterministic: `ptr:{partnerId}:{requestId}`.

        `ref` (optional, at most 64 chars) is stored verbatim in the ledger for your reconciliation.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/players/u_9f3c1a2b7d4e5f6a8b90/transfer-in' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273610000' \
          -H 'X-Nonce: n10a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 04e1c8d4d9a3673f4d78851a67ff6e6cc20be1675bd8ddc04a700eb76e670418' \
          -H 'Content-Type: application/json' \
          --data-raw '{"requestId":"dep-20261006-0001","amount":500000,"ref":"ops-order-77"}'
        ```
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273610000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n10a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 04e1c8d4d9a3673f4d78851a67ff6e6cc20be1675bd8ddc04a700eb76e670418
        - $ref: "#/components/parameters/PlayerRef"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TransferRequest"
            example:
              requestId: dep-20261006-0001
              amount: 500000
              ref: ops-order-77
      responses:
        "200":
          description: Transfer completed (or replayed).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransferResponse"
              example:
                ok: true
                replayed: false
                transferId: tin_2c9e51f7a0b3
                txId: ptr:12:dep-20261006-0001
                requestId: dep-20261006-0001
                type: transfer_in
                amount: 500000
                balance: 1500000
                ts: 1791273600000
        "400":
          description: |
            Invalid request. Codes: `bad_request`, `bad_amount`, `amount_out_of_range`, `bad_request_id`, `bad_username`,
            `bad_range`, `bad_limits`, `bad_return_url`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_amount:
                  summary: Not a positive integer
                  value:
                    error: bad_amount
                    message: amount must be a positive integer
                    traceId: tr_8c1e0f7a52
                amount_out_of_range:
                  summary: Outside minTransfer/maxTransfer
                  value:
                    error: amount_out_of_range
                    message: amount is outside the allowed transfer range
                    traceId: tr_8c1e0f7a52
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          description: |
            `insufficient`, `seated`, `idempotency_conflict`, `username_taken`, `player_suspended`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                idempotency_conflict:
                  summary: Same requestId, different parameters
                  value:
                    error: idempotency_conflict
                    message: requestId was already used with different parameters
                    traceId: tr_8c1e0f7a52
                player_suspended:
                  summary: Player is suspended
                  value:
                    error: player_suspended
                    message: player is suspended; transfers in are not accepted
                    traceId: tr_8c1e0f7a52
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "415":
          $ref: "#/components/responses/UnsupportedMediaType"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
        "503":
          $ref: "#/components/responses/Maintenance"
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/players/u_9f3c1a2b7d4e5f6a8b90/transfer-in' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273610000' \
              -H 'X-Nonce: n10a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 04e1c8d4d9a3673f4d78851a67ff6e6cc20be1675bd8ddc04a700eb76e670418' \
              -H 'Content-Type: application/json' \
              --data-raw '{"requestId":"dep-20261006-0001","amount":500000,"ref":"ops-order-77"}'
  /players/{playerId}/transfer-out:
    post:
      x-endpoint-no: 11
      operationId: transferOut
      tags:
        - Wallet
      summary: Transfer chips out (Snowball → you)
      description: |
        Debits the wallet. Send `amount`, or `all: true` to take the entire balance (the response `amount` is the actual
        figure; a zero balance is `409 insufficient`).

        * `409 insufficient`: `amount` is more than the wallet balance (the balance never goes negative).
        * `409 seated`: the player is seated at a Texas table or has an unfinished buy-in, **or has unsettled baccarat
          bets**. The body carries `retryable: true` and `currentTable`. Either call `POST /players/{playerId}/kick` and
          retry, or let the player leave the seat in game. **Chips on a table are not wallet balance and cannot be
          transferred out.**
        * The seated check and the debit are one atomic step, so a bet or buy-in racing with the transfer sees the
          balance after the debit.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/players/u_9f3c1a2b7d4e5f6a8b90/transfer-out' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273611000' \
          -H 'X-Nonce: n11a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 74ab9a6c83c9d7b4c31d5b01a1478de42ef3214625b3a31c45c8b5b4928c0a22' \
          -H 'Content-Type: application/json' \
          --data-raw '{"requestId":"wd-20261006-0042","amount":200000,"ref":"ops-order-78"}'
        ```
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273611000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n11a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 74ab9a6c83c9d7b4c31d5b01a1478de42ef3214625b3a31c45c8b5b4928c0a22
        - $ref: "#/components/parameters/PlayerRef"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TransferOutRequest"
            examples:
              amount:
                value:
                  requestId: wd-20261006-0042
                  amount: 200000
                  ref: ops-order-78
              all:
                value:
                  requestId: wd-20261006-0043
                  all: true
      responses:
        "200":
          description: Transfer completed (or replayed).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransferResponse"
              example:
                ok: true
                replayed: false
                transferId: tout_7d41b9e20c55
                txId: ptr:12:wd-20261006-0042
                requestId: wd-20261006-0042
                type: transfer_out
                amount: 200000
                balance: 1300000
                ts: 1791273700000
        "400":
          description: |
            Invalid request. Codes: `bad_request`, `bad_amount`, `amount_out_of_range`, `bad_request_id`, `bad_username`,
            `bad_range`, `bad_limits`, `bad_return_url`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_amount:
                  summary: Not a positive integer
                  value:
                    error: bad_amount
                    message: amount must be a positive integer
                    traceId: tr_8c1e0f7a52
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          description: |
            `insufficient` (amount above balance, or `all` on a zero balance), `seated` (seated, unfinished buy-in, or
            unsettled baccarat bets; `retryable: true`), `idempotency_conflict`, `player_suspended`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                insufficient:
                  summary: More than the wallet balance
                  value:
                    error: insufficient
                    message: amount exceeds wallet balance
                    traceId: tr_8c1e0f7a52
                seated:
                  summary: Seated or unsettled bets
                  value:
                    error: seated
                    message: player is seated or has unsettled bets
                    traceId: tr_8c1e0f7a52
                    retryable: true
                    currentTable: newbie-show-6
                idempotency_conflict:
                  summary: Same requestId, different parameters
                  value:
                    error: idempotency_conflict
                    message: requestId was already used with different parameters
                    traceId: tr_8c1e0f7a52
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "415":
          $ref: "#/components/responses/UnsupportedMediaType"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
        "503":
          $ref: "#/components/responses/Maintenance"
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/players/u_9f3c1a2b7d4e5f6a8b90/transfer-out' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273611000' \
              -H 'X-Nonce: n11a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 74ab9a6c83c9d7b4c31d5b01a1478de42ef3214625b3a31c45c8b5b4928c0a22' \
              -H 'Content-Type: application/json' \
              --data-raw '{"requestId":"wd-20261006-0042","amount":200000,"ref":"ops-order-78"}'
  /transfers/{requestId}:
    get:
      x-endpoint-no: 12
      operationId: getTransfer
      tags:
        - Wallet
      summary: Look up a transfer by requestId
      description: |
        For reconciliation after a timeout. There is no in-progress state: the transfer either completed or does not exist.
        `not_found` is returned with HTTP 200 and means no transfer with that `requestId` exists for your partner account.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/transfers/dep-20261006-0001' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273612000' \
          -H 'X-Nonce: n12a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 0cf2add8a2a2529be7c6778132acaed9697247797c8ea378701a1d1ec2bc2b63'
        ```
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273612000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n12a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 0cf2add8a2a2529be7c6778132acaed9697247797c8ea378701a1d1ec2bc2b63
        - name: requestId
          in: path
          required: true
          schema:
            $ref: "#/components/schemas/RequestId"
      responses:
        "200":
          description: Lookup result.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransferLookup"
              examples:
                completed:
                  value:
                    status: completed
                    transfer:
                      transferId: tin_2c9e51f7a0b3
                      txId: ptr:12:dep-20261006-0001
                      requestId: dep-20261006-0001
                      playerId: u_9f3c1a2b7d4e5f6a8b90
                      type: transfer_in
                      amount: 500000
                      balance: 1500000
                      ref: ops-order-77
                      ts: 1791273600000
                not_found:
                  value:
                    status: not_found
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/transfers/dep-20261006-0001' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273612000' \
              -H 'X-Nonce: n12a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 0cf2add8a2a2529be7c6778132acaed9697247797c8ea378701a1d1ec2bc2b63'
  /players/{playerId}/transactions:
    get:
      x-endpoint-no: 13
      operationId: listPlayerTransactions
      tags:
        - History
      summary: Player wallet ledger
      description: |
        `from` / `to` are epoch ms, half-open `[from, to)`. Default: last 30 days; maximum span 92 days (else
        `400 bad_range`). `amount` is signed from the player's point of view (credits positive, debits negative);
        `balance` is the wallet balance **after** that row and never negative. Texas hand results are not wallet rows (chips
        on the table); see `/rounds`.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/players/u_9f3c1a2b7d4e5f6a8b90/transactions?from=1791187200000&limit=50&to=1791273600000&type=transfer_in%2Ctransfer_out' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273613000' \
          -H 'X-Nonce: n13a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: ef7a899ce4e4364464e226c2ac0e79f158491bcc13a539c31c19fc5175c1029a'
        ```
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273613000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n13a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: ef7a899ce4e4364464e226c2ac0e79f158491bcc13a539c31c19fc5175c1029a
        - $ref: "#/components/parameters/PlayerRef"
        - $ref: "#/components/parameters/From"
        - $ref: "#/components/parameters/To"
        - $ref: "#/components/parameters/TxType"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Cursor"
      responses:
        "200":
          description: A page of ledger rows.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionPage"
              example:
                items:
                  - txId: ptr:12:dep-20261006-0001
                    type: transfer_in
                    amount: 500000
                    balance: 1500000
                    ref: ops-order-77
                    game: null
                    ts: 1791273600000
                  - txId: buyin:8841
                    type: buy_in
                    amount: -300000
                    balance: 1200000
                    game: texas
                    ref: newbie-show-6
                    ts: 1791273700000
                  - txId: bacbet:4412-1
                    type: bet
                    amount: -50000
                    balance: 1150000
                    game: baccarat
                    ref: bac-newbie-1#4412
                    ts: 1791273800000
                  - txId: bacpay:4412-1
                    type: payout
                    amount: 100000
                    balance: 1250000
                    game: baccarat
                    ref: bac-newbie-1#4412
                    ts: 1791273820000
                nextCursor: null
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/players/u_9f3c1a2b7d4e5f6a8b90/transactions?from=1791187200000&limit=50&to=1791273600000&type=transfer_in%2Ctransfer_out' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273613000' \
              -H 'X-Nonce: n13a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: ef7a899ce4e4364464e226c2ac0e79f158491bcc13a539c31c19fc5175c1029a'
  /players/{playerId}/rounds:
    get:
      x-endpoint-no: 14
      operationId: listPlayerRounds
      tags:
        - History
      summary: Bets and net result per round / hand
      description: |
        One row per round, with the **net** (won minus lost) of each.

        * **Baccarat**: `bets[]`, `staked`, `returned`, `net = returned - staked`, `result`.
        * **Texas**: `net` is after rake. `staked` / `returned` are `null` (player-versus-player, not meaningful). Hole cards
          and action detail are **not** exposed through this API.
        * `capped: true` means the round hit the win cap from `PUT /limits` and the payout was truncated.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/players/u_9f3c1a2b7d4e5f6a8b90/rounds?from=1791187200000&limit=50&to=1791273600000' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273614000' \
          -H 'X-Nonce: n14a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: e2c6bca059fbd5ee44e15c0cb6e596c1707899c39d588fe0e64c07a4e8abd975'
        ```
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273614000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n14a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: e2c6bca059fbd5ee44e15c0cb6e596c1707899c39d588fe0e64c07a4e8abd975
        - $ref: "#/components/parameters/PlayerRef"
        - $ref: "#/components/parameters/From"
        - $ref: "#/components/parameters/To"
        - name: game
          in: query
          schema:
            $ref: "#/components/schemas/Game"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Cursor"
      responses:
        "200":
          description: A page of rounds.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RoundPage"
              example:
                items:
                  - game: baccarat
                    roundId: bac-newbie-1#4412
                    tableId: bac-newbie-1
                    tier: newbie
                    ts: 1791273820000
                    bets:
                      - type: player
                        amount: 50000
                      - type: playerPair
                        amount: 5000
                    staked: 55000
                    returned: 105000
                    net: 50000
                    capped: false
                    result:
                      winner: player
                      playerPoints: 8
                      bankerPoints: 5
                  - game: texas
                    roundId: newbie-wait-1-381
                    tableId: newbie-wait-1
                    tier: newbie
                    ts: 1791273900000
                    staked: null
                    returned: null
                    net: -12000
                    rake: 0
                    capped: false
                    result:
                      showdown: true
                nextCursor: null
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/players/u_9f3c1a2b7d4e5f6a8b90/rounds?from=1791187200000&limit=50&to=1791273600000' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273614000' \
              -H 'X-Nonce: n14a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: e2c6bca059fbd5ee44e15c0cb6e596c1707899c39d588fe0e64c07a4e8abd975'
  /transactions:
    get:
      x-endpoint-no: 15
      operationId: listTransactions
      tags:
        - History
      summary: Partner-wide ledger (reconciliation)
      description: |
        Same row shape as the per-player ledger, plus `playerId` and `username`. For daily reconciliation pull only
        `type=transfer_in,transfer_out`.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/transactions?from=1791187200000&limit=50&to=1791273600000&type=transfer_in%2Ctransfer_out' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273615000' \
          -H 'X-Nonce: n15a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 0a478e4994c56cfb2bfd9a5b7efc85884337c4d417413a148c033f105ae7bdda'
        ```
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273615000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n15a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 0a478e4994c56cfb2bfd9a5b7efc85884337c4d417413a148c033f105ae7bdda
        - $ref: "#/components/parameters/From"
        - $ref: "#/components/parameters/To"
        - $ref: "#/components/parameters/TxType"
        - name: playerId
          in: query
          description: Restrict to one player (`playerId` or `externalPlayerId`).
          schema:
            type: string
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Cursor"
      responses:
        "200":
          description: A page of ledger rows.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerTransactionPage"
              example:
                items:
                  - txId: ptr:12:dep-20261006-0001
                    type: transfer_in
                    amount: 500000
                    balance: 1500000
                    ref: ops-order-77
                    game: null
                    ts: 1791273600000
                    playerId: u_9f3c1a2b7d4e5f6a8b90
                    username: ab0001
                nextCursor: null
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/transactions?from=1791187200000&limit=50&to=1791273600000&type=transfer_in%2Ctransfer_out' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273615000' \
              -H 'X-Nonce: n15a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 0a478e4994c56cfb2bfd9a5b7efc85884337c4d417413a148c033f105ae7bdda'
  /reports/summary:
    get:
      x-endpoint-no: 16
      operationId: reportSummary
      tags:
        - Reports
      summary: Daily transfer, turnover and win summary
      description: |
        Per UTC+8 day: transfers in / out, turnover, player win, rake and active players, plus totals.

        Identity for your own checks: `Σ(transferIn − transferOut) = Δ(player wallet total) + Δ(chips at tables)`. The
        response includes `walletTotal` and `atTables` so you can verify it.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/reports/summary?from=1791187200000&to=1791273600000' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273616000' \
          -H 'X-Nonce: n16a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 01ba580a5b712a668a010867c4580c03394c0f4f7ed94a30cf68f1d03035ba40'
        ```
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273616000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n16a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 01ba580a5b712a668a010867c4580c03394c0f4f7ed94a30cf68f1d03035ba40
        - $ref: "#/components/parameters/From"
        - $ref: "#/components/parameters/To"
        - name: groupBy
          in: query
          schema:
            type: string
            enum:
              - day
            default: day
      responses:
        "200":
          description: Summary rows.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SummaryReport"
              example:
                items:
                  - day: 2026-10-06
                    transferIn: 5000000
                    transferOut: 1200000
                    net: 3800000
                    turnover: 8200000
                    playerWin: -150000
                    rake: 18000
                    activePlayers: 42
                totals:
                  transferIn: 5000000
                  transferOut: 1200000
                  net: 3800000
                  turnover: 8200000
                  playerWin: -150000
                  rake: 18000
                walletTotal: 2600000
                atTables: 1200000
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/reports/summary?from=1791187200000&to=1791273600000' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273616000' \
              -H 'X-Nonce: n16a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 01ba580a5b712a668a010867c4580c03394c0f4f7ed94a30cf68f1d03035ba40'
  /reports/rtp:
    get:
      x-endpoint-no: 23
      operationId: reportRtp
      tags:
        - Reports
      summary: Realized return and rake
      description: |
        By day, game and Texas table type.

        * **Baccarat**: `realizedRTP = returned / staked` over real-player bets, with `n` and `lowSample: true` below 5,000
          rounds. This is a **measured outcome** and swings heavily on small samples. Rule-defined theoretical values:
          banker 98.94%, player 98.76%, tie 85.64%.
        * **Texas** has no RTP (player versus player): the row gives `humanNet`, `botNet` and `rake`, and
          `botNet + humanNet + rake = 0`. On `botsOnly` tables `humanNet` is the players' net result against the bots.
        * `mode` is the bot mode in force for that hand; `day: "n/a"` marks hands from before mode tracking.
        * Realized figures are results, **not settings and not promises**.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/reports/rtp?from=1791187200000&to=1791273600000' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273617000' \
          -H 'X-Nonce: n17a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 88eb8ea1c64b7feceff8aaadcf40c878c996027cf92259294b07489c110b7fdd'
        ```
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273617000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n17a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 88eb8ea1c64b7feceff8aaadcf40c878c996027cf92259294b07489c110b7fdd
        - $ref: "#/components/parameters/From"
        - $ref: "#/components/parameters/To"
        - name: groupBy
          in: query
          schema:
            type: string
            enum:
              - day
            default: day
        - name: tableType
          in: query
          schema:
            type: string
            enum:
              - botsOnly
              - mixed
              - all
            default: all
        - name: game
          in: query
          schema:
            $ref: "#/components/schemas/Game"
      responses:
        "200":
          description: Report rows.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RtpReport"
              example:
                items:
                  - day: 2026-10-06
                    game: texas
                    tableType: botsOnly
                    mode: normal
                    hands: 1840
                    humanStaked: null
                    humanNet: -61200
                    botNet: 61200
                    rake: 0
                    realizedReturn: null
                  - day: 2026-10-06
                    game: baccarat
                    tableType: null
                    hands: 912
                    staked: 41230000
                    returned: 40695000
                    realizedRTP: 0.987
                    rake: 0
                    n: 912
                    lowSample: true
                totals:
                  hands: 2752
                  rake: 0
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/reports/rtp?from=1791187200000&to=1791273600000' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273617000' \
              -H 'X-Nonce: n17a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 88eb8ea1c64b7feceff8aaadcf40c878c996027cf92259294b07489c110b7fdd'
  /limits:
    get:
      x-endpoint-no: 17
      operationId: getLimits
      tags:
        - Settings
      summary: Read operator limits
      description: |
        Every field comes back as `{ value, platformMin, platformMax }`; values you write must lie inside the platform
        bounds. Changes are audit-logged and take effect from the **next round / next bet**; bets already placed are not
        affected.

        | Setting | Meaning |
        |---|---|
        | `tiers.{newbie,mid,high}.{minBet,maxBet,sideMax}` | Baccarat table limits and tie/pair side-bet cap per tier. Platform defaults: newbie 1K-50K, mid 5K-500K, high 50K-5M; tie/pair 5K / 50K / 500K. |
        | `tiers.*.{minBuyIn,maxBuyIn}` | Texas buy-in range per tier. |
        | `maxPayoutPerBet` | Cap on the payout (stake included) of one bet. Checked **at bet time**: a stake whose payout would exceed it is rejected `bet_limit` (the player is shown the largest allowed stake). Never silently truncated. |
        | `maxWinPerRound` | Cap on one player's net win in one baccarat/roulette round. Applied **at settlement**; the excess is not paid and the round is flagged `capped: true`. |
        | `maxWinPerDay` | Cap on a player's net win per UTC+8 day. Once reached, later settlements that day are truncated to the remaining headroom and, at zero, new bets are refused `daily_win_cap`. Resets at 00:00 UTC+8. For Texas the cap does not truncate hands; once reached the player cannot make new buy-ins for the rest of the day. |
        | `dailyBonus.{enabled,amount}` | Daily login bonus; off for partner players by default. |
        | `minTransfer`, `maxTransfer` | Allowed transfer size (guards against fat-finger amounts). |
        | `sessionHours` | Partner session length, 1-24, default 12. |

        **Not settable**: baccarat / roulette payouts and drawing rules (RTP follows from the rules), Texas shuffling and
        dealing. Attempting to write them is rejected with `bad_limits`.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/limits' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273618000' \
          -H 'X-Nonce: n18a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: d9b8d5b3743990f7693ee1a526cb21197ce8b37147ec009819fde2f034f85a40'
        ```
      responses:
        "200":
          description: Current limits with platform bounds.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LimitsView"
              example:
                tiers:
                  newbie:
                    minBet:
                      value: 1000
                      platformMin: 1000
                      platformMax: 50000
                    maxBet:
                      value: 50000
                      platformMin: 1000
                      platformMax: 50000
                    sideMax:
                      value: 5000
                      platformMin: 1000
                      platformMax: 5000
                maxPayoutPerBet:
                  value: 2000000
                  platformMin: 100000
                  platformMax: 50000000
                maxWinPerRound:
                  value: 5000000
                  platformMin: 100000
                  platformMax: 100000000
                maxWinPerDay:
                  value: 20000000
                  platformMin: 100000
                  platformMax: 1000000000
                dailyBonus:
                  enabled:
                    value: false
                  amount:
                    value: 0
                    platformMin: 0
                    platformMax: 100000
                minTransfer:
                  value: 1000
                  platformMin: 1
                  platformMax: 1000000
                maxTransfer:
                  value: 100000000
                  platformMin: 1000
                  platformMax: 9007199254740991
                sessionHours:
                  value: 12
                  platformMin: 1
                  platformMax: 24
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273618000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n18a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: d9b8d5b3743990f7693ee1a526cb21197ce8b37147ec009819fde2f034f85a40
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/limits' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273618000' \
              -H 'X-Nonce: n18a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: d9b8d5b3743990f7693ee1a526cb21197ce8b37147ec009819fde2f034f85a40'
    put:
      x-endpoint-no: 18
      operationId: putLimits
      tags:
        - Settings
      summary: Write operator limits
      description: |
        Overwrites the whole limits document with the values sent (omitted fields return to platform defaults).

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X PUT 'https://partner-api-sandbox.snowballcsn.app/v1/limits' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273619000' \
          -H 'X-Nonce: n19a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 05324da4ed463bba99c425cc8dde0058a6d3074077990c2f366b0a89a90a465e' \
          -H 'Content-Type: application/json' \
          --data-raw '{"tiers":{"newbie":{"minBet":1000,"maxBet":50000,"sideMax":5000},"mid":{"minBet":5000,"maxBet":500000,"sideMax":50000}},"maxPayoutPerBet":2000000,"maxWinPerRound":5000000,"maxWinPerDay":20000000,"dailyBonus":{"enabled":false,"amount":0},"minTransfer":1000,"maxTransfer":100000000,"sessionHours":12}'
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LimitsWrite"
            example:
              tiers:
                newbie:
                  minBet: 1000
                  maxBet: 50000
                  sideMax: 5000
                mid:
                  minBet: 5000
                  maxBet: 500000
                  sideMax: 50000
              maxPayoutPerBet: 2000000
              maxWinPerRound: 5000000
              maxWinPerDay: 20000000
              dailyBonus:
                enabled: false
                amount: 0
              minTransfer: 1000
              maxTransfer: 100000000
              sessionHours: 12
      responses:
        "200":
          description: Stored limits (same shape as GET).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LimitsView"
              example:
                tiers:
                  newbie:
                    minBet:
                      value: 1000
                      platformMin: 1000
                      platformMax: 50000
                    maxBet:
                      value: 50000
                      platformMin: 1000
                      platformMax: 50000
                    sideMax:
                      value: 5000
                      platformMin: 1000
                      platformMax: 5000
                maxPayoutPerBet:
                  value: 2000000
                  platformMin: 100000
                  platformMax: 50000000
                maxWinPerRound:
                  value: 5000000
                  platformMin: 100000
                  platformMax: 100000000
                maxWinPerDay:
                  value: 20000000
                  platformMin: 100000
                  platformMax: 1000000000
                dailyBonus:
                  enabled:
                    value: false
                  amount:
                    value: 0
                    platformMin: 0
                    platformMax: 100000
                minTransfer:
                  value: 1000
                  platformMin: 1
                  platformMax: 1000000
                maxTransfer:
                  value: 100000000
                  platformMin: 1000
                  platformMax: 9007199254740991
                sessionHours:
                  value: 12
                  platformMin: 1
                  platformMax: 24
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "415":
          $ref: "#/components/responses/UnsupportedMediaType"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273619000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n19a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 05324da4ed463bba99c425cc8dde0058a6d3074077990c2f366b0a89a90a465e
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X PUT 'https://partner-api-sandbox.snowballcsn.app/v1/limits' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273619000' \
              -H 'X-Nonce: n19a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 05324da4ed463bba99c425cc8dde0058a6d3074077990c2f366b0a89a90a465e' \
              -H 'Content-Type: application/json' \
              --data-raw '{"tiers":{"newbie":{"minBet":1000,"maxBet":50000,"sideMax":5000},"mid":{"minBet":5000,"maxBet":500000,"sideMax":50000}},"maxPayoutPerBet":2000000,"maxWinPerRound":5000000,"maxWinPerDay":20000000,"dailyBonus":{"enabled":false,"amount":0},"minTransfer":1000,"maxTransfer":100000000,"sessionHours":12}'
  /webhook:
    get:
      x-endpoint-no: 19
      operationId: getWebhook
      tags:
        - Webhook
      summary: Read webhook settings
      description: |
        See the **Webhook** section on the documentation home page for event shapes, signing and retry policy.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/webhook' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273620000' \
          -H 'X-Nonce: n20a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 2dbf915c97363a0d1f54bd7223fa5bc9bc4c7d11c5c7f9e936581db34889796e'
        ```
      responses:
        "200":
          description: Current settings (the signing secret is never returned here).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookSettings"
              example:
                url: https://ops.example.com/snowball/hook
                events:
                  - balance.changed
                  - transfer.completed
                enabled: true
                threshold: 5000000
                coalesceMs: 1000
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273620000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n20a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 2dbf915c97363a0d1f54bd7223fa5bc9bc4c7d11c5c7f9e936581db34889796e
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/webhook' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273620000' \
              -H 'X-Nonce: n20a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 2dbf915c97363a0d1f54bd7223fa5bc9bc4c7d11c5c7f9e936581db34889796e'
    put:
      x-endpoint-no: 19
      operationId: putWebhook
      tags:
        - Webhook
      summary: Set webhook URL, events and secret
      description: |
        `url` must be https and publicly reachable. The **first** successful call returns `signingSecret` exactly once;
        send `resetSecret: true` to rotate it (the new one is again shown once).

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X PUT 'https://partner-api-sandbox.snowballcsn.app/v1/webhook' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273621000' \
          -H 'X-Nonce: n21a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 5f6d048e9cdc02f03af9bba4e6bef43114df4df663ce06088dbf4dccc22ea06a' \
          -H 'Content-Type: application/json' \
          --data-raw '{"url":"https://ops.example.com/snowball/hook","events":["balance.changed","transfer.completed","win.big","player.session"],"enabled":true,"threshold":5000000,"coalesceMs":1000}'
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookWrite"
            example:
              url: https://ops.example.com/snowball/hook
              events:
                - balance.changed
                - transfer.completed
                - win.big
                - player.session
              enabled: true
              threshold: 5000000
              coalesceMs: 1000
      responses:
        "200":
          description: Stored settings; `signingSecret` only on first set or after `resetSecret`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookSettings"
              example:
                url: https://ops.example.com/snowball/hook
                events:
                  - balance.changed
                  - transfer.completed
                  - win.big
                  - player.session
                enabled: true
                threshold: 5000000
                coalesceMs: 1000
                signingSecret: whsec_3f9a0c51d27b4e68a1c3
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "415":
          $ref: "#/components/responses/UnsupportedMediaType"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273621000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n21a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 5f6d048e9cdc02f03af9bba4e6bef43114df4df663ce06088dbf4dccc22ea06a
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X PUT 'https://partner-api-sandbox.snowballcsn.app/v1/webhook' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273621000' \
              -H 'X-Nonce: n21a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 5f6d048e9cdc02f03af9bba4e6bef43114df4df663ce06088dbf4dccc22ea06a' \
              -H 'Content-Type: application/json' \
              --data-raw '{"url":"https://ops.example.com/snowball/hook","events":["balance.changed","transfer.completed","win.big","player.session"],"enabled":true,"threshold":5000000,"coalesceMs":1000}'
  /webhook/test:
    post:
      x-endpoint-no: 20
      operationId: testWebhook
      tags:
        - Webhook
      summary: Send a test event
      description: |
        Delivers one signed `webhook.test` event to your configured URL and reports the HTTP status your server gave.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/webhook/test' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273622000' \
          -H 'X-Nonce: n22a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 06be7fb91cf850e8b8a2df71de708370babaebfafc7fa6bb442ccbdce05c49fd' \
          -H 'Content-Type: application/json' \
          --data-raw '{"event":"balance.changed"}'
        ```
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                event:
                  type: string
                  description: Event type to simulate; defaults to `balance.changed`.
                  examples:
                    - balance.changed
            example:
              event: balance.changed
      responses:
        "200":
          description: Delivery attempt result.
          content:
            application/json:
              schema:
                type: object
                required:
                  - delivered
                  - eventId
                properties:
                  delivered:
                    type: boolean
                  eventId:
                    type: string
                  httpStatus:
                    type:
                      - integer
                      - "null"
                    description: Status returned by your endpoint, or null if unreachable.
                  durationMs:
                    type: integer
              example:
                delivered: true
                eventId: evt_test_01J9Z3K8M2
                httpStatus: 200
                durationMs: 143
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273622000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n22a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 06be7fb91cf850e8b8a2df71de708370babaebfafc7fa6bb442ccbdce05c49fd
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/webhook/test' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273622000' \
              -H 'X-Nonce: n22a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 06be7fb91cf850e8b8a2df71de708370babaebfafc7fa6bb442ccbdce05c49fd' \
              -H 'Content-Type: application/json' \
              --data-raw '{"event":"balance.changed"}'
  /bots/mode:
    get:
      x-endpoint-no: 21
      operationId: getBotMode
      tags:
        - Settings
      summary: Read bot mode and schedule
      description: |
        Bot mode is a **playing style only** (`smart`, `normal`, `casual`). It does not change dealing or results and is
        **not an RTP dial**. With no configuration the mode is `normal`.

        Window priority (strongest first): single dates > date range > weekly days > every day > `default`.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/bots/mode' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273623000' \
          -H 'X-Nonce: n23a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 9cd8b1c50aabef9828604e10d327ecf9145e59febe4761241c0ae875d6ccc21b'
        ```
      responses:
        "200":
          description: Rules and the mode in force now.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BotModeView"
              example:
                configured: true
                rules:
                  tz: Asia/Kuala_Lumpur
                  switchAt: 00:00
                  default: normal
                  windows:
                    - days:
                        - 5
                        - 6
                      from: 20:00
                      to: 02:00
                      mode: casual
                    - dates:
                        - 2026-10-10
                      from: 00:00
                      to: 24:00
                      mode: smart
                override: null
                effective:
                  mode: normal
                  scheduled: normal
                  businessDay: 2026-10-06
                now: 1791273600000
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273623000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n23a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 9cd8b1c50aabef9828604e10d327ecf9145e59febe4761241c0ae875d6ccc21b
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/bots/mode' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273623000' \
              -H 'X-Nonce: n23a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 9cd8b1c50aabef9828604e10d327ecf9145e59febe4761241c0ae875d6ccc21b'
    put:
      x-endpoint-no: 21
      operationId: putBotMode
      tags:
        - Settings
      summary: Write bot mode and schedule
      description: |
        Send `rules` (replaces the whole schedule, at most 32 windows), and/or `immediate` (switch now; it lasts at most
        24 hours and ends early when the schedule changes the mode), and/or `clearImmediate: true`. At least one is required.
        Takes effect from the next decision.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X PUT 'https://partner-api-sandbox.snowballcsn.app/v1/bots/mode' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273624000' \
          -H 'X-Nonce: n24a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 0363c491d8892345b40207bb0ef72aa06578567f6ef244e996f61e6d96fa1899' \
          -H 'Content-Type: application/json' \
          --data-raw '{"rules":{"tz":"Asia/Kuala_Lumpur","switchAt":"00:00","default":"normal","windows":[{"days":[5,6],"from":"20:00","to":"02:00","mode":"casual"}]}}'
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BotModeWrite"
            examples:
              rules:
                value:
                  rules:
                    tz: Asia/Kuala_Lumpur
                    switchAt: 00:00
                    default: normal
                    windows:
                      - days:
                          - 5
                          - 6
                        from: 20:00
                        to: 02:00
                        mode: casual
              immediate:
                value:
                  immediate: smart
      responses:
        "200":
          description: Stored state (same shape as GET, plus `ok`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BotModeView"
              example:
                ok: true
                configured: true
                rules:
                  tz: Asia/Kuala_Lumpur
                  switchAt: 00:00
                  default: normal
                  windows:
                    - days:
                        - 5
                        - 6
                      from: 20:00
                      to: 02:00
                      mode: casual
                override: null
                effective:
                  mode: normal
                  scheduled: normal
                  businessDay: 2026-10-06
                now: 1791273600000
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "415":
          $ref: "#/components/responses/UnsupportedMediaType"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273624000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n24a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 0363c491d8892345b40207bb0ef72aa06578567f6ef244e996f61e6d96fa1899
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X PUT 'https://partner-api-sandbox.snowballcsn.app/v1/bots/mode' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273624000' \
              -H 'X-Nonce: n24a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 0363c491d8892345b40207bb0ef72aa06578567f6ef244e996f61e6d96fa1899' \
              -H 'Content-Type: application/json' \
              --data-raw '{"rules":{"tz":"Asia/Kuala_Lumpur","switchAt":"00:00","default":"normal","windows":[{"days":[5,6],"from":"20:00","to":"02:00","mode":"casual"}]}}'
  /texas/tables:
    get:
      x-endpoint-no: 22
      operationId: getTexasTables
      tags:
        - Settings
      summary: Read Texas table types
      description: |
        Per tier (`newbie`, `mid`, `high`) a table type:

        * **`botsOnly`**: your players play only against bots, never with other partners' players. Bot style follows
          `/bots/mode`. No rake by default (optional). This does **not** set a return rate: bot mode only changes how the
          bots play, not the deal or the results; the cards come from a verifiable shuffle and bots cannot see anyone's
          hole cards. Realized results are measured in `/reports/rtp`.
        * **`mixed`**: real players plus bots at the same table, rake collected per `rake{pct,cap,noFlopNoDrop}`, settable per tier.

        Your players always sit at their own tables, not shared with other partners.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/texas/tables' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273625000' \
          -H 'X-Nonce: n25a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 82c87fb8aad651e056d9f1bbd4f23c9f5245a61878765bfa5126acbf066b43fa'
        ```
      responses:
        "200":
          description: Current table types.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TexasTables"
              example:
                tiers:
                  newbie:
                    type: botsOnly
                  mid:
                    type: mixed
                    rake:
                      pct: 5
                      cap: 30000
                      noFlopNoDrop: true
                  high:
                    type: mixed
                    rake:
                      pct: 3
                      cap: 100000
                      noFlopNoDrop: true
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273625000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n25a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 82c87fb8aad651e056d9f1bbd4f23c9f5245a61878765bfa5126acbf066b43fa
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X GET 'https://partner-api-sandbox.snowballcsn.app/v1/texas/tables' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273625000' \
              -H 'X-Nonce: n25a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 82c87fb8aad651e056d9f1bbd4f23c9f5245a61878765bfa5126acbf066b43fa'
    put:
      x-endpoint-no: 22
      operationId: putTexasTables
      tags:
        - Settings
      summary: Write Texas table types
      description: |
        Overwrites the whole document. Applies from the next table that opens / the next hand. Audit-logged.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X PUT 'https://partner-api-sandbox.snowballcsn.app/v1/texas/tables' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273626000' \
          -H 'X-Nonce: n26a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: b380911c9d5c9df6a054e9cbb77438cc56be6b68171e78d1cec5436224259884' \
          -H 'Content-Type: application/json' \
          --data-raw '{"tiers":{"newbie":{"type":"botsOnly"},"mid":{"type":"mixed","rake":{"pct":5,"cap":30000,"noFlopNoDrop":true}},"high":{"type":"mixed","rake":{"pct":3,"cap":100000,"noFlopNoDrop":true}}}}'
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TexasTables"
            example:
              tiers:
                newbie:
                  type: botsOnly
                mid:
                  type: mixed
                  rake:
                    pct: 5
                    cap: 30000
                    noFlopNoDrop: true
                high:
                  type: mixed
                  rake:
                    pct: 3
                    cap: 100000
                    noFlopNoDrop: true
      responses:
        "200":
          description: Stored table types.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TexasTables"
              example:
                tiers:
                  newbie:
                    type: botsOnly
                  mid:
                    type: mixed
                    rake:
                      pct: 5
                      cap: 30000
                      noFlopNoDrop: true
                  high:
                    type: mixed
                    rake:
                      pct: 3
                      cap: 100000
                      noFlopNoDrop: true
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "415":
          $ref: "#/components/responses/UnsupportedMediaType"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273626000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n26a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: b380911c9d5c9df6a054e9cbb77438cc56be6b68171e78d1cec5436224259884
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X PUT 'https://partner-api-sandbox.snowballcsn.app/v1/texas/tables' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273626000' \
              -H 'X-Nonce: n26a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: b380911c9d5c9df6a054e9cbb77438cc56be6b68171e78d1cec5436224259884' \
              -H 'Content-Type: application/json' \
              --data-raw '{"tiers":{"newbie":{"type":"botsOnly"},"mid":{"type":"mixed","rake":{"pct":5,"cap":30000,"noFlopNoDrop":true}},"high":{"type":"mixed","rake":{"pct":3,"cap":100000,"noFlopNoDrop":true}}}}'
  /sandbox/players/{playerId}/faucet:
    post:
      operationId: sandboxFaucet
      tags:
        - Sandbox
      summary: Add fake chips (sandbox only)
      description: |
        Exists only on the sandbox host (not part of the 23 production endpoints). Credits any amount of fake chips.

        Sandbox also accepts the request header `X-Sandbox-Scenario: timeout | 500 | insufficient | seated` on any
        endpoint to force that failure, so you can test your retry and idempotency handling.

        **Example request** (signed with the published test secret; header values are real and verifiable with the test vectors on the home page):

        ```bash
        curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/sandbox/players/u_9f3c1a2b7d4e5f6a8b90/faucet' \
          -H 'X-Partner-Id: 12' \
          -H 'X-Key-Id: k1' \
          -H 'X-Timestamp: 1791273627000' \
          -H 'X-Nonce: n27a1b2c3d4e5f60718293a4b5c6d7e' \
          -H 'X-Signature: 1dfe3bb2a831dae2ed7505ef618eeb3b271595dcd26440a8fd20def1865de46c' \
          -H 'Content-Type: application/json' \
          --data-raw '{"amount":1000000}'
        ```
      parameters:
        - name: X-Partner-Id
          in: header
          required: true
          description: Your partner id.
          schema:
            type: string
          example: "12"
        - name: X-Key-Id
          in: header
          required: true
          description: Key id.
          schema:
            type: string
          example: k1
        - name: X-Timestamp
          in: header
          required: true
          description: Epoch ms; within 5 minutes of our clock.
          schema:
            type: string
          example: "1791273627000"
        - name: X-Nonce
          in: header
          required: true
          description: 16-64 chars, unique for 5 minutes.
          schema:
            type: string
          example: n27a1b2c3d4e5f60718293a4b5c6d7e
        - name: X-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of the canonical string (see Signing on the home page). The example value is a real signature of the example request below, made with the published test secret.
          schema:
            type: string
          example: 1dfe3bb2a831dae2ed7505ef618eeb3b271595dcd26440a8fd20def1865de46c
        - $ref: "#/components/parameters/PlayerRef"
        - name: X-Sandbox-Scenario
          in: header
          required: false
          schema:
            type: string
            enum:
              - timeout
              - "500"
              - insufficient
              - seated
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - amount
              properties:
                amount:
                  $ref: "#/components/schemas/Amount"
            example:
              amount: 1000000
      responses:
        "200":
          description: Credited.
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                  - balance
                properties:
                  ok:
                    type: boolean
                  balance:
                    type: integer
              example:
                ok: true
                balance: 2500000
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          description: |
            Authentication failed. `bad_signature` (missing header, unknown partner/key, or signature mismatch; not broken
            down further), `timestamp_out_of_window`, `replay`, `launch_token_invalid`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                bad_signature:
                  summary: Signature, key or partner not accepted
                  value:
                    error: bad_signature
                    message: signature verification failed
                    traceId: tr_8c1e0f7a52
                timestamp_out_of_window:
                  summary: Clock off by more than 5 minutes
                  value:
                    error: timestamp_out_of_window
                    message: X-Timestamp is more than 5 minutes from server time
                    traceId: tr_8c1e0f7a52
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      x-codeSamples:
        - lang: Shell
          label: curl
          source: |-
            curl -X POST 'https://partner-api-sandbox.snowballcsn.app/v1/sandbox/players/u_9f3c1a2b7d4e5f6a8b90/faucet' \
              -H 'X-Partner-Id: 12' \
              -H 'X-Key-Id: k1' \
              -H 'X-Timestamp: 1791273627000' \
              -H 'X-Nonce: n27a1b2c3d4e5f60718293a4b5c6d7e' \
              -H 'X-Signature: 1dfe3bb2a831dae2ed7505ef618eeb3b271595dcd26440a8fd20def1865de46c' \
              -H 'Content-Type: application/json' \
              --data-raw '{"amount":1000000}'
components:
  securitySchemes:
    SnowballHmac:
      type: apiKey
      in: header
      name: X-Signature
      description: |
        HMAC-SHA256 request signing. Send **all five** headers on every request:

        | Header | Value |
        |---|---|
        | `X-Partner-Id` | Your partner id (integer as string) |
        | `X-Key-Id` | Key id (`k1`, `k2`, ...; two keys may be active during rotation) |
        | `X-Timestamp` | Send time, epoch ms. More than 5 minutes off our clock → 401 `timestamp_out_of_window` |
        | `X-Nonce` | 16-64 chars `[A-Za-z0-9_-]`, unique per partner within 5 minutes, else 401 `replay` |
        | `X-Signature` | Lowercase hex of `HMAC_SHA256(secret, canonicalString)` |

        `canonicalString` = the following lines joined with `\n` (no trailing newline):
        `SNOWBALL-HMAC-SHA256-V1`, `partnerId`, `timestamp`, `nonce`, `METHOD` (upper case), `path` (with the `/v1`
        prefix, no query), `canonicalQuery` (keys sorted, then values sorted, `key=value` joined by `&`, RFC 3986
        percent-encoding; empty string if none), `sha256hex(rawBody)` (hash of the empty string when there is no body).

        The secret (64 hex characters) is used as a **UTF-8 string** key, not hex-decoded. Sign the **raw bytes** you send.
        Full algorithm, test vectors and Node / PHP code are on the documentation home page. The "Authorize" button in this
        UI cannot compute signatures, so "Try it out" is disabled.
  parameters:
    PlayerRef:
      name: playerId
      in: path
      required: true
      description: Snowball `playerId` (`u_...`) **or** your `externalPlayerId` (never starts with `u_`).
      schema:
        type: string
        minLength: 1
        maxLength: 64
      examples:
        byPlayerId:
          value: u_9f3c1a2b7d4e5f6a8b90
        byExternal:
          value: ops-user-88421
    Limit:
      name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 200
        default: 50
      example: 50
    Cursor:
      name: cursor
      in: query
      description: Opaque value from the previous page's `nextCursor`.
      schema:
        type: string
    From:
      name: from
      in: query
      description: Start, epoch ms, inclusive. Default is 30 days before `to`.
      schema:
        type: integer
        format: int64
      example: 1791187200000
    To:
      name: to
      in: query
      description: End, epoch ms, exclusive. Default is now. `to - from` may be at most 92 days.
      schema:
        type: integer
        format: int64
      example: 1791273600000
    TxType:
      name: type
      in: query
      description: Comma-separated filter of transaction types.
      schema:
        type: string
      example: transfer_in,transfer_out
  schemas:
    ErrorCode:
      type: string
      description: Machine-readable error code. Only this field is stable.
      enum:
        - bad_request
        - bad_amount
        - amount_out_of_range
        - bad_request_id
        - bad_username
        - bad_range
        - bad_limits
        - bad_return_url
        - bad_signature
        - timestamp_out_of_window
        - replay
        - launch_token_invalid
        - ip_not_allowed
        - partner_disabled
        - banned
        - partner_login_only
        - forbidden_game
        - player_not_found
        - transfer_not_found
        - insufficient
        - seated
        - idempotency_conflict
        - username_taken
        - player_suspended
        - payload_too_large
        - unsupported_media_type
        - rate_limited
        - internal
        - maintenance
    Error:
      type: object
      required:
        - error
        - message
        - traceId
      properties:
        error:
          $ref: "#/components/schemas/ErrorCode"
        message:
          type: string
          description: English hint; not stable.
        traceId:
          type: string
        retryable:
          type: boolean
          description: Present on `seated`.
        currentTable:
          type: string
          description: Present on `seated`.
        details:
          type: object
          additionalProperties: true
    Amount:
      type: integer
      format: int64
      minimum: 1
      maximum: 9007199254740991
      description: Positive integer number of chips.
    RequestId:
      type: string
      pattern: ^[A-Za-z0-9_.:-]{1,64}$
      description: Unique per partner forever.
    PlayerStatus:
      type: string
      enum:
        - active
        - suspended
    Game:
      type: string
      enum:
        - texas
        - baccarat
        - roulette
      description: Roulette is listed for forward compatibility and is not included in v1.
    PingResponse:
      type: object
      required:
        - ok
        - serverTime
        - partnerId
        - env
      properties:
        ok:
          type: boolean
        serverTime:
          type: integer
          format: int64
          description: Server clock, epoch ms.
        partnerId:
          type: integer
        env:
          type: string
          enum:
            - sandbox
            - production
    CreatePlayerRequest:
      type: object
      properties:
        requestId:
          type: string
          description: Logged only; the idempotency key is `externalPlayerId`.
        externalPlayerId:
          type: string
          minLength: 1
          maxLength: 64
          description: Your player number; must not start with `u_`.
        username:
          type:
            - string
            - "null"
          pattern: ^[a-z0-9_]{3,24}$
          description: Omit or null to auto-generate. Must carry your partner prefix.
        displayName:
          type:
            - string
            - "null"
          maxLength: 16
        lang:
          type:
            - string
            - "null"
          description: Stored only.
        validDays:
          type:
            - integer
            - "null"
          description: Account validity in days; null = no expiry.
    CreatePlayerResponse:
      type: object
      required:
        - created
        - playerId
        - username
        - status
        - balance
        - createdAt
      properties:
        created:
          type: boolean
          description: false when the externalPlayerId already existed.
        playerId:
          type: string
          examples:
            - u_9f3c1a2b7d4e5f6a8b90
        username:
          type: string
        externalPlayerId:
          type: string
        status:
          $ref: "#/components/schemas/PlayerStatus"
        balance:
          type: integer
        createdAt:
          type: integer
          format: int64
    Player:
      type: object
      required:
        - playerId
        - username
        - status
        - balance
        - seated
      properties:
        playerId:
          type: string
        username:
          type: string
        externalPlayerId:
          type:
            - string
            - "null"
        displayName:
          type:
            - string
            - "null"
        status:
          $ref: "#/components/schemas/PlayerStatus"
        balance:
          type: integer
        seated:
          type: boolean
        currentTable:
          type:
            - string
            - "null"
        lastLoginAt:
          type:
            - integer
            - "null"
          format: int64
        createdAt:
          type: integer
          format: int64
    PlayerPage:
      type: object
      required:
        - items
        - nextCursor
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/Player"
        nextCursor:
          type:
            - string
            - "null"
    BatchRequest:
      type: object
      required:
        - batchId
      description: Provide `count` **or** `players` (at most 200).
      properties:
        batchId:
          type: string
          description: Replaying the same batchId returns the same result.
        count:
          type: integer
          minimum: 1
          maximum: 200
        prefix:
          type: string
        validDays:
          type:
            - integer
            - "null"
        players:
          type: array
          maxItems: 200
          items:
            type: object
            properties:
              externalPlayerId:
                type: string
              username:
                type:
                  - string
                  - "null"
              displayName:
                type:
                  - string
                  - "null"
    BatchResponse:
      type: object
      required:
        - items
      properties:
        items:
          type: array
          items:
            type: object
            required:
              - playerId
              - username
            properties:
              playerId:
                type: string
              username:
                type: string
              externalPlayerId:
                type:
                  - string
                  - "null"
    LaunchRequest:
      type: object
      description: Provide `externalPlayerId` **or** `playerId`.
      properties:
        externalPlayerId:
          type: string
          description: Unknown ids are created automatically.
        playerId:
          type: string
        displayName:
          type: string
          maxLength: 16
        game:
          type:
            - string
            - "null"
          enum:
            - texas
            - baccarat
            - null
          description: Land directly in this game; omit for the Snowball lobby.
        tableId:
          type:
            - string
            - "null"
        lang:
          type: string
        returnUrl:
          type: string
          format: uri
          description: Must be on your allow-listed domains.
        ttlSeconds:
          type: integer
          minimum: 10
          maximum: 300
          default: 60
    LaunchResponse:
      type: object
      required:
        - playerId
        - created
        - launchToken
        - launchUrl
        - expiresAt
      properties:
        playerId:
          type: string
        created:
          type: boolean
        launchToken:
          type: string
          description: One-time token, `lt_` + 64 hex.
        launchUrl:
          type: string
          format: uri
        expiresAt:
          type: integer
          format: int64
    Balance:
      type: object
      required:
        - playerId
        - balance
        - seated
        - asOf
      properties:
        playerId:
          type: string
        balance:
          type: integer
        seated:
          type: boolean
          description: Also true for baccarat players with unsettled bets.
        currentTable:
          type:
            - string
            - "null"
        asOf:
          type: integer
          format: int64
    TransferRequest:
      type: object
      required:
        - requestId
        - amount
      properties:
        requestId:
          $ref: "#/components/schemas/RequestId"
        amount:
          $ref: "#/components/schemas/Amount"
        ref:
          type: string
          maxLength: 64
          description: Stored verbatim in the ledger.
    TransferOutRequest:
      type: object
      required:
        - requestId
      description: "Send `amount` or `all: true`."
      properties:
        requestId:
          $ref: "#/components/schemas/RequestId"
        amount:
          $ref: "#/components/schemas/Amount"
        all:
          type: boolean
        ref:
          type: string
          maxLength: 64
    TransferResponse:
      type: object
      required:
        - ok
        - replayed
        - transferId
        - txId
        - requestId
        - type
        - amount
        - balance
        - ts
      properties:
        ok:
          type: boolean
        replayed:
          type: boolean
        transferId:
          type: string
        txId:
          type: string
          description: "Deterministic: `ptr:{partnerId}:{requestId}`."
        requestId:
          type: string
        type:
          type: string
          enum:
            - transfer_in
            - transfer_out
        amount:
          type: integer
        balance:
          type: integer
          description: Wallet balance after the transfer.
        ts:
          type: integer
          format: int64
    TransferLookup:
      type: object
      required:
        - status
      properties:
        status:
          type: string
          enum:
            - completed
            - not_found
        transfer:
          type: object
          properties:
            transferId:
              type: string
            txId:
              type: string
            requestId:
              type: string
            playerId:
              type: string
            type:
              type: string
              enum:
                - transfer_in
                - transfer_out
            amount:
              type: integer
            balance:
              type: integer
            ref:
              type:
                - string
                - "null"
            ts:
              type: integer
              format: int64
    Transaction:
      type: object
      required:
        - txId
        - type
        - amount
        - balance
        - ts
      properties:
        txId:
          type: string
        type:
          type: string
          enum:
            - transfer_in
            - transfer_out
            - buy_in
            - cash_out
            - bet
            - payout
            - refund
            - adjustment
            - daily_bonus
        amount:
          type: integer
          description: Signed from the player's point of view.
        balance:
          type: integer
          description: Wallet balance after this row; never negative.
        ref:
          type:
            - string
            - "null"
        game:
          type:
            - string
            - "null"
          enum:
            - texas
            - baccarat
            - roulette
            - null
        ts:
          type: integer
          format: int64
    TransactionPage:
      type: object
      required:
        - items
        - nextCursor
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/Transaction"
        nextCursor:
          type:
            - string
            - "null"
    PartnerTransactionPage:
      type: object
      required:
        - items
        - nextCursor
      properties:
        items:
          type: array
          items:
            allOf:
              - $ref: "#/components/schemas/Transaction"
              - type: object
                properties:
                  playerId:
                    type: string
                  username:
                    type: string
        nextCursor:
          type:
            - string
            - "null"
    Round:
      type: object
      required:
        - game
        - roundId
        - tableId
        - ts
        - net
      properties:
        game:
          $ref: "#/components/schemas/Game"
        roundId:
          type: string
        tableId:
          type: string
        tier:
          type: string
          enum:
            - newbie
            - mid
            - high
        ts:
          type: integer
          format: int64
        bets:
          type: array
          description: Baccarat only.
          items:
            type: object
            properties:
              type:
                type: string
                examples:
                  - player
                  - banker
                  - tie
                  - playerPair
                  - bankerPair
              amount:
                type: integer
        staked:
          type:
            - integer
            - "null"
        returned:
          type:
            - integer
            - "null"
        net:
          type: integer
        rake:
          type: integer
          description: Texas only; already deducted from `net`.
        capped:
          type: boolean
        result:
          type: object
          additionalProperties: true
    RoundPage:
      type: object
      required:
        - items
        - nextCursor
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/Round"
        nextCursor:
          type:
            - string
            - "null"
    SummaryReport:
      type: object
      required:
        - items
        - totals
      properties:
        items:
          type: array
          items:
            type: object
            properties:
              day:
                type: string
                format: date
              transferIn:
                type: integer
              transferOut:
                type: integer
              net:
                type: integer
              turnover:
                type: integer
              playerWin:
                type: integer
              rake:
                type: integer
              activePlayers:
                type: integer
        totals:
          type: object
          additionalProperties:
            type: integer
        walletTotal:
          type: integer
          description: Sum of all your players' wallet balances.
        atTables:
          type: integer
          description: Chips currently on tables.
    RtpReport:
      type: object
      required:
        - items
        - totals
      properties:
        items:
          type: array
          items:
            type: object
            properties:
              day:
                type: string
              game:
                $ref: "#/components/schemas/Game"
              tableType:
                type:
                  - string
                  - "null"
                enum:
                  - botsOnly
                  - mixed
                  - null
              mode:
                type: string
              hands:
                type: integer
              staked:
                type: integer
              returned:
                type: integer
              realizedRTP:
                type: number
              n:
                type: integer
              lowSample:
                type: boolean
              humanStaked:
                type:
                  - integer
                  - "null"
              humanNet:
                type: integer
              botNet:
                type: integer
              rake:
                type: integer
              realizedReturn:
                type:
                  - number
                  - "null"
        totals:
          type: object
          additionalProperties: true
    LimitField:
      type: object
      properties:
        value: {}
        platformMin:
          type: integer
        platformMax:
          type: integer
    LimitsView:
      type: object
      description: Every leaf is a `LimitField` (`value`, `platformMin`, `platformMax`).
      additionalProperties: true
      properties:
        tiers:
          type: object
          additionalProperties: true
        maxPayoutPerBet:
          $ref: "#/components/schemas/LimitField"
        maxWinPerRound:
          $ref: "#/components/schemas/LimitField"
        maxWinPerDay:
          $ref: "#/components/schemas/LimitField"
        dailyBonus:
          type: object
          additionalProperties: true
        minTransfer:
          $ref: "#/components/schemas/LimitField"
        maxTransfer:
          $ref: "#/components/schemas/LimitField"
        sessionHours:
          $ref: "#/components/schemas/LimitField"
    LimitsWrite:
      type: object
      additionalProperties: false
      properties:
        tiers:
          type: object
          additionalProperties:
            type: object
            properties:
              minBet:
                type: integer
              maxBet:
                type: integer
              sideMax:
                type: integer
              minBuyIn:
                type: integer
              maxBuyIn:
                type: integer
        maxPayoutPerBet:
          type: integer
        maxWinPerRound:
          type: integer
        maxWinPerDay:
          type: integer
        dailyBonus:
          type: object
          properties:
            enabled:
              type: boolean
            amount:
              type: integer
        minTransfer:
          type: integer
        maxTransfer:
          type: integer
        sessionHours:
          type: integer
          minimum: 1
          maximum: 24
    WebhookSettings:
      type: object
      properties:
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
            enum:
              - balance.changed
              - win.big
              - transfer.completed
              - player.session
        enabled:
          type: boolean
        threshold:
          type: integer
          description: Net win that triggers `win.big`.
        coalesceMs:
          type: integer
          description: Merge `balance.changed` per player within this window; 0 = off.
        signingSecret:
          type: string
          description: Shown only on first set or after `resetSecret`.
    WebhookWrite:
      type: object
      required:
        - url
        - events
        - enabled
      properties:
        url:
          type: string
          format: uri
          description: https only.
        events:
          type: array
          items:
            type: string
            enum:
              - balance.changed
              - win.big
              - transfer.completed
              - player.session
        enabled:
          type: boolean
        threshold:
          type: integer
        coalesceMs:
          type: integer
        resetSecret:
          type: boolean
    BotMode:
      type: string
      enum:
        - smart
        - normal
        - casual
    BotWindow:
      type: object
      required:
        - from
        - to
        - mode
      description: |
        A time window. Selector (pick at most one): `dates` (specific days, strongest), `range` (date range, both ends
        included), `days` (weekdays 0=Sunday..6=Saturday on which the window **starts**); none = every day. `from`/`to`
        are `HH:MM` in `tz`; `to` at or before `from` runs past midnight; `24:00` = end of day.
      properties:
        days:
          type: array
          items:
            type: integer
            minimum: 0
            maximum: 6
        dates:
          type: array
          items:
            type: string
            format: date
        range:
          type: object
          required:
            - from
            - to
          properties:
            from:
              type: string
              format: date
            to:
              type: string
              format: date
        from:
          type: string
          pattern: ^\d{1,2}:\d{2}$
        to:
          type: string
          pattern: ^\d{1,2}:\d{2}$
        mode:
          $ref: "#/components/schemas/BotMode"
    BotRules:
      type: object
      required:
        - tz
        - switchAt
        - default
        - windows
      properties:
        tz:
          type: string
          description: IANA time zone.
          examples:
            - Asia/Kuala_Lumpur
        switchAt:
          type: string
          description: HH:MM local time at which the business day changes (report grouping).
        default:
          $ref: "#/components/schemas/BotMode"
        windows:
          type: array
          maxItems: 32
          items:
            $ref: "#/components/schemas/BotWindow"
    BotModeView:
      type: object
      properties:
        ok:
          type: boolean
        configured:
          type: boolean
        rules:
          $ref: "#/components/schemas/BotRules"
        override:
          type:
            - object
            - "null"
          properties:
            mode:
              $ref: "#/components/schemas/BotMode"
            since:
              type: integer
              format: int64
        effective:
          type: object
          properties:
            mode:
              $ref: "#/components/schemas/BotMode"
            scheduled:
              $ref: "#/components/schemas/BotMode"
            businessDay:
              type: string
              format: date
        now:
          type: integer
          format: int64
    BotModeWrite:
      type: object
      minProperties: 1
      properties:
        rules:
          $ref: "#/components/schemas/BotRules"
        immediate:
          $ref: "#/components/schemas/BotMode"
        clearImmediate:
          type: boolean
    Rake:
      type: object
      properties:
        pct:
          type: number
          minimum: 0
          description: Percent of the pot.
        cap:
          type: integer
          description: Maximum rake per hand, in chips.
        noFlopNoDrop:
          type: boolean
          description: No rake on hands that end before the flop.
    TexasTables:
      type: object
      required:
        - tiers
      properties:
        tiers:
          type: object
          properties:
            newbie:
              $ref: "#/components/schemas/TexasTier"
            mid:
              $ref: "#/components/schemas/TexasTier"
            high:
              $ref: "#/components/schemas/TexasTier"
    TexasTier:
      type: object
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - botsOnly
            - mixed
        rake:
          $ref: "#/components/schemas/Rake"
  responses:
    BadRequest:
      description: |
        Invalid request. Codes: `bad_request`, `bad_amount`, `amount_out_of_range`, `bad_request_id`, `bad_username`,
        `bad_range`, `bad_limits`, `bad_return_url`.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: bad_amount
            message: amount must be a positive integer
            traceId: tr_8c1e0f7a52
    Forbidden:
      description: |
        `ip_not_allowed` (checked before the signature), `partner_disabled`, `banned`, `partner_login_only`, `forbidden_game`.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: ip_not_allowed
            message: source IP is not on the allow-list
            traceId: tr_8c1e0f7a52
    NotFound:
      description: "`player_not_found` (does not exist or is not yours) or `transfer_not_found`."
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: player_not_found
            message: player not found
            traceId: tr_8c1e0f7a52
    Conflict:
      description: |
        `insufficient`, `seated`, `idempotency_conflict`, `username_taken`, `player_suspended`.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: idempotency_conflict
            message: requestId was already used with different parameters
            traceId: tr_8c1e0f7a52
    PayloadTooLarge:
      description: "`payload_too_large`: body above 64 KB."
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: payload_too_large
            message: request body exceeds 64 KB
            traceId: tr_8c1e0f7a52
    UnsupportedMediaType:
      description: "`unsupported_media_type`: body must be `application/json`."
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: unsupported_media_type
            message: Content-Type must be application/json
            traceId: tr_8c1e0f7a52
    RateLimited:
      description: "`rate_limited`. Wait `Retry-After` seconds."
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds to wait.
        X-RateLimit-Limit:
          schema:
            type: integer
        X-RateLimit-Remaining:
          schema:
            type: integer
        X-RateLimit-Reset:
          schema:
            type: integer
          description: Epoch seconds.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: rate_limited
            message: too many requests
            traceId: tr_8c1e0f7a52
    ServerError:
      description: "`internal`: our fault. For transfers, **retry with the same `requestId`**."
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: internal
            message: internal error
            traceId: tr_8c1e0f7a52
    Maintenance:
      description: |
        `maintenance`: maintenance or deployment drain; `Retry-After` is set. Transfers stay available during a game drain.
      headers:
        Retry-After:
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: maintenance
            message: service is being updated
            traceId: tr_8c1e0f7a52
