{
 "openapi": "3.1.0",
 "info": {
  "title": "Notell slots operator API",
  "version": "2.0.0",
  "description": "TEST_ONLY platform: game mathematics provided by the platform, non-withdrawable units, no real payments. Server-to-server JSON over HTTPS; every request carries `partner` and `api_key` (keep the key on your server). For `/gameActions.do` and `/playerRtp.do` the caller IP must be on the project allowlist. Errors are `{\"status\":\"error\",\"message\":\"<CODE>\"}`; the HTTP status of each code is listed in the error reference. Guides: https://docs.notellgaming.com/en/"
 },
 "servers": [
  {
   "url": "https://notell-api.com",
   "description": "Public API; partner + api_key"
  }
 ],
 "tags": [
  {
   "name": "launch"
  },
  {
   "name": "free spins"
  },
  {
   "name": "target outcomes"
  },
  {
   "name": "rounds"
  },
  {
   "name": "reporting"
  },
  {
   "name": "catalog"
  },
  {
   "name": "callbacks"
  },
  {
   "name": "rtp"
  }
 ],
 "paths": {
  "/playGame.do": {
   "post": {
    "operationId": "launchGame",
    "tags": [
     "launch"
    ],
    "summary": "Launch a game client with an own-backend session",
    "description": "`lang` is a standard language code (BCP 47: en, de, pt-BR, zh-Hans, zh-Hant, es-419 ...; case-insensitive, `_` accepted) mapped to each provider's own client code. A missing `lang` = the project's default language; a language the game does not support answers UNSUPPORTED_LANGUAGE. Relaunching with an unfinished round continues that round in the new language. `gametype:\"fun\"` opens the same game as a free-play demo (isolated bonus balance, default 100,000.00; no callbacks to your wallet, nothing in your reports); the response has the same shape plus `meta.gametype:\"fun\"`. Errors: INVALID_DEMO_BALANCE (400), DEMO_UNAVAILABLE (503).",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "description": "1-128 printable ASCII characters. A repeat with the same key and body returns the first answer; the same key with another body answers IDEMPOTENCY_CONFLICT (HTTP 400 on this endpoint).",
      "schema": {
       "type": "string",
       "minLength": 1,
       "maxLength": 128,
       "pattern": "^[\\x21-\\x7e]+$"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/LaunchRequest"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Session link (hand only this link to the player).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LaunchResponse"
        }
       }
      }
     },
     "400": {
      "description": "INVALID_REQUEST (unknown field, bad currency/player_id/enum), UNSUPPORTED_LANGUAGE, REAL_MODE_DISABLED, PROMO_UNSUPPORTED, INVALID_IDEMPOTENCY_KEY, IDEMPOTENCY_CONFLICT, INVALID_DEMO_BALANCE, ROUND_IN_PROGRESS, BALANCE_UNAVAILABLE, CALLBACK_HTTP_ERROR, INVALID_CALLBACK_RESPONSE, WALLET_IDENTITY_MISMATCH, REQUEST_FAILED",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "UNAUTHORIZED",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "UNSUPPORTED_GAME (also when `symbol` is missing or not a string)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "RATE_LIMITED",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "503": {
      "description": "DEMO_UNAVAILABLE: demo mode (`gametype: \"fun\"`) is not provisioned on this server.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/gameActions.do": {
   "post": {
    "operationId": "gameActions",
    "tags": [
     "free spins",
     "target outcomes",
     "rounds",
     "reporting"
    ],
    "summary": "Operator free spins, target outcomes, round cancel and reports",
    "description": "The `method` field selects the operation. Free spins: freespins_create, freespins_get, freespins_list, freespins_cancel. Target outcomes: outcome_schedule, outcome_get, outcome_list, outcome_cancel. Rounds: round_cancel (cancel an unfinished round and refund its debit). Read-only reports: report_rounds, report_ggr, report_reconciliation. Rate limits per partner: 429 RATE_LIMITED with Retry-After (reports have their own, lower quota). The legacy methods freespins_set / freespins_delete (and freespins_get without an id) answer METHOD_RETIRED. Reports are not idempotent-key aware. Malformed JSON answers 400 REQUEST_FAILED.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "description": "1-128 printable ASCII characters. Honoured by freespins_*, outcome_* and round_cancel (report_* ignore it). The key is scoped to your project and this endpoint: the same key with another body (any method) answers IDEMPOTENCY_CONFLICT (409).",
      "schema": {
       "type": "string",
       "minLength": 1,
       "maxLength": 128,
       "pattern": "^[\\x21-\\x7e]+$"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "oneOf": [
         {
          "$ref": "#/components/schemas/FreeSpinsCreate"
         },
         {
          "$ref": "#/components/schemas/FreeSpinsLookup"
         },
         {
          "$ref": "#/components/schemas/FreeSpinsList"
         },
         {
          "$ref": "#/components/schemas/OutcomeSchedule"
         },
         {
          "$ref": "#/components/schemas/OutcomeLookup"
         },
         {
          "$ref": "#/components/schemas/OutcomeList"
         },
         {
          "$ref": "#/components/schemas/RoundCancel"
         },
         {
          "$ref": "#/components/schemas/ReportRounds"
         },
         {
          "$ref": "#/components/schemas/ReportGgr"
         },
         {
          "$ref": "#/components/schemas/ReportReconciliation"
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "`{status:\"ok\", grant}`, `{status:\"ok\", grants:[...]}`, `{status:\"ok\", outcome}`, `{status:\"ok\", outcomes:[...]}`, `{status:\"ok\", round}`, `{status:\"ok\", rounds, next_cursor}`, `{status:\"ok\", period, group_by, rows, note}` or `{status:\"ok\", players, next_cursor, note}`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "status"
         ],
         "properties": {
          "status": {
           "type": "string",
           "const": "ok"
          },
          "grant": {
           "$ref": "#/components/schemas/FreeSpinGrant"
          },
          "grants": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/FreeSpinGrant"
           }
          },
          "outcome": {
           "$ref": "#/components/schemas/TargetOutcome"
          },
          "outcomes": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/TargetOutcome"
           }
          },
          "round": {
           "$ref": "#/components/schemas/RoundCancelResult"
          },
          "rounds": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/RoundReport"
           }
          },
          "next_cursor": {
           "type": [
            "string",
            "null"
           ]
          },
          "rows": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/GgrRow"
           },
           "description": "report_ggr: at most 20000 rows."
          },
          "players": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ReconciliationRow"
           }
          },
          "period": {
           "type": "object",
           "properties": {
            "from": {
             "type": "string"
            },
            "to": {
             "type": "string"
            }
           },
           "description": "report_ggr: the reported period."
          },
          "group_by": {
           "type": "array",
           "items": {
            "type": "string",
            "enum": [
             "day",
             "symbol",
             "currency"
            ]
           }
          },
          "note": {
           "type": "string",
           "description": "report_ggr / report_reconciliation: how the figures are computed."
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "INVALID_REQUEST, INVALID_METHOD, METHOD_RETIRED, INVALID_IDEMPOTENCY_KEY, INVALID_COUNT, INVALID_FREE_SPIN_BET, INVALID_EXPIRY, FREE_SPINS_UNSUPPORTED, FORCED_OUTCOMES_UNSUPPORTED, GAME_HAS_NO_BONUS, INVALID_KIND, INVALID_MULTIPLIER, MULTIPLIER_ABOVE_MAX_WIN, INVALID_SPIN_NUMBER, PROJECT_BET_LIMIT, PROJECT_GAME_DISABLED, GAME_MODE_DISABLED, CURRENCY_REQUIRED, INVALID_FROM, INVALID_TO, INVALID_PERIOD, INVALID_PLAYER_ID, INVALID_SYMBOL, INVALID_ROUND_ID, INVALID_CURRENCY, INVALID_STATE, INVALID_LIMIT, INVALID_CURSOR, INVALID_GROUP_BY, ROUND_CANCEL_UNAVAILABLE, REQUEST_FAILED",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "UNAUTHORIZED",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "403": {
      "description": "IP_NOT_ALLOWED",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "GAME_NOT_FOUND, GRANT_NOT_FOUND, OUTCOME_NOT_FOUND, ROUND_NOT_FOUND",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "409": {
      "description": "IDEMPOTENCY_CONFLICT, DUPLICATE_EXTERNAL_ID, ROUND_NOT_CANCELLABLE, ROUND_BUSY",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "RATE_LIMITED: per-partner quota exceeded. Headers: Retry-After (seconds), X-RateLimit-Limit.",
      "headers": {
       "Retry-After": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/games/health": {
   "get": {
    "operationId": "gamesHealth",
    "tags": [
     "catalog"
    ],
    "summary": "Published games with launch languages and operator capabilities",
    "description": "`games[].game_id` lists the published games with `languages` (standard codes the original client supports), `free_spins` and `target_outcomes` (whether operator free spins / scheduled target outcomes are available for the game).",
    "responses": {
     "200": {
      "description": "Runtime release and games.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/GamesHealth"
        }
       }
      }
     }
    }
   }
  },
  "/games/catalog": {
   "get": {
    "operationId": "gamesCatalog",
    "tags": [
     "catalog"
    ],
    "summary": "Full game catalog for operators: image, category, tags, RTP, volatility, max win",
    "description": "Same fields as `games[]` of /games/health plus `game_id` and `name`. `image` is an absolute URL (null when the game has no card); `volatility` is derived from the calibration statistics (rule: documentation, Catalog); `rtp.default` is the natural RTP in percent, the operator may set 60-100.",
    "responses": {
     "200": {
      "description": "Catalog.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/GamesCatalog"
        }
       }
      }
     }
    }
   }
  },
  "/capabilities": {
   "get": {
    "operationId": "capabilities",
    "tags": [
     "catalog"
    ],
    "summary": "Casino capability list (stable contract of the testcasino integration)",
    "description": "All released games with their currencies, launch languages restricted to ru / en / uk (the casino languages), default currency and language, promo support and informational mode names. Additive: the catalog fields of /games/catalog on every game, and `callbackSourceIps` (our outgoing wallet-callback IPs) and `rateLimits` at the top level.",
    "responses": {
     "200": {
      "description": "Capabilities.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Capabilities"
        }
       }
      }
     }
    }
   }
  },
  "/health": {
   "get": {
    "operationId": "health",
    "tags": [
     "catalog"
    ],
    "summary": "Runtime liveness",
    "responses": {
     "200": {
      "description": "Database reachable.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "status",
          "mode"
         ],
         "properties": {
          "status": {
           "type": "string",
           "const": "ok"
          },
          "mode": {
           "type": "string",
           "const": "TEST_ONLY"
          },
          "backend": {
           "type": "string"
          },
          "client": {
           "type": "string"
          },
          "mathVersion": {
           "type": "string"
          },
          "runtime": {
           "type": "string"
          },
          "releaseId": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "503": {
      "description": "DATABASE_UNAVAILABLE",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/playerRtp.do": {
   "post": {
    "operationId": "playerRtp",
    "tags": [
     "rtp"
    ],
    "summary": "Personal RTP and bet limits of a player",
    "description": "Actions `get`, `set`, `auto`, `bet-limit`. The player RTP level is 60-100 %. The caller IP must be on the project allowlist. Personal RTP only takes effect while it is enabled for the project. Not rate limited; `Idempotency-Key` is ignored.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/PlayerRtpRequest"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Player RTP state.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "status",
          "player"
         ],
         "properties": {
          "status": {
           "type": "string",
           "const": "ok"
          },
          "player": {
           "$ref": "#/components/schemas/PlayerRtp"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "INVALID_ACTION, INVALID_PLAYER, RTP_LEVEL_INVALID, RTP_LEVEL_OUT_OF_RANGE, RTP_PROVIDER_UNSUPPORTED, RTP_POLICY_REQUIRED, INVALID_REASON, INVALID_CURRENCY, INVALID_BET_LIMIT, UNKNOWN_PROJECT",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "UNAUTHORIZED",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "403": {
      "description": "IP_NOT_ALLOWED",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  }
 },
 "webhooks": {
  "walletCallback": {
   "post": {
    "operationId": "walletCallback",
    "tags": [
     "callbacks"
    ],
    "summary": "Wallet callback sent by the platform to the operator: POST <callbackBaseUrl>/<currency>",
    "description": "Canonical JSON with the project `api_key` in the body, signed with HMAC-SHA256: headers `X-Notell-Timestamp` and `X-Notell-Signature: v1=<hex>` over `<timestamp>\\nPOST\\n<url path>\\n<raw body>`. Our callback IPs: `callbackSourceIps` in /capabilities. 2 s timeout, no redirects. Answer HTTP 2xx with `{status, balance, currency}`; error answers need balance and currency too. Methods: `session_info` (read the balance; sent at launch), `bet` (debit before a paid round), `win` (credit), `refund` (give back `meta.original_transaction`; answer ok also for a transaction you never received), `round_info` (start / finish notifications), `freespins_finished` (one summary per finished grant; answer `{\"status\":\"ok\",\"balance\":...,\"currency\":...}`). A retry carries the same transaction id and body: answer it from your record. BET REFUSAL: answer `{\"status\":\"error\",\"message\":\"INSUFFICIENT_FUNDS\"}` with HTTP 200 to reject a bet. Any HTTP 4xx except 408 and 429 on a bet is a definitive refusal: the round is rejected at once and a `refund` is queued. 5xx, 408, 429, a time-out or an invalid answer are retried with the same transaction (4 immediate attempts, then 1 s doubling to 60 s) until the cancel policy (10 minutes or 600 attempts) cancels the round with a `refund`. FREE SPINS: a bet of a free-spin round has amount 0 and `meta:{is_freespins:true, freespins:{grant_id, external_id, nominal_bet, remaining}}`; accept a zero bet then. Wins of free-spin rounds are held: no `win` is sent per spin. After the last spin one `win` with `meta.freespins.final_credit:true` and `total_win` credits the whole grant, then `freespins_finished` follows (its `total_win` equals that credit).",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "oneOf": [
         {
          "$ref": "#/components/schemas/WalletCallback"
         },
         {
          "$ref": "#/components/schemas/FreeSpinsFinished"
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Operator answer (HTTP 2xx).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/WalletAnswer"
        }
       }
      }
     }
    }
   }
  }
 },
 "components": {
  "schemas": {
   "Error": {
    "type": "object",
    "required": [
     "status",
     "message"
    ],
    "properties": {
     "status": {
      "type": "string",
      "const": "error"
     },
     "message": {
      "type": "string",
      "pattern": "^[A-Z][A-Z0-9_]+$"
     }
    }
   },
   "LaunchRequest": {
    "type": "object",
    "additionalProperties": false,
    "required": [
     "provider",
     "partner",
     "api_key",
     "currency",
     "symbol",
     "gametype"
    ],
    "properties": {
     "provider": {
      "type": "string",
      "const": "own"
     },
     "partner": {
      "type": "string"
     },
     "api_key": {
      "type": "string"
     },
     "player_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128,
      "description": "Required for gametype demo (your wallet player). For gametype fun optional: it only scopes the free-play demo wallet."
     },
     "currency": {
      "type": "string",
      "pattern": "^[A-Z]{3}$",
      "description": "Any of the 157 ISO 4217 fiat codes the platform accepts (see currencies in GET /capabilities). Crypto is rejected. Every currency uses 2-decimal amounts. The currency selects the wallet callback path: POST <callbackBaseUrl>/<currency>."
     },
     "symbol": {
      "type": "string",
      "description": "Game id, see /games/health."
     },
     "gametype": {
      "type": "string",
      "enum": [
       "demo",
       "fun"
      ],
      "description": "demo = the game plays against YOUR wallet callbacks (as before). fun = free-play DEMO: an isolated demo balance, no wallet callback, no GGR; see demo_balance."
     },
     "demo_balance": {
      "description": "gametype fun only: starting demo balance in the session currency, 1.00 .. 10000000.00 (number or decimal string, max 2 decimals). Default 100000.00. Applied when the demo wallet is created (a new launch without player_id is always a new wallet); with player_id the balance persists across launches.",
      "oneOf": [
       {
        "type": "number"
       },
       {
        "type": "string"
       }
      ]
     },
     "demo_reset": {
      "type": "boolean",
      "description": "gametype fun with player_id only: refill that player's demo wallet to demo_balance (default 100000.00) at this launch (ignored while a round of the player is unfinished). A wallet below 1.00 is refilled automatically."
     },
     "lang": {
      "type": "string",
      "description": "Standard language code (BCP 47)."
     },
     "client_platform": {
      "type": "string",
      "enum": [
       "desktop",
       "mobile"
      ],
      "description": "Pragmatic clients only."
     },
     "region": {
      "type": "string",
      "enum": [
       "RU",
       "NONE"
      ]
     }
    }
   },
   "LaunchResponse": {
    "type": "object",
    "required": [
     "status",
     "link",
     "meta"
    ],
    "properties": {
     "status": {
      "type": "string",
      "const": "ok"
     },
     "link": {
      "type": "string",
      "format": "uri"
     },
     "meta": {
      "type": "object",
      "required": [
       "session_id"
      ],
      "properties": {
       "session_id": {
        "type": "string"
       },
       "currency": {
        "type": "string"
       },
       "language": {
        "type": "string"
       },
       "gametype": {
        "type": "string",
        "const": "fun",
        "description": "Present only for gametype \"fun\"."
       }
      }
     }
    }
   },
   "FreeSpinsCreate": {
    "type": "object",
    "additionalProperties": false,
    "required": [
     "method",
     "partner",
     "api_key",
     "player_id",
     "currency",
     "symbol",
     "count",
     "bet"
    ],
    "properties": {
     "method": {
      "type": "string",
      "const": "freespins_create"
     },
     "partner": {
      "type": "string"
     },
     "api_key": {
      "type": "string"
     },
     "player_id": {
      "type": "string"
     },
     "currency": {
      "type": "string",
      "pattern": "^[A-Z]{3}$",
      "description": "Any of the 157 ISO 4217 fiat codes the platform accepts (see currencies in GET /capabilities). Crypto is rejected. Every currency uses 2-decimal amounts. The currency selects the wallet callback path: POST <callbackBaseUrl>/<currency>."
     },
     "symbol": {
      "type": "string"
     },
     "count": {
      "type": "integer",
      "minimum": 1,
      "maximum": 1000
     },
     "bet": {
      "type": "string",
      "pattern": "^[0-9]+(\\.[0-9]+)?$",
      "description": "Total bet of one spin; must be one of the game's own bets in its base mode and within the project's bet limits."
     },
     "expires_at": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 with zone; default now + 7 days, minimum now + 1 hour, maximum now + 30 days."
     },
     "external_id": {
      "type": "string"
     },
     "meta": {
      "type": "object"
     }
    }
   },
   "FreeSpinsLookup": {
    "type": "object",
    "additionalProperties": false,
    "required": [
     "method",
     "partner",
     "api_key"
    ],
    "properties": {
     "partner": {
      "type": "string"
     },
     "api_key": {
      "type": "string"
     },
     "method": {
      "type": "string",
      "enum": [
       "freespins_get",
       "freespins_cancel"
      ]
     },
     "grant_id": {
      "type": "string"
     },
     "external_id": {
      "type": "string"
     }
    },
    "description": "freespins_get / freespins_cancel (idempotent): grant_id or external_id."
   },
   "FreeSpinsList": {
    "type": "object",
    "additionalProperties": false,
    "required": [
     "method",
     "partner",
     "api_key"
    ],
    "properties": {
     "partner": {
      "type": "string"
     },
     "api_key": {
      "type": "string"
     },
     "method": {
      "type": "string",
      "const": "freespins_list"
     },
     "player_id": {
      "type": "string"
     },
     "symbol": {
      "type": "string"
     },
     "currency": {
      "type": "string",
      "pattern": "^[A-Z]{3}$",
      "description": "Any of the 157 ISO 4217 fiat codes the platform accepts (see currencies in GET /capabilities). Crypto is rejected. Every currency uses 2-decimal amounts. The currency selects the wallet callback path: POST <callbackBaseUrl>/<currency>."
     },
     "status": {
      "type": "string",
      "enum": [
       "active",
       "used",
       "expired",
       "cancelled"
      ]
     }
    },
    "description": "At most 200 grants, newest first."
   },
   "FreeSpinGrant": {
    "type": "object",
    "properties": {
     "grant_id": {
      "type": "string"
     },
     "external_id": {
      "type": "string"
     },
     "player_id": {
      "type": "string"
     },
     "currency": {
      "type": "string"
     },
     "symbol": {
      "type": "string"
     },
     "provider": {
      "type": "string"
     },
     "total": {
      "type": "integer"
     },
     "remaining": {
      "type": "integer"
     },
     "played": {
      "type": "integer"
     },
     "bet": {
      "type": "string",
      "pattern": "^[0-9]+(\\.[0-9]+)?$",
      "description": "Decimal amount as a string."
     },
     "total_win": {
      "type": "string",
      "pattern": "^[0-9]+(\\.[0-9]+)?$",
      "description": "Decimal amount as a string."
     },
     "status": {
      "type": "string",
      "enum": [
       "active",
       "used",
       "expired",
       "cancelled"
      ]
     },
     "created_at": {
      "type": "string",
      "format": "date-time"
     },
     "expires_at": {
      "type": "string",
      "format": "date-time"
     },
     "finished_at": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time"
     }
    }
   },
   "OutcomeSchedule": {
    "type": "object",
    "additionalProperties": false,
    "required": [
     "method",
     "partner",
     "api_key",
     "player_id",
     "symbol",
     "multiplier"
    ],
    "properties": {
     "method": {
      "type": "string",
      "const": "outcome_schedule"
     },
     "partner": {
      "type": "string"
     },
     "api_key": {
      "type": "string"
     },
     "player_id": {
      "type": "string"
     },
     "symbol": {
      "type": "string"
     },
     "multiplier": {
      "type": [
       "string",
       "number"
      ],
      "description": "Win as a multiple of the bet: positive, up to 2 decimals (send a decimal string). Tolerance +-2 %."
     },
     "kind": {
      "type": "string",
      "enum": [
       "spin",
       "bonus"
      ]
     },
     "spin_number": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100000,
      "default": 1,
      "description": "The player's n-th paid spin in that game, counted from the moment the entry becomes `ready`; free-spin rounds do not count."
     },
     "expires_at": {
      "type": "string",
      "format": "date-time",
      "description": "Default 7 days, 1 hour - 90 days."
     },
     "external_id": {
      "type": "string"
     },
     "comment": {
      "type": "string"
     }
    }
   },
   "OutcomeLookup": {
    "type": "object",
    "additionalProperties": false,
    "required": [
     "method",
     "partner",
     "api_key"
    ],
    "properties": {
     "partner": {
      "type": "string"
     },
     "api_key": {
      "type": "string"
     },
     "method": {
      "type": "string",
      "enum": [
       "outcome_get",
       "outcome_cancel"
      ]
     },
     "outcome_id": {
      "type": "string"
     },
     "external_id": {
      "type": "string"
     }
    }
   },
   "OutcomeList": {
    "type": "object",
    "additionalProperties": false,
    "required": [
     "method",
     "partner",
     "api_key"
    ],
    "properties": {
     "partner": {
      "type": "string"
     },
     "api_key": {
      "type": "string"
     },
     "method": {
      "type": "string",
      "const": "outcome_list"
     },
     "player_id": {
      "type": "string"
     },
     "symbol": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "pending",
       "ready",
       "unreachable",
       "applied",
       "expired",
       "cancelled"
      ]
     }
    },
    "description": "At most 200 outcomes."
   },
   "TargetOutcome": {
    "type": "object",
    "properties": {
     "outcome_id": {
      "type": "string"
     },
     "external_id": {
      "type": "string"
     },
     "player_id": {
      "type": "string"
     },
     "symbol": {
      "type": "string"
     },
     "provider": {
      "type": "string"
     },
     "kind": {
      "type": "string",
      "enum": [
       "spin",
       "bonus"
      ]
     },
     "multiplier": {
      "type": "string",
      "description": "Win multiple of the bet as a string, for example \"50\"."
     },
     "spin_number": {
      "type": "integer"
     },
     "paid_spins_since": {
      "type": "integer"
     },
     "status": {
      "type": "string",
      "enum": [
       "pending",
       "ready",
       "unreachable",
       "applied",
       "expired",
       "cancelled"
      ]
     },
     "variants": {},
     "comment": {
      "type": "string"
     },
     "created_at": {
      "type": "string",
      "format": "date-time"
     },
     "expires_at": {
      "type": "string",
      "format": "date-time"
     },
     "applied_at": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time"
     },
     "round_id": {
      "type": [
       "string",
       "null"
      ]
     }
    }
   },
   "RoundCancel": {
    "type": "object",
    "additionalProperties": false,
    "required": [
     "method",
     "partner",
     "api_key",
     "round_id"
    ],
    "properties": {
     "method": {
      "type": "string",
      "const": "round_cancel"
     },
     "partner": {
      "type": "string"
     },
     "api_key": {
      "type": "string"
     },
     "round_id": {
      "type": "string"
     }
    },
    "description": "Cancels an unfinished round (debit not confirmed, or confirmed but result not shown and no win planned) and sends a refund. Idempotent per round."
   },
   "RoundCancelResult": {
    "type": "object",
    "properties": {
     "round_id": {
      "type": "string"
     },
     "state": {
      "type": "string",
      "const": "cancelled"
     },
     "result": {
      "type": "string",
      "enum": [
       "cancelled",
       "already_cancelled"
      ]
     },
     "reason": {
      "type": "string",
      "enum": [
       "wallet_unconfirmed",
       "operator_cancel"
      ]
     },
     "source": {
      "type": "string",
      "enum": [
       "worker",
       "operator"
      ]
     },
     "refund": {
      "type": "object",
      "properties": {
       "transaction": {
        "type": [
         "string",
         "null"
        ]
       },
       "amount": {
        "type": [
         "string",
         "null"
        ]
       },
       "status": {
        "type": "string",
        "enum": [
         "pending",
         "confirmed"
        ]
       }
      }
     }
    }
   },
   "ReportRounds": {
    "type": "object",
    "additionalProperties": false,
    "required": [
     "method",
     "partner",
     "api_key"
    ],
    "properties": {
     "partner": {
      "type": "string"
     },
     "api_key": {
      "type": "string"
     },
     "method": {
      "type": "string",
      "const": "report_rounds"
     },
     "player_id": {
      "type": "string"
     },
     "symbol": {
      "type": "string"
     },
     "currency": {
      "type": "string",
      "pattern": "^[A-Z]{3}$",
      "description": "Any of the 157 ISO 4217 fiat codes the platform accepts (see currencies in GET /capabilities). Crypto is rejected. Every currency uses 2-decimal amounts. The currency selects the wallet callback path: POST <callbackBaseUrl>/<currency>."
     },
     "round_id": {
      "type": "string"
     },
     "state": {
      "type": "string",
      "enum": [
       "settled",
       "pending",
       "cancelled",
       "rejected"
      ]
     },
     "from": {
      "type": "string",
      "description": "ISO 8601 date or date-time WITH a zone (Z or +hh:mm), inclusive."
     },
     "to": {
      "type": "string",
      "description": "Exclusive. Same format as from."
     },
     "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 500,
      "default": 100
     },
     "cursor": {
      "type": "string"
     }
    },
    "description": "Round history, newest first; pass next_cursor as cursor for the next page."
   },
   "RoundReport": {
    "type": "object",
    "properties": {
     "round_id": {
      "type": "string"
     },
     "player_id": {
      "type": "string"
     },
     "symbol": {
      "type": "string"
     },
     "currency": {
      "type": "string"
     },
     "mode": {
      "type": "string"
     },
     "state": {
      "type": "string",
      "enum": [
       "settled",
       "debit_pending",
       "in_progress",
       "credit_pending",
       "rejected",
       "cancelled",
       "reconciliation_required"
      ]
     },
     "free_spin": {
      "type": "boolean"
     },
     "bet": {
      "$ref": "#/components/schemas/OperationAmount"
     },
     "win": {
      "$ref": "#/components/schemas/OperationAmount"
     },
     "refund": {
      "$ref": "#/components/schemas/OperationAmount"
     },
     "cancel_reason": {
      "type": [
       "string",
       "null"
      ]
     },
     "created_at": {
      "type": "string",
      "format": "date-time"
     },
     "settled_at": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time"
     }
    }
   },
   "OperationAmount": {
    "type": "object",
    "properties": {
     "amount": {
      "type": [
       "string",
       "null"
      ]
     },
     "status": {
      "type": [
       "string",
       "null"
      ],
      "enum": [
       "planned",
       "sent",
       "unknown",
       "confirmed",
       "rejected",
       null
      ]
     }
    }
   },
   "ReportGgr": {
    "type": "object",
    "additionalProperties": false,
    "required": [
     "method",
     "partner",
     "api_key"
    ],
    "properties": {
     "partner": {
      "type": "string"
     },
     "api_key": {
      "type": "string"
     },
     "method": {
      "type": "string",
      "const": "report_ggr"
     },
     "from": {
      "type": "string"
     },
     "to": {
      "type": "string"
     },
     "group_by": {
      "type": "array",
      "items": {
       "type": "string",
       "enum": [
        "day",
        "symbol",
        "currency"
       ]
      }
     },
     "symbol": {
      "type": "string"
     },
     "currency": {
      "type": "string",
      "pattern": "^[A-Z]{3}$",
      "description": "Any of the 157 ISO 4217 fiat codes the platform accepts (see currencies in GET /capabilities). Crypto is rejected. Every currency uses 2-decimal amounts. The currency selects the wallet callback path: POST <callbackBaseUrl>/<currency>."
     }
    },
    "description": "GGR = bets - refunds - wins over confirmed wallet operations, per day / game / currency (default all three; at most 366 days, default last 31)."
   },
   "ReportReconciliation": {
    "type": "object",
    "additionalProperties": false,
    "required": [
     "method",
     "partner",
     "api_key"
    ],
    "properties": {
     "partner": {
      "type": "string"
     },
     "api_key": {
      "type": "string"
     },
     "method": {
      "type": "string",
      "const": "report_reconciliation"
     },
     "player_id": {
      "type": "string"
     },
     "currency": {
      "type": "string",
      "pattern": "^[A-Z]{3}$",
      "description": "Any of the 157 ISO 4217 fiat codes the platform accepts (see currencies in GET /capabilities). Crypto is rejected. Every currency uses 2-decimal amounts. The currency selects the wallet callback path: POST <callbackBaseUrl>/<currency>."
     },
     "from": {
      "type": "string"
     },
     "to": {
      "type": "string"
     },
     "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 1000,
      "default": 200
     },
     "cursor": {
      "type": "string"
     }
    },
    "description": "Per player and currency totals of the confirmed wallet operations (bets, wins, refunds) and the pending ones."
   },
   "ReconciliationRow": {
    "type": "object",
    "properties": {
     "player_id": {
      "type": "string"
     },
     "currency": {
      "type": "string"
     },
     "bets": {
      "type": "string"
     },
     "wins": {
      "type": "string"
     },
     "refunds": {
      "type": "string"
     },
     "net_wallet_change": {
      "type": "string"
     },
     "bet_count": {
      "type": "integer"
     },
     "win_count": {
      "type": "integer"
     },
     "refund_count": {
      "type": "integer"
     },
     "pending": {
      "type": "object",
      "properties": {
       "operations": {
        "type": "integer"
       },
       "refunds": {
        "type": "integer"
       },
       "wins": {
        "type": "integer"
       }
      }
     }
    }
   },
   "CatalogEnrichment": {
    "type": "object",
    "properties": {
     "image": {
      "type": [
       "string",
       "null"
      ],
      "format": "uri"
     },
     "category": {
      "type": "string"
     },
     "tags": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "rtp": {
      "type": "object",
      "properties": {
       "default": {
        "type": [
         "number",
         "null"
        ]
       },
       "min": {
        "type": [
         "number",
         "null"
        ]
       },
       "max": {
        "type": [
         "number",
         "null"
        ]
       },
       "configurable": {
        "type": "boolean"
       },
       "by_mode": {
        "type": "object"
       },
       "note": {
        "type": "string"
       }
      }
     },
     "volatility": {
      "type": [
       "string",
       "null"
      ],
      "enum": [
       "low",
       "medium",
       "high",
       "very-high",
       null
      ]
     },
     "volatility_basis": {
      "type": [
       "string",
       "null"
      ]
     },
     "hit_frequency": {
      "type": [
       "number",
       "null"
      ]
     },
     "max_win": {
      "type": [
       "number",
       "null"
      ],
      "description": "Largest win as a multiple of the bet, when known."
     },
     "features": {
      "type": "object",
      "properties": {
       "buy_feature": {
        "type": "boolean"
       },
       "ante_bet": {
        "type": "boolean"
       },
       "free_spins": {
        "type": "boolean"
       },
       "target_outcomes": {
        "type": "boolean"
       }
      }
     }
    }
   },
   "GamesCatalog": {
    "type": "object",
    "required": [
     "status",
     "games"
    ],
    "properties": {
     "status": {
      "type": "string",
      "const": "ok"
     },
     "releaseId": {
      "type": "string"
     },
     "games": {
      "type": "array",
      "items": {
       "allOf": [
        {
         "$ref": "#/components/schemas/CatalogEnrichment"
        },
        {
         "type": "object",
         "required": [
          "game_id",
          "name",
          "provider"
         ],
         "properties": {
          "game_id": {
           "type": "string"
          },
          "name": {
           "type": "string"
          },
          "provider": {
           "type": "string"
          },
          "languages": {
           "type": "array",
           "items": {
            "type": "string"
           }
          }
         }
        }
       ]
      }
     }
    }
   },
   "GamesHealth": {
    "type": "object",
    "required": [
     "status",
     "releaseId",
     "games"
    ],
    "properties": {
     "status": {
      "type": "string",
      "const": "ok"
     },
     "releaseId": {
      "type": "string"
     },
     "databasePurpose": {
      "type": "string"
     },
     "games": {
      "type": "array",
      "items": {
       "type": "object",
       "required": [
        "game_id"
       ],
       "properties": {
        "game_id": {
         "type": "string"
        },
        "languages": {
         "type": "array",
         "items": {
          "type": "string"
         }
        },
        "free_spins": {
         "type": "boolean"
        },
        "target_outcomes": {
         "type": "boolean"
        },
        "image": {
         "type": [
          "string",
          "null"
         ]
        },
        "category": {
         "type": "string"
        },
        "tags": {
         "type": "array",
         "items": {
          "type": "string"
         }
        },
        "rtp": {
         "type": "object"
        },
        "volatility": {
         "type": [
          "string",
          "null"
         ]
        },
        "max_win": {
         "type": [
          "number",
          "null"
         ]
        },
        "features": {
         "type": "object"
        }
       }
      }
     }
    }
   },
   "Capabilities": {
    "type": "object",
    "required": [
     "status",
     "mode",
     "currencies",
     "games"
    ],
    "properties": {
     "status": {
      "type": "string",
      "const": "ok"
     },
     "mode": {
      "type": "string",
      "const": "TEST_ONLY"
     },
     "callbackSourceIps": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Outgoing IPs of our wallet callbacks (allowlist them)."
     },
     "rateLimits": {
      "type": "object",
      "description": "Per-partner quotas per minute: launchPerMinute, actionsPerMinute, reportsPerMinute."
     },
     "currencies": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "code": {
         "type": "string",
         "pattern": "^[A-Z]{3}$"
        },
        "precision": {
         "type": "integer",
         "const": 2
        }
       }
      }
     },
     "games": {
      "type": "array",
      "items": {
       "type": "object",
       "required": [
        "gameId",
        "name",
        "provider",
        "currencies",
        "languages",
        "defaultCurrency",
        "defaultLanguage",
        "promoSupported",
        "modes"
       ],
       "properties": {
        "gameId": {
         "type": "string"
        },
        "name": {
         "type": "string"
        },
        "provider": {
         "type": "string"
        },
        "currencies": {
         "type": "array",
         "items": {
          "type": "string"
         }
        },
        "languages": {
         "type": "array",
         "items": {
          "type": "string",
          "enum": [
           "ru",
           "en",
           "uk"
          ]
         }
        },
        "defaultCurrency": {
         "type": "string"
        },
        "defaultLanguage": {
         "type": "string",
         "enum": [
          "ru",
          "en",
          "uk"
         ]
        },
        "promoSupported": {
         "type": "boolean"
        },
        "modes": {
         "type": "array",
         "items": {
          "type": "string"
         }
        },
        "image": {
         "type": [
          "string",
          "null"
         ]
        },
        "category": {
         "type": "string"
        },
        "tags": {
         "type": "array",
         "items": {
          "type": "string"
         }
        },
        "rtp": {
         "type": "object"
        },
        "volatility": {
         "type": [
          "string",
          "null"
         ]
        },
        "max_win": {
         "type": [
          "number",
          "null"
         ]
        },
        "features": {
         "type": "object"
        }
       }
      }
     }
    }
   },
   "WalletCallback": {
    "type": "object",
    "required": [
     "method",
     "player_id",
     "api_key"
    ],
    "description": "Amounts are JSON numbers; the platform writes them with exactly 2 decimals (1.50). Verify the signature over the raw body bytes.",
    "properties": {
     "method": {
      "type": "string",
      "enum": [
       "session_info",
       "bet",
       "win",
       "refund",
       "round_info",
       "freespins_finished"
      ]
     },
     "api_key": {
      "type": "string"
     },
     "player_id": {
      "type": "string"
     },
     "session_id": {
      "type": "string",
      "description": "session_info."
     },
     "amount": {
      "type": "number",
      "description": "bet / win / refund. 0 for a free-spin bet."
     },
     "meta": {
      "type": "object",
      "description": "bet / win / refund.",
      "properties": {
       "symbol": {
        "type": "string"
       },
       "time": {
        "type": "string"
       },
       "transaction": {
        "type": "string",
        "description": "Unique id; a retry repeats it."
       },
       "round_id": {
        "type": "string"
       },
       "session_id": {
        "type": "string"
       },
       "original_transaction": {
        "type": "string",
        "description": "refund: the debit that is given back."
       },
       "reason": {
        "type": "string",
        "enum": [
         "wallet_unconfirmed",
         "operator_cancel"
        ],
        "description": "refund."
       },
       "is_freespins": {
        "type": "boolean"
       },
       "freespins": {
        "type": "object",
        "description": "Free-spin rounds. Per-spin wins are not sent: one win per grant is sent after its last spin with final_credit true and total_win.",
        "properties": {
         "grant_id": {
          "type": "string"
         },
         "external_id": {
          "type": "string"
         },
         "nominal_bet": {
          "type": "string",
          "pattern": "^[0-9]+(\\.[0-9]{2})?$",
          "description": "Decimal amount as a string with 2 decimals, for example \"10.50\"."
         },
         "remaining": {
          "type": "integer"
         },
         "total_win": {
          "type": "string",
          "pattern": "^[0-9]+(\\.[0-9]{2})?$",
          "description": "final win only: the whole grant win."
         },
         "final_credit": {
          "type": "boolean",
          "description": "true on the single win that credits the whole grant."
         }
        }
       }
      }
     },
     "status": {
      "type": "string",
      "enum": [
       "start",
       "finish"
      ],
      "description": "round_info."
     },
     "round_id": {
      "type": "string",
      "description": "round_info."
     },
     "game_symbol": {
      "type": "string",
      "description": "round_info."
     },
     "currency": {
      "type": "string",
      "description": "round_info."
     },
     "timestamp": {
      "type": "string",
      "description": "round_info."
     },
     "bet_amount": {
      "type": "number",
      "description": "round_info."
     },
     "bet_type": {
      "type": "string",
      "description": "round_info: spin, purchase, ante or freespin, depending on the provider."
     },
     "bonus_buy": {
      "type": "boolean",
      "description": "round_info."
     },
     "bonus_trigger": {
      "type": "boolean",
      "description": "round_info."
     },
     "win_amount": {
      "type": "number",
      "description": "round_info finish."
     },
     "multiplier": {
      "type": "number",
      "description": "round_info finish."
     },
     "has_win": {
      "type": "boolean",
      "description": "round_info finish."
     },
     "forced": {
      "type": "boolean",
      "description": "round_info: true for a round played from a target outcome."
     }
    }
   },
   "FreeSpinsFinished": {
    "type": "object",
    "required": [
     "method",
     "grant_id",
     "player_id",
     "currency",
     "game",
     "status"
    ],
    "properties": {
     "method": {
      "type": "string",
      "const": "freespins_finished"
     },
     "grant_id": {
      "type": "string"
     },
     "external_id": {
      "type": "string"
     },
     "player_id": {
      "type": "string"
     },
     "currency": {
      "type": "string"
     },
     "game": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "used",
       "expired",
       "cancelled"
      ]
     },
     "total": {
      "type": "integer"
     },
     "played": {
      "type": "integer"
     },
     "bet": {
      "type": "string",
      "pattern": "^[0-9]+(\\.[0-9]+)?$",
      "description": "Decimal amount as a string."
     },
     "total_win": {
      "type": "string",
      "pattern": "^[0-9]+(\\.[0-9]+)?$",
      "description": "Decimal amount as a string."
     },
     "finished_at": {
      "type": "string",
      "format": "date-time"
     }
    }
   },
   "WalletAnswer": {
    "type": "object",
    "required": [
     "status",
     "balance",
     "currency"
    ],
    "description": "Both answers, ok and error, must carry a numeric balance and the currency of the callback path; otherwise the platform treats the wallet as broken (INVALID_CALLBACK_RESPONSE), not as a refusal.",
    "properties": {
     "status": {
      "type": "string",
      "enum": [
       "ok",
       "error"
      ]
     },
     "balance": {
      "type": "number",
      "minimum": 0,
      "description": "Plain non-negative JSON number with at most 2 decimals (no exponent, no sign)."
     },
     "currency": {
      "type": "string",
      "description": "Must equal the currency of the callback URL path."
     },
     "message": {
      "type": "string",
      "description": "With status \"error\": for example INSUFFICIENT_FUNDS."
     }
    }
   },
   "GgrRow": {
    "type": "object",
    "description": "One report_ggr row. day / symbol / currency are present according to group_by. ggr = bets - refunds - wins.",
    "properties": {
     "day": {
      "type": "string",
      "format": "date"
     },
     "symbol": {
      "type": "string"
     },
     "currency": {
      "type": "string"
     },
     "rounds": {
      "type": "integer"
     },
     "cancelled_rounds": {
      "type": "integer"
     },
     "free_spin_rounds": {
      "type": "integer"
     },
     "bets": {
      "type": "string",
      "pattern": "^[0-9]+(\\.[0-9]{2})?$",
      "description": "Decimal amount as a string with 2 decimals, for example \"10.50\"."
     },
     "wins": {
      "type": "string",
      "pattern": "^[0-9]+(\\.[0-9]{2})?$",
      "description": "Decimal amount as a string with 2 decimals, for example \"10.50\"."
     },
     "refunds": {
      "type": "string",
      "pattern": "^[0-9]+(\\.[0-9]{2})?$",
      "description": "Decimal amount as a string with 2 decimals, for example \"10.50\"."
     },
     "ggr": {
      "type": "string",
      "pattern": "^[0-9]+(\\.[0-9]{2})?$",
      "description": "Decimal amount as a string with 2 decimals, for example \"10.50\"."
     },
     "pending_operations": {
      "type": "integer"
     }
    }
   },
   "PlayerRtpRequest": {
    "type": "object",
    "required": [
     "partner",
     "api_key",
     "action",
     "player_id"
    ],
    "properties": {
     "partner": {
      "type": "string"
     },
     "api_key": {
      "type": "string"
     },
     "action": {
      "type": "string",
      "enum": [
       "get",
       "set",
       "auto",
       "bet-limit"
      ]
     },
     "player_id": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_.:@-]{1,128}$"
     },
     "provider": {
      "type": "string",
      "enum": [
       "endorphina",
       "pragmatic",
       "nolimitcity"
      ],
      "default": "endorphina",
      "description": "The provider the action applies to."
     },
     "rtp": {
      "type": "number",
      "minimum": 60,
      "maximum": 100,
      "description": "set: a JSON number, percent with at most 2 decimals, e.g. 94.5."
     },
     "reason": {
      "type": "string",
      "maxLength": 200,
      "description": "set / auto: shown in the player journal."
     },
     "currency": {
      "type": "string",
      "pattern": "^[A-Z]{3}$",
      "description": "bet-limit: currency code."
     },
     "min_minor": {
      "type": [
       "string",
       "null"
      ],
      "pattern": "^[0-9]{1,18}$",
      "description": "bet-limit: minimum bet per spin in minor units (at least 1); null together with max_minor removes the limit."
     },
     "max_minor": {
      "type": [
       "string",
       "null"
      ],
      "pattern": "^[0-9]{1,18}$",
      "description": "bet-limit: maximum bet per spin in minor units."
     }
    }
   },
   "PlayerRtp": {
    "type": "object",
    "properties": {
     "partner": {
      "type": "string"
     },
     "player": {
      "type": "string"
     },
     "nickname": {
      "type": [
       "string",
       "null"
      ]
     },
     "bound": {
      "type": "boolean"
     },
     "provider": {
      "type": "string"
     },
     "startBps": {
      "type": "integer",
      "description": "Starting RTP in basis points (9450 = 94.50 %)."
     },
     "currentBps": {
      "type": "integer",
      "description": "Current RTP in basis points."
     },
     "mode": {
      "type": "string",
      "enum": [
       "auto",
       "manual"
      ]
     },
     "roundsSinceAdjust": {
      "type": "integer"
     },
     "providerBps": {
      "type": [
       "integer",
       "null"
      ],
      "description": "Project provider RTP in basis points."
     },
     "effective": {
      "type": [
       "object",
       "null"
      ],
      "properties": {
       "bps": {
        "type": "integer"
       },
       "source": {
        "type": "string",
        "enum": [
         "player",
         "provider-cap",
         "manual"
        ]
       }
      }
     },
     "betLimits": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "currency": {
         "type": "string"
        },
        "minMinor": {
         "type": "string"
        },
        "maxMinor": {
         "type": "string"
        }
       }
      }
     },
     "journal": {
      "type": "array",
      "items": {
       "type": "object"
      },
      "description": "At most 100 entries."
     },
     "providers": {
      "type": "array",
      "items": {
       "type": "object"
      },
      "description": "Per-provider rows."
     }
    }
   }
  }
 }
}
