{
  "openapi": "3.1.0",
  "info": {
    "title": "Slots TEST_ONLY operator API (runtime 4512)",
    "version": "2.0.0",
    "description": "TEST_ONLY platform: own math, 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) and the caller IP must be on the partner allowlist configured in the admin. Narrative document: /docs/operator-api. Errors are `{\"status\":\"error\",\"message\":\"<CODE>\"}`."
  },
  "servers": [
    {
      "url": "https://notell-api.com",
      "description": "Public beta API; partner + api_key, no HTTP Basic Auth."
    }
  ],
  "tags": [
    {
      "name": "launch"
    },
    {
      "name": "free spins"
    },
    {
      "name": "target outcomes"
    },
    {
      "name": "rounds"
    },
    {
      "name": "reporting"
    },
    {
      "name": "catalog"
    },
    {
      "name": "callbacks"
    }
  ],
  "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 (409).",
            "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, UNSUPPORTED_LANGUAGE, REAL_MODE_DISABLED, PROMO_UNSUPPORTED, ROUND_IN_PROGRESS, INVALID_IDEMPOTENCY_KEY",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHORIZED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "UNSUPPORTED_GAME",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "IDEMPOTENCY_CONFLICT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "CLIENT_FILES_REQUIRED",
            "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.",
        "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 (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}` or `{status:\"ok\", outcomes:[...]}`.",
            "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": {
                        "type": "object"
                      }
                    },
                    "players": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ReconciliationRow"
                      }
                    },
                    "period": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST, METHOD_RETIRED, INVALID_FREE_SPIN_BET, PROJECT_BET_LIMIT, INVALID_EXPIRY, GAME_HAS_NO_BONUS, INVALID_MULTIPLIER, MULTIPLIER_ABOVE_MAX_WIN, INVALID_SPIN_NUMBER, INVALID_KIND, FORCED_OUTCOMES_UNSUPPORTED",
            "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"
                }
              }
            }
          }
        }
      }
    }
  },
  "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 partner `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>` (secret: documentation, Callback signature). Our callback IPs: `callbackSourceIps` in /capabilities. 2 s timeout; the answer must be exact JSON `{status, balance, currency}` where `balance` is a JSON number. Methods: `session_info` (read the balance), `bet` (debit before a paid round), `win` (credit of a positive result), `refund` (give back the debit of `meta.original_transaction`: sent when a bet stays unconfirmed beyond the cancel policy or the operator cancels the round; same transaction id on every retry; answer ok also for a transaction you never received and then refuse that bet), `round_info` (start / finish notifications), `freespins_finished` (one summary per finished free-spins grant: grant_id, external_id, player_id, currency, game, status, total, played, bet, total_win, finished_at; answer `{status:\"ok\"}`, retried with backoff until then). A retry carries the same transaction id and body, answer it from your record. Bets of free-spin rounds have amount 0 and `meta:{is_freespins:true, freespins:{grant_id, external_id, nominal_bet, remaining}}` (wins carry the same meta): accept a zero bet when `is_freespins` is true. One `freespins_finished` summary per finished grant is retried until you answer `{\"status\":\"ok\"}`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/WalletCallback"
                  },
                  {
                    "$ref": "#/components/schemas/FreeSpinsFinished"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Operator answer.",
            "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",
            "enum": [
              "UAH",
              "USD",
              "EUR",
              "RUB"
            ]
          },
          "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"
              }
            }
          }
        }
      },
      "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"
          },
          "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"
          },
          "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": [
              "number",
              "string"
            ],
            "description": "Win as a multiple of the spin's bet (+-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 scheduling; 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": "number"
          },
          "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"
          },
          "round_id": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "settled",
              "pending",
              "cancelled",
              "rejected"
            ]
          },
          "from": {
            "type": "string",
            "description": "ISO 8601 date or date-time (UTC), inclusive."
          },
          "to": {
            "type": "string",
            "description": "Exclusive."
          },
          "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"
          }
        },
        "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"
          },
          "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"
          }
        }
      },
      "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",
                  "enum": [
                    "UAH",
                    "USD",
                    "EUR",
                    "RUB"
                  ]
                },
                "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"
        ],
        "properties": {
          "method": {
            "type": "string",
            "enum": [
              "session_info",
              "bet",
              "win",
              "refund",
              "round_info",
              "freespins_finished"
            ]
          },
          "api_key": {
            "type": "string"
          },
          "player_id": {
            "type": "string"
          },
          "amount": {
            "type": "number",
            "description": "JSON number (not a string); 0 for a free-spin bet."
          },
          "meta": {
            "type": "object",
            "description": "symbol, time, transaction, round_id, session_id; free-spin rounds add is_freespins and freespins; refund adds original_transaction and reason (wallet_unconfirmed | operator_cancel)."
          }
        }
      },
      "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"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "error"
            ]
          },
          "balance": {
            "type": "number",
            "description": "JSON number."
          },
          "currency": {
            "type": "string"
          },
          "message": {
            "type": "string",
            "description": "e.g. INSUFFICIENT_FUNDS, IDEMPOTENCY_CONFLICT."
          }
        }
      }
    }
  }
}