{
  "openapi": "3.1.1",
  "info": {
    "title": "Fairly API",
    "description": "Real prices for PC games, including checkout fees, and the cheapest way to play. Data is licensed CC BY 4.0, attribution: \"Fairly\"; game cover images are not covered by that license. Versioning and deprecation policy: https://fairly.gg/developers#versioning. Some store links are affiliate links; Fairly earns a commission on purchases and the price you pay does not change. Ranking is by price, never by commission. Append `?raw=1` to a `/go/{offer_id}` link for the plain store link.",
    "version": "v1"
  },
  "servers": [
    {
      "url": "https://api.fairly.gg/"
    }
  ],
  "paths": {
    "/v1/games": {
      "get": {
        "tags": [
          "Games"
        ],
        "summary": "Search games by title",
        "description": "Title search (pg_trgm), first 20 results.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfIReadOnlyListOfGameSummary"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          },
          { }
        ]
      }
    },
    "/v1/games/index": {
      "get": {
        "tags": [
          "Games"
        ],
        "summary": "Indexable games for the sitemap",
        "description": "Games with at least one active offer or active subscription entry, ordered by id. `id` is the URL id (`730` or `igdb:123`); `updated_at` is the latest offer fetch or subscription addition (sitemap `lastmod`). `page_size` defaults to 1000, max 5000.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page number from 1 (at most 1000).",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "description": "Rows per page, 1-5000. Default 1000.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfGameIndexPage"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/games/{id}": {
      "get": {
        "tags": [
          "Games"
        ],
        "summary": "Game with offers, subscriptions, cheapest way to play and historical lows",
        "description": "`id` is a Steam AppID (`730`) or an IGDB id (`igdb:123`). `time_to_beat_minutes` is the main story length (IGDB time to beat, hastily); null when unknown, for free-to-play, multiplayer-only and live-service games, or outside 2-100 hours. `is_free_to_play` marks games that cost nothing to start: they have no price and are left out of price lists. SteamSpy playtime is never used as main story. `other_region_offers` lists offers measured in other regions for editions with at most one offer measured in the visitor region; they are labelled with `measured_in` and never enter `cheapest_way` or the ranking.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owned",
            "in": "query",
            "description": "Comma-separated subscription codes the caller already owns (e.g. `gp_pc,psplus_extra`). An owned subscription costs nothing, so a game in its catalog is \"already included\" instead of a buy recommendation. Nothing is stored. Unknown codes are rejected.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfGameDetailModel"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          },
          { }
        ]
      }
    },
    "/v1/games/{id}/history": {
      "get": {
        "tags": [
          "Games"
        ],
        "summary": "Authorized-store price history, daily minimum list price (before store fees)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfIReadOnlyListOfHistoryPoint"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          },
          { }
        ]
      }
    },
    "/v1/discover/under/{amount}": {
      "get": {
        "tags": [
          "Discover"
        ],
        "summary": "Games under $3, $5 or $10 (rounded to a round number in the requested currency)",
        "description": "Parameters: `sort` (default, hours_per_dollar, discount, popularity, reviews), `page` (from 1), `page_size` (1-100, default 50), `min_review` (0-100; 0 turns the filter off). `data` holds `rows`, `total`, `at_low_count`, `best_value`, `page` and `page_size`. On under lists `default` and `hours_per_dollar` order by hours per dollar while any row has a time-to-beat, and by real price (ascending) when none does. `min_review` defaults to 75 here.",
        "parameters": [
          {
            "name": "amount",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "default, hours_per_dollar, discount, popularity or reviews.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number from 1 (at most 1000).",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "description": "Rows per page, 1-100. Default 50.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "min_review",
            "in": "query",
            "description": "Minimum positive review percent, 0-100; 0 turns the filter off. Default 75.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "owned",
            "in": "query",
            "description": "Comma-separated subscription codes the caller already owns (e.g. `gp_pc,psplus_extra`). An owned subscription costs nothing, so a game in its catalog is \"already included\" instead of a buy recommendation. Nothing is stored. Unknown codes are rejected.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "hide_owned",
            "in": "query",
            "description": "With `owned`: true leaves out games that are already included in an owned subscription. Default false.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfDiscoverPageModel"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          },
          { }
        ]
      }
    },
    "/v1/discover/tiers": {
      "get": {
        "tags": [
          "Discover"
        ],
        "summary": "Under tiers ($3, $5, $10) as applied in the requested currency",
        "description": "`data` lists `usd` (3, 5 or 10) and `threshold` (Money: exactly the USD amount in USD; converted from USD and rounded to a round number in other currencies, e.g. TRY 100/200/400). The same amount filters /discover/under/{amount}. `euros` is a deprecated alias of `usd`.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfIReadOnlyListOfDiscoverTierModel"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          },
          { }
        ]
      }
    },
    "/v1/discover/historical-lows": {
      "get": {
        "tags": [
          "Discover"
        ],
        "summary": "Games at their historical low",
        "description": "Parameters: `sort` (default, hours_per_dollar, discount, popularity, reviews), `page` (from 1), `page_size` (1-100, default 50), `min_review` (0-100; 0 turns the filter off). `data` holds `rows`, `total`, `at_low_count`, `best_value`, `page` and `page_size`. On under lists `default` and `hours_per_dollar` order by hours per dollar while any row has a time-to-beat, and by real price (ascending) when none does.",
        "parameters": [
          {
            "name": "sort",
            "in": "query",
            "description": "default, hours_per_dollar, discount, popularity or reviews.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number from 1 (at most 1000).",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "description": "Rows per page, 1-100. Default 50.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "min_review",
            "in": "query",
            "description": "Minimum positive review percent, 0-100. Default: no filter.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "owned",
            "in": "query",
            "description": "Comma-separated subscription codes the caller already owns (e.g. `gp_pc,psplus_extra`). An owned subscription costs nothing, so a game in its catalog is \"already included\" instead of a buy recommendation. Nothing is stored. Unknown codes are rejected.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "hide_owned",
            "in": "query",
            "description": "With `owned`: true leaves out games that are already included in an owned subscription. Default false.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfDiscoverPageModel"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          },
          { }
        ]
      }
    },
    "/v1/discover/leaving/{service}": {
      "get": {
        "tags": [
          "Discover"
        ],
        "summary": "Subscription games leaving within 30 days (game-pass, ps-plus)",
        "description": "Parameters: `sort` (default, hours_per_dollar, discount, popularity, reviews), `page` (from 1), `page_size` (1-100, default 50), `min_review` (0-100; 0 turns the filter off). `data` holds `rows`, `total`, `at_low_count`, `best_value`, `page` and `page_size`. On under lists `default` and `hours_per_dollar` order by hours per dollar while any row has a time-to-beat, and by real price (ascending) when none does.",
        "parameters": [
          {
            "name": "service",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "default, hours_per_dollar, discount, popularity or reviews.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number from 1 (at most 1000).",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "description": "Rows per page, 1-100. Default 50.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "min_review",
            "in": "query",
            "description": "Minimum positive review percent, 0-100. Default: no filter.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "owned",
            "in": "query",
            "description": "Comma-separated subscription codes the caller already owns (e.g. `gp_pc,psplus_extra`). An owned subscription costs nothing, so a game in its catalog is \"already included\" instead of a buy recommendation. Nothing is stored. Unknown codes are rejected.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "hide_owned",
            "in": "query",
            "description": "With `owned`: true leaves out games that are already included in an owned subscription. Default false.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfDiscoverPageModel"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          },
          { }
        ]
      }
    },
    "/v1/discover/play-with/{service}": {
      "get": {
        "tags": [
          "Discover"
        ],
        "summary": "Games you can play with a subscription (game-pass, ps-plus)",
        "description": "Parameters: `sort` (default, hours_per_dollar, discount, popularity, reviews), `page` (from 1), `page_size` (1-100, default 50), `min_review` (0-100; 0 turns the filter off). `data` holds `rows`, `total`, `at_low_count`, `best_value`, `page` and `page_size`. On under lists `default` and `hours_per_dollar` order by hours per dollar while any row has a time-to-beat, and by real price (ascending) when none does.",
        "parameters": [
          {
            "name": "service",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "default, hours_per_dollar, discount, popularity or reviews.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number from 1 (at most 1000).",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "description": "Rows per page, 1-100. Default 50.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "min_review",
            "in": "query",
            "description": "Minimum positive review percent, 0-100. Default: no filter.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "owned",
            "in": "query",
            "description": "Comma-separated subscription codes the caller already owns (e.g. `gp_pc,psplus_extra`). An owned subscription costs nothing, so a game in its catalog is \"already included\" instead of a buy recommendation. Nothing is stored. Unknown codes are rejected.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "hide_owned",
            "in": "query",
            "description": "With `owned`: true leaves out games that are already included in an owned subscription. Default false.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfDiscoverPageModel"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          },
          { }
        ]
      }
    },
    "/v1/discover/deal-of-the-day": {
      "get": {
        "tags": [
          "Discover"
        ],
        "summary": "Deal of the day for the region",
        "description": "One game per region and UTC day, picked on the first request and stored (a game is not picked again for 7 days). Candidates: top-500 popularity, Steam reviews >= 85%, an authorized, fresh, discounted offer measured in the region, real price >= $2 (converted to the requested currency). Score is discount % x ln(1 + 500 / popularity rank). 404 when no game qualifies.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfDealModel"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          },
          { }
        ]
      }
    },
    "/v1/discover/popular": {
      "get": {
        "tags": [
          "Discover"
        ],
        "summary": "Most popular games with a usable offer",
        "description": "Parameters: `sort` (default, hours_per_dollar, discount, popularity, reviews), `page` (from 1), `page_size` (1-100, default 50), `min_review` (0-100; 0 turns the filter off). `data` holds `rows`, `total`, `at_low_count`, `best_value`, `page` and `page_size`. On under lists `default` and `hours_per_dollar` order by hours per dollar while any row has a time-to-beat, and by real price (ascending) when none does. Popularity order (Steam most played, wishlists, owners), free-to-play games excluded, only games with a fresh offer measured in the region. The list has a fixed short length (the home page chips); `page_size` can only shorten it.",
        "parameters": [
          {
            "name": "sort",
            "in": "query",
            "description": "default, hours_per_dollar, discount, popularity or reviews.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number from 1 (at most 1000).",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "description": "Rows per page, 1-100. Default 50.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "min_review",
            "in": "query",
            "description": "Minimum positive review percent, 0-100. Default: no filter.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "owned",
            "in": "query",
            "description": "Comma-separated subscription codes the caller already owns (e.g. `gp_pc,psplus_extra`). An owned subscription costs nothing, so a game in its catalog is \"already included\" instead of a buy recommendation. Nothing is stored. Unknown codes are rejected.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "hide_owned",
            "in": "query",
            "description": "With `owned`: true leaves out games that are already included in an owned subscription. Default false.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfDiscoverPageModel"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          },
          { }
        ]
      }
    },
    "/v1/discover/this-week": {
      "get": {
        "tags": [
          "Discover"
        ],
        "summary": "This week's picks",
        "description": "Parameters: `sort` (default, hours_per_dollar, discount, popularity, reviews), `page` (from 1), `page_size` (1-100, default 50), `min_review` (0-100; 0 turns the filter off). `data` holds `rows`, `total`, `at_low_count`, `best_value`, `page` and `page_size`. On under lists `default` and `hours_per_dollar` order by hours per dollar while any row has a time-to-beat, and by real price (ascending) when none does.",
        "parameters": [
          {
            "name": "sort",
            "in": "query",
            "description": "default, hours_per_dollar, discount, popularity or reviews.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number from 1 (at most 1000).",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "description": "Rows per page, 1-100. Default 50.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "min_review",
            "in": "query",
            "description": "Minimum positive review percent, 0-100. Default: no filter.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "owned",
            "in": "query",
            "description": "Comma-separated subscription codes the caller already owns (e.g. `gp_pc,psplus_extra`). An owned subscription costs nothing, so a game in its catalog is \"already included\" instead of a buy recommendation. Nothing is stored. Unknown codes are rejected.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "hide_owned",
            "in": "query",
            "description": "With `owned`: true leaves out games that are already included in an owned subscription. Default false.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfDiscoverPageModel"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          },
          { }
        ]
      }
    },
    "/v1/subscriptions": {
      "get": {
        "tags": [
          "Subscriptions"
        ],
        "summary": "Subscription summaries by region",
        "description": "Price per month in the region, catalog size (all games we list, the same number the site shows everywhere), new this week and leaving within 30 days.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfIReadOnlyListOfSubscriptionSummaryModel"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          },
          { }
        ]
      }
    },
    "/v1/subscriptions/{code}/catalog": {
      "get": {
        "tags": [
          "Subscriptions"
        ],
        "summary": "Subscription catalog by region",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfIReadOnlyListOfDealModel"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          },
          { }
        ]
      }
    },
    "/v1/stores": {
      "get": {
        "tags": [
          "Stores"
        ],
        "summary": "Stores with type, fee rules and verification dates",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfIReadOnlyListOfStoreView"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          },
          { }
        ]
      }
    },
    "/v1/meta/status": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "Source health: last successful fetch, age, consecutive failures and whether it is overdue (no key needed)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfIReadOnlyListOfSourceStatus"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          },
          { }
        ]
      }
    },
    "/v1/keys": {
      "post": {
        "tags": [
          "Keys"
        ],
        "summary": "Create a developer API key",
        "description": "Returns the key once, in plain text. Only its SHA-256 hash is stored. Limited to 3 keys per IP per day.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "$ref": "#/components/schemas/CreateKeyRequest"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedKeyModel"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/alerts": {
      "post": {
        "tags": [
          "Alerts"
        ],
        "summary": "Create a price alert (double opt-in)",
        "description": "Sends a confirmation email. Always answers 202 for a valid request, whether or not the address is already registered. `game_id` is a Steam AppID or `igdb:N`; `edition` defaults to `standard`; `region` defaults to the region of `currency`. No API key; limited per IP and per address.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AlertRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscribeAccepted"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/alerts/confirm": {
      "get": {
        "tags": [
          "Alerts"
        ],
        "summary": "Confirm a price alert",
        "description": "`token` comes from the confirmation email and works once.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResult"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/newsletter": {
      "post": {
        "tags": [
          "Newsletter"
        ],
        "summary": "Subscribe to the weekly email (double opt-in)",
        "description": "Sends a confirmation email. Always answers 202 for a valid request. No API key; limited per IP and per address.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewsletterRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscribeAccepted"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/newsletter/confirm": {
      "get": {
        "tags": [
          "Newsletter"
        ],
        "summary": "Confirm the weekly email subscription",
        "description": "`token` comes from the confirmation email and works once.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResult"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/unsubscribe/{token}": {
      "get": {
        "tags": [
          "Alerts",
          "Newsletter"
        ],
        "summary": "Unsubscribe from an alert or the weekly email",
        "description": "GET is the human click. POST is RFC 8058 one-click: the body must be `List-Unsubscribe=One-Click` (application/x-www-form-urlencoded). Both delete the subscription and answer 200 for any well-formed token.",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResult"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Alerts",
          "Newsletter"
        ],
        "summary": "Unsubscribe from an alert or the weekly email",
        "description": "GET is the human click. POST is RFC 8058 one-click: the body must be `List-Unsubscribe=One-Click` (application/x-www-form-urlencoded). Both delete the subscription and answer 200 for any well-formed token.",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResult"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/reports": {
      "post": {
        "tags": [
          "Reports"
        ],
        "summary": "Report a wrong price, a wrong match or a dead link",
        "description": "`kind` is wrong_price, wrong_match or dead_link. No API key; limited per IP.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReportRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResult"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/plan": {
      "post": {
        "tags": [
          "Plan"
        ],
        "summary": "Subscription rotation plan: the smartest way to play a list of games",
        "description": "Month-by-month plan for up to 30 games: buy at the cheapest measured real price, or play inside a subscription month while the game is in the catalog. Honors weekly hours (default 10, a subscription month is 4 weeks), announced leave dates and the subscriptions you already own (`owned_subscriptions`, marginal cost 0). Totals compare against buying every game at its cheapest measured price, never list price. Games without a reliable main-story length (free to play, live service, multiplayer only, missing) are left out of every time-based calculation and listed in `excluded`. Future months assume a game stays in its catalog unless a leave date is known (`assumes_catalog_stays`). `assumptions` repeats the inputs; every price carries `buy_price_fetched_at`. No API key needed; the keyless per-IP limits apply. Nothing is stored.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "$ref": "#/components/schemas/PlanRequestBody"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfPlanResult"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          },
          { }
        ]
      }
    },
    "/v1/admin/queue": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Unresolved match queue with the raw record summary and the top 3 suggested games",
        "description": "Parameters: `page`, `page_size` (1-100, default 20), `reason`. Internal key only.",
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/queue/{id}/resolve": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Resolve a queue row: approve the suggestion, reject the record or link it to a game",
        "description": "Body: `{\"action\": \"approve|reject|link\", \"game_id\": 123, \"edition\": \"deluxe\"}`. `edition` is optional (standard, deluxe, gold, goty, ultimate, bundle; not with reject): CheapShark records whose title does not reveal the edition stay unlisted until one is chosen here. Writes `match_override` (updates it if the record was decided before) and resolves the row.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/queue/resolve-bulk": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Approve several queue rows for the same suggested game in one transaction (all or nothing)",
        "description": "Body: `{\"ids\": [1, 2], \"game_id\": 123, \"edition\": \"deluxe\"}`; 1-50 distinct ids, `edition` optional. Every row goes through the same checks as a single approve (`game_id` must be among that row's current suggestions); the first failing row aborts the call and nothing is written.",
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/subscriptions/{code}/entries": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Subscription catalog entries with leaving dates",
        "description": "Optional `region`, `page`, `page_size` (1-200, default 50).",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/subscriptions/{code}/entries/{productId}": {
      "put": {
        "tags": [
          "Admin"
        ],
        "summary": "Set the leaving date of an entry (from Xbox Wire); null clears it",
        "description": "Requires `region`. Body: `{\"leaving_at\": \"2026-11-30\"}` or `{\"leaving_at\": null}`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "productId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/subscriptions/prices": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Every stored subscription monthly price with its source and the date it was checked",
        "description": "Admin-only: the public `/subscriptions` endpoint returns just the converted price.",
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/subscriptions/{code}/price": {
      "put": {
        "tags": [
          "Admin"
        ],
        "summary": "Set the monthly price of a subscription in a region (no price source exists)",
        "description": "Requires `region`. Body: `{\"monthly_price\": 9.99, \"currency\": \"USD\", \"source\": \"https://...\", \"source_date\": \"2026-10-09\"}`. `source` (up to 500 characters) and `source_date` (yyyy-MM-dd, not in the future) are optional and replace the stored values: omitting them clears the previous source.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/keys": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "API keys with 7-day usage (Redis and api_usage combined) and the 100+ requests flag",
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/keys/{id}/revoke": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Revoke an API key",
        "description": "Internal keys cannot be revoked through the API.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/sources": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Source health: source_status plus the last fetched_at of each raw table; older than 24 hours is red",
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/games/{key}/offers": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Every offer of a game across sources and regions, plus its itad_id and ITAD historical-low seeds",
        "description": "`key` is a Steam AppID (digits) or a slug. Not filtered by `Itad:Public`: rows hidden from public reads carry `hidden: true` (shown as \"ITAD (gizli)\"). Admin key only, never cached.",
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/fee-rules": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Checkout fee rules per store, region and payment method",
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/fee-rules/{id}": {
      "put": {
        "tags": [
          "Admin"
        ],
        "summary": "Correct a fee rule (sets verified_at to now and status to verified)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/reports": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Wrong price / wrong match / dead link reports with weekly counts",
        "description": "Parameters: `resolved` (true/false), `kind`, `page`, `page_size`. Unresolved first.",
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/reports/{id}/resolve": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Mark a report as resolved",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/kpi": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Experiment dashboard: weekly kpi_snapshot rows (newest first), the live current week and the targets",
        "description": "Parameter: `weeks` (1-104, default 12). `current_week` is computed on the fly by the same code as the Monday job (`partial: true`). `status` per KPI is `met`, `not_met`, `below_drop` or `unmeasured` (null value). Click rate and report counts are informational and have no target.",
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/api-metrics": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "API and MCP usage: requests per key or anonymous, per endpoint or tool, error rate and latency percentiles",
        "description": "Parameter: `hours` (1-840, default 24). Hourly buckets in Redis (35 days). `subject` is `k{key id}`, `anon`, `ext` (extension) or `unauth`; no IPs are stored. Latency percentiles are the upper bound of the histogram bucket. MCP calls appear as `mcp:tool:{name}`.",
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/experiment-report": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Weekly experiment report: KPIs, thresholds, funnel counts, clicks by channel, API usage, channel members, commission",
        "description": "Parameter: `week` (yyyy-MM-dd, any day of the week; default current). Unmeasured values are null, never estimated. Thresholds come from `DataRules.Experiment` and are placeholders until the owner confirms them.",
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/affiliate-sales/import": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Import an affiliate network sales report (CSV body, Content-Type: text/csv; up to 5 MB)",
        "description": "Parameters: `network` (required: `impact` = Actions report, `cj` = Commission Detail, anything else = generic column mapping), optional `currency` for reports without one and `store` (store code) for rows whose click id is unknown and whose merchant name matches no store. Upserts by (network, transaction id): re-uploading updates status and amounts, never duplicates. The file is accepted or rejected as a whole (400 lists up to 20 row errors).",
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    },
    "/v1/admin/affiliate-sales/summary": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Clicks to sales to commission, grouped by store, channel (src) and page",
        "description": "Parameters: `from`, `to` (yyyy-MM-dd UTC, both inclusive; default last 30 days, at most 366 days). Bot clicks are excluded. Reversed sales count in `sales`/`reversed` but not in amounts. `unmatched` holds sales without a click (by sale date).",
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          {
            "ApiKey": [ ]
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "AlertRequest": {
        "required": [
          "email",
          "game_id",
          "target_price",
          "currency",
          "region",
          "edition"
        ],
        "type": "object",
        "properties": {
          "email": {
            "type": [
              "null",
              "string"
            ]
          },
          "game_id": {
            "type": [
              "null",
              "string"
            ]
          },
          "target_price": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region": {
            "type": [
              "null",
              "string"
            ]
          },
          "edition": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "ApiResponseOfDealModel": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds",
          "stale"
        ],
        "type": "object",
        "properties": {
          "data": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/DealModel"
              }
            ]
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "data_age_seconds": {
            "type": "integer",
            "format": "int64"
          },
          "stale": {
            "type": "boolean"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "attribution": {
            "type": "string"
          }
        }
      },
      "ApiResponseOfDiscoverPageModel": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds",
          "stale"
        ],
        "type": "object",
        "properties": {
          "data": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/DiscoverPageModel"
              }
            ]
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "data_age_seconds": {
            "type": "integer",
            "format": "int64"
          },
          "stale": {
            "type": "boolean"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "attribution": {
            "type": "string"
          }
        }
      },
      "ApiResponseOfGameDetailModel": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds",
          "stale"
        ],
        "type": "object",
        "properties": {
          "data": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/GameDetailModel"
              }
            ]
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "data_age_seconds": {
            "type": "integer",
            "format": "int64"
          },
          "stale": {
            "type": "boolean"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "attribution": {
            "type": "string"
          }
        }
      },
      "ApiResponseOfGameIndexPage": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds",
          "stale"
        ],
        "type": "object",
        "properties": {
          "data": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/GameIndexPage"
              }
            ]
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "data_age_seconds": {
            "type": "integer",
            "format": "int64"
          },
          "stale": {
            "type": "boolean"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "attribution": {
            "type": "string"
          }
        }
      },
      "ApiResponseOfIReadOnlyListOfDealModel": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds",
          "stale"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/DealModel"
            }
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "data_age_seconds": {
            "type": "integer",
            "format": "int64"
          },
          "stale": {
            "type": "boolean"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "attribution": {
            "type": "string"
          }
        }
      },
      "ApiResponseOfIReadOnlyListOfDiscoverTierModel": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds",
          "stale"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/DiscoverTierModel"
            }
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "data_age_seconds": {
            "type": "integer",
            "format": "int64"
          },
          "stale": {
            "type": "boolean"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "attribution": {
            "type": "string"
          }
        }
      },
      "ApiResponseOfIReadOnlyListOfGameSummary": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds",
          "stale"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/GameSummary"
            }
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "data_age_seconds": {
            "type": "integer",
            "format": "int64"
          },
          "stale": {
            "type": "boolean"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "attribution": {
            "type": "string"
          }
        }
      },
      "ApiResponseOfIReadOnlyListOfHistoryPoint": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds",
          "stale"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/HistoryPoint"
            }
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "data_age_seconds": {
            "type": "integer",
            "format": "int64"
          },
          "stale": {
            "type": "boolean"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "attribution": {
            "type": "string"
          }
        }
      },
      "ApiResponseOfIReadOnlyListOfSourceStatus": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds",
          "stale"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/SourceStatus"
            }
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "data_age_seconds": {
            "type": "integer",
            "format": "int64"
          },
          "stale": {
            "type": "boolean"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "attribution": {
            "type": "string"
          }
        }
      },
      "ApiResponseOfIReadOnlyListOfStoreView": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds",
          "stale"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/StoreView"
            }
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "data_age_seconds": {
            "type": "integer",
            "format": "int64"
          },
          "stale": {
            "type": "boolean"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "attribution": {
            "type": "string"
          }
        }
      },
      "ApiResponseOfIReadOnlyListOfSubscriptionSummaryModel": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds",
          "stale"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/SubscriptionSummaryModel"
            }
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "data_age_seconds": {
            "type": "integer",
            "format": "int64"
          },
          "stale": {
            "type": "boolean"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "attribution": {
            "type": "string"
          }
        }
      },
      "ApiResponseOfPlanResult": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds",
          "stale"
        ],
        "type": "object",
        "properties": {
          "data": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/PlanResult"
              }
            ]
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "data_age_seconds": {
            "type": "integer",
            "format": "int64"
          },
          "stale": {
            "type": "boolean"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "attribution": {
            "type": "string"
          }
        }
      },
      "CheapestWayResult": {
        "required": [
          "winner",
          "candidates",
          "notes"
        ],
        "type": "object",
        "properties": {
          "winner": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/PlayOption"
              }
            ]
          },
          "candidates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlayOption"
            }
          },
          "notes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SubscriptionNote"
            }
          },
          "sentence": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "CreatedKeyModel": {
        "required": [
          "api_key",
          "daily_quota",
          "rate_limit_per_minute",
          "created_at",
          "notice"
        ],
        "type": "object",
        "properties": {
          "api_key": {
            "type": "string"
          },
          "daily_quota": {
            "type": "integer",
            "format": "int32"
          },
          "rate_limit_per_minute": {
            "type": "integer",
            "format": "int32"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "notice": {
            "type": "string"
          }
        }
      },
      "CreateKeyRequest": {
        "required": [
          "email",
          "project_name"
        ],
        "type": "object",
        "properties": {
          "email": {
            "type": [
              "null",
              "string"
            ]
          },
          "project_name": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "DealModel": {
        "required": [
          "game",
          "best_offer",
          "time_to_beat_hours",
          "review_percent",
          "other_editions",
          "leaving_at",
          "drop",
          "is_new",
          "hours_per_unit",
          "leaving_announced",
          "subscription_codes"
        ],
        "type": "object",
        "properties": {
          "game": {
            "$ref": "#/components/schemas/GameSummary"
          },
          "best_offer": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/OfferModel"
              }
            ]
          },
          "time_to_beat_hours": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "review_percent": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "other_editions": {
            "type": "integer",
            "format": "int32"
          },
          "leaving_at": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "drop": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Money"
              }
            ]
          },
          "is_new": {
            "type": "boolean"
          },
          "hours_per_unit": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "leaving_announced": {
            "type": "boolean"
          },
          "subscription_codes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "DiscoverPageModel": {
        "required": [
          "rows",
          "total",
          "at_low_count",
          "best_value",
          "page",
          "page_size",
          "threshold"
        ],
        "type": "object",
        "properties": {
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DealModel"
            }
          },
          "total": {
            "type": "integer",
            "format": "int32"
          },
          "at_low_count": {
            "type": "integer",
            "format": "int32"
          },
          "best_value": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/DealModel"
              }
            ]
          },
          "page": {
            "type": "integer",
            "format": "int32"
          },
          "page_size": {
            "type": "integer",
            "format": "int32"
          },
          "threshold": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Money"
              }
            ]
          }
        }
      },
      "DiscoverTierModel": {
        "required": [
          "usd",
          "threshold"
        ],
        "type": "object",
        "properties": {
          "usd": {
            "type": "integer",
            "format": "int32"
          },
          "threshold": {
            "$ref": "#/components/schemas/Money"
          },
          "euros": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "Edition": {
        "enum": [
          "standard",
          "deluxe",
          "gold",
          "goty",
          "ultimate",
          "bundle",
          "unknown"
        ]
      },
      "FeeKind": {
        "enum": [
          "none",
          "percent",
          "fixed",
          "percent_plus_fixed"
        ]
      },
      "FeeLabel": {
        "enum": [
          "verified",
          "may_apply",
          "stale"
        ]
      },
      "FeeOrder": {
        "enum": [
          "pct_then_fixed",
          "fixed_then_pct"
        ]
      },
      "FeeRuleStatus": {
        "enum": [
          "verified",
          "stale",
          "unknown"
        ]
      },
      "FeeRuleView": {
        "required": [
          "region",
          "payment_method",
          "kind",
          "order",
          "percent",
          "fixed",
          "currency",
          "status",
          "verified_at"
        ],
        "type": "object",
        "properties": {
          "region": {
            "$ref": "#/components/schemas/Region"
          },
          "payment_method": {
            "$ref": "#/components/schemas/PaymentMethod"
          },
          "kind": {
            "$ref": "#/components/schemas/FeeKind"
          },
          "order": {
            "$ref": "#/components/schemas/FeeOrder"
          },
          "percent": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "fixed": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/FeeRuleStatus"
          },
          "verified_at": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          }
        }
      },
      "FinishInTimeResult": {
        "required": [
          "days_left",
          "hours_needed",
          "hours_per_day",
          "verdict"
        ],
        "type": "object",
        "properties": {
          "days_left": {
            "type": "integer",
            "format": "int32"
          },
          "hours_needed": {
            "type": "number",
            "format": "double"
          },
          "hours_per_day": {
            "type": "number",
            "format": "double"
          },
          "verdict": {
            "$ref": "#/components/schemas/FinishVerdict"
          }
        }
      },
      "FinishVerdict": {
        "enum": [
          "comfortable",
          "tight",
          "not_realistic"
        ]
      },
      "FreshnessKind": {
        "enum": [
          "fresh",
          "stale",
          "hidden"
        ]
      },
      "GameDetailModel": {
        "required": [
          "game",
          "release_date",
          "genres",
          "review_percent",
          "editions",
          "time_to_beat_minutes",
          "offers",
          "subscriptions",
          "cheapest_way",
          "lows",
          "other_region_offers",
          "console_offers",
          "is_free_to_play"
        ],
        "type": "object",
        "properties": {
          "game": {
            "$ref": "#/components/schemas/GameSummary"
          },
          "release_date": {
            "type": [
              "null",
              "string"
            ],
            "format": "date"
          },
          "genres": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "review_percent": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "editions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Edition"
            }
          },
          "time_to_beat_minutes": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "offers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OfferModel"
            }
          },
          "subscriptions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SubscriptionView"
            }
          },
          "cheapest_way": {
            "$ref": "#/components/schemas/CheapestWayResult"
          },
          "lows": {
            "$ref": "#/components/schemas/LowsModel"
          },
          "other_region_offers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OfferModel"
            }
          },
          "console_offers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OfferModel"
            }
          },
          "is_free_to_play": {
            "type": "boolean"
          }
        }
      },
      "GameIndexEntry": {
        "required": [
          "id",
          "slug",
          "title",
          "updated_at"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "GameIndexPage": {
        "required": [
          "items",
          "total",
          "page",
          "page_size"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GameIndexEntry"
            }
          },
          "total": {
            "type": "integer",
            "format": "int32"
          },
          "page": {
            "type": "integer",
            "format": "int32"
          },
          "page_size": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "GameSummary": {
        "required": [
          "id",
          "steam_app_id",
          "igdb_id",
          "title",
          "slug",
          "cover_url"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "steam_app_id": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "igdb_id": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "title": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "cover_url": {
            "type": [
              "null",
              "string"
            ]
          },
          "cover_portrait_url": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "HistoryPoint": {
        "required": [
          "day",
          "price"
        ],
        "type": "object",
        "properties": {
          "day": {
            "type": "string",
            "format": "date"
          },
          "price": {
            "$ref": "#/components/schemas/Money"
          }
        }
      },
      "HttpValidationProblemDetails": {
        "type": "object",
        "properties": {
          "type": {
            "type": [
              "null",
              "string"
            ]
          },
          "title": {
            "type": [
              "null",
              "string"
            ]
          },
          "status": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "detail": {
            "type": [
              "null",
              "string"
            ]
          },
          "instance": {
            "type": [
              "null",
              "string"
            ]
          },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        }
      },
      "LowBasis": {
        "enum": [
          "own",
          "itad",
          null
        ]
      },
      "LowLabel": {
        "required": [
          "kind"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "$ref": "#/components/schemas/LowLabelKind"
          },
          "above_percent": {
            "type": "integer",
            "format": "int32",
            "default": 0
          },
          "since": {
            "type": [
              "null",
              "string"
            ],
            "format": "date"
          },
          "basis": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/LowBasis"
              }
            ]
          }
        }
      },
      "LowLabelKind": {
        "enum": [
          "unknown",
          "at_low",
          "above_low",
          "since_tracking"
        ]
      },
      "LowsModel": {
        "required": [
          "all_time",
          "last_90_days",
          "all_time_at"
        ],
        "type": "object",
        "properties": {
          "all_time": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Money"
              }
            ]
          },
          "last_90_days": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Money"
              }
            ]
          },
          "all_time_at": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          }
        }
      },
      "Money": {
        "required": [
          "amount",
          "currency",
          "formatted"
        ],
        "type": "object",
        "properties": {
          "amount": {
            "type": "number"
          },
          "currency": {
            "type": "string",
            "description": "USD, EUR, GBP or TRY."
          },
          "formatted": {
            "type": "string",
            "description": "Display text, e.g. \"€9.99\"."
          }
        }
      },
      "NewsletterRequest": {
        "required": [
          "email"
        ],
        "type": "object",
        "properties": {
          "email": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "OfferModel": {
        "required": [
          "offer_id",
          "store_code",
          "store_name",
          "store_kind",
          "edition",
          "platform",
          "region_lock",
          "real_price",
          "list_price",
          "fee_label",
          "fee_verified_at",
          "discount_pct",
          "low",
          "converted",
          "fetched_at",
          "freshness",
          "url",
          "measured_in",
          "discount_ends_at",
          "price_region",
          "eligible"
        ],
        "type": "object",
        "properties": {
          "offer_id": {
            "type": "integer",
            "format": "int64"
          },
          "store_code": {
            "type": "string"
          },
          "store_name": {
            "type": "string"
          },
          "store_kind": {
            "$ref": "#/components/schemas/StoreKind"
          },
          "edition": {
            "$ref": "#/components/schemas/Edition"
          },
          "platform": {
            "$ref": "#/components/schemas/Platform"
          },
          "region_lock": {
            "$ref": "#/components/schemas/RegionLock"
          },
          "real_price": {
            "$ref": "#/components/schemas/Money"
          },
          "list_price": {
            "$ref": "#/components/schemas/Money"
          },
          "fee_label": {
            "$ref": "#/components/schemas/FeeLabel"
          },
          "fee_verified_at": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "discount_pct": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int16"
          },
          "low": {
            "$ref": "#/components/schemas/LowLabel"
          },
          "converted": {
            "type": "boolean"
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "freshness": {
            "$ref": "#/components/schemas/FreshnessKind"
          },
          "url": {
            "type": "string"
          },
          "measured_in": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "discount_ends_at": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "price_region": {
            "$ref": "#/components/schemas/Region"
          },
          "eligible": {
            "type": "boolean"
          }
        }
      },
      "PaymentMethod": {
        "enum": [
          "card",
          "paypal",
          "wallet",
          "other"
        ]
      },
      "PlanAssumptions": {
        "required": [
          "hours_per_week",
          "hours_per_month",
          "weeks_per_month",
          "play_time_source",
          "subscriptions",
          "owned_codes"
        ],
        "type": "object",
        "properties": {
          "hours_per_week": {
            "type": "number",
            "format": "double"
          },
          "hours_per_month": {
            "type": "number",
            "format": "double"
          },
          "weeks_per_month": {
            "type": "integer",
            "format": "int32"
          },
          "play_time_source": {
            "type": "string"
          },
          "subscriptions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlanSubscription"
            }
          },
          "owned_codes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "PlanExcluded": {
        "required": [
          "game_id",
          "title",
          "reason",
          "buy_price"
        ],
        "type": "object",
        "properties": {
          "game_id": {
            "type": "integer",
            "format": "int64"
          },
          "title": {
            "type": "string"
          },
          "reason": {
            "$ref": "#/components/schemas/PlanExclusion"
          },
          "buy_price": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Money"
              }
            ]
          },
          "game": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/GameSummary"
              }
            ]
          }
        }
      },
      "PlanExclusion": {
        "enum": [
          "free_to_play",
          "no_play_time",
          "no_way_to_play"
        ]
      },
      "PlannedGame": {
        "required": [
          "game_id",
          "title",
          "source",
          "subscription_code",
          "play_minutes",
          "first_month",
          "finish_month",
          "cost",
          "buy_store",
          "buy_offer_id",
          "buy_price_fetched_at",
          "leaving_at",
          "assumes_catalog_stays"
        ],
        "type": "object",
        "properties": {
          "game_id": {
            "type": "integer",
            "format": "int64"
          },
          "title": {
            "type": "string"
          },
          "source": {
            "$ref": "#/components/schemas/PlanSource"
          },
          "subscription_code": {
            "type": [
              "null",
              "string"
            ]
          },
          "play_minutes": {
            "type": "integer",
            "format": "int32"
          },
          "first_month": {
            "type": "integer",
            "format": "int32"
          },
          "finish_month": {
            "type": "integer",
            "format": "int32"
          },
          "cost": {
            "$ref": "#/components/schemas/Money"
          },
          "buy_store": {
            "type": [
              "null",
              "string"
            ]
          },
          "buy_offer_id": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "buy_price_fetched_at": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "leaving_at": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "assumes_catalog_stays": {
            "type": "boolean"
          },
          "game": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/GameSummary"
              }
            ]
          }
        }
      },
      "PlannedMonth": {
        "required": [
          "index",
          "starts_at",
          "paid_subscription",
          "finishing_game_ids"
        ],
        "type": "object",
        "properties": {
          "index": {
            "type": "integer",
            "format": "int32"
          },
          "starts_at": {
            "type": "string",
            "format": "date-time"
          },
          "paid_subscription": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/PlanSubscription"
              }
            ]
          },
          "finishing_game_ids": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int64"
            }
          }
        }
      },
      "PlanRequestBody": {
        "required": [
          "games",
          "owned_subscriptions",
          "hours_per_week",
          "region",
          "currency",
          "include_marketplaces"
        ],
        "type": "object",
        "properties": {
          "games": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          },
          "owned_subscriptions": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          },
          "hours_per_week": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "region": {
            "type": [
              "null",
              "string"
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "include_marketplaces": {
            "type": [
              "null",
              "boolean"
            ]
          }
        }
      },
      "PlanResult": {
        "required": [
          "months",
          "games",
          "excluded",
          "totals",
          "assumptions"
        ],
        "type": "object",
        "properties": {
          "months": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlannedMonth"
            }
          },
          "games": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlannedGame"
            }
          },
          "excluded": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlanExcluded"
            }
          },
          "totals": {
            "$ref": "#/components/schemas/PlanTotals"
          },
          "assumptions": {
            "$ref": "#/components/schemas/PlanAssumptions"
          }
        }
      },
      "PlanSource": {
        "enum": [
          "buy",
          "owned_subscription",
          "paid_subscription"
        ]
      },
      "PlanSubscription": {
        "required": [
          "code",
          "name",
          "monthly_price"
        ],
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "monthly_price": {
            "$ref": "#/components/schemas/Money"
          }
        }
      },
      "PlanTotals": {
        "required": [
          "plan",
          "buy_everything",
          "savings",
          "buy_everything_excludes_game_ids"
        ],
        "type": "object",
        "properties": {
          "plan": {
            "$ref": "#/components/schemas/Money"
          },
          "buy_everything": {
            "$ref": "#/components/schemas/Money"
          },
          "savings": {
            "$ref": "#/components/schemas/Money"
          },
          "buy_everything_excludes_game_ids": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int64"
            }
          }
        }
      },
      "Platform": {
        "enum": [
          "steam",
          "epic",
          "gog",
          "ubisoft",
          "ea",
          "xbox_pc",
          "ps",
          "switch",
          "xbox"
        ]
      },
      "PlayKind": {
        "enum": [
          "buy_authorized",
          "buy_marketplace",
          "subscription"
        ]
      },
      "PlayOption": {
        "required": [
          "kind",
          "label",
          "cost"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "$ref": "#/components/schemas/PlayKind"
          },
          "label": {
            "type": "string"
          },
          "cost": {
            "$ref": "#/components/schemas/Money"
          },
          "months": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "offer_id": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "subscription_code": {
            "type": [
              "null",
              "string"
            ]
          },
          "owned": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "ProblemDetails": {
        "type": "object",
        "properties": {
          "type": {
            "type": [
              "null",
              "string"
            ]
          },
          "title": {
            "type": [
              "null",
              "string"
            ]
          },
          "status": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "detail": {
            "type": [
              "null",
              "string"
            ]
          },
          "instance": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "Region": {
        "enum": [
          "tr",
          "us",
          "eu",
          "gb",
          null
        ]
      },
      "RegionLock": {
        "enum": [
          "global",
          "eu",
          "tr",
          "ru_cis",
          "row",
          "latam",
          "asia"
        ]
      },
      "ReportRequest": {
        "required": [
          "offer_id",
          "kind"
        ],
        "type": "object",
        "properties": {
          "offer_id": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "kind": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "SourceStatus": {
        "required": [
          "source",
          "last_success_at",
          "freshness"
        ],
        "type": "object",
        "properties": {
          "source": {
            "type": "string"
          },
          "last_success_at": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "freshness": {
            "$ref": "#/components/schemas/FreshnessKind"
          },
          "consecutive_failures": {
            "type": "integer",
            "format": "int32",
            "default": 0
          },
          "age_seconds": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "expected_every_seconds": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "overdue": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "StoreKind": {
        "enum": [
          "authorized",
          "marketplace",
          "console",
          "subscription"
        ]
      },
      "StoreView": {
        "required": [
          "code",
          "name",
          "kind",
          "fee_rules"
        ],
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "$ref": "#/components/schemas/StoreKind"
          },
          "fee_rules": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FeeRuleView"
            }
          }
        }
      },
      "SubscribeAccepted": {
        "required": [
          "status",
          "message"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "SubscriptionNote": {
        "required": [
          "code",
          "name",
          "kind",
          "leaving_at"
        ],
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "$ref": "#/components/schemas/SubscriptionNoteKind"
          },
          "leaving_at": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          }
        }
      },
      "SubscriptionNoteKind": {
        "enum": [
          "duration_unknown",
          "leaving_too_soon"
        ]
      },
      "SubscriptionSummaryModel": {
        "required": [
          "code",
          "name",
          "monthly_price",
          "catalog_count",
          "new_this_week",
          "leaving_soon"
        ],
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "monthly_price": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Money"
              }
            ]
          },
          "catalog_count": {
            "type": "integer",
            "format": "int32"
          },
          "new_this_week": {
            "type": "integer",
            "format": "int32"
          },
          "leaving_soon": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "SubscriptionView": {
        "required": [
          "code",
          "name",
          "monthly_price",
          "leaving_at",
          "in_catalog"
        ],
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "monthly_price": {
            "$ref": "#/components/schemas/Money"
          },
          "leaving_at": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "in_catalog": {
            "type": "boolean"
          },
          "leaving_announced": {
            "type": "boolean",
            "default": false
          },
          "owned": {
            "type": "boolean",
            "default": false
          },
          "finish": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/FinishInTimeResult"
              }
            ]
          }
        }
      },
      "TokenResult": {
        "required": [
          "status"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string"
          }
        }
      }
    },
    "securitySchemes": {
      "ApiKey": {
        "type": "apiKey",
        "description": "Optional on read endpoints: without a key, 10 requests per minute and 100 per day per IP. Get a free key with POST /v1/keys: 1,000 requests per day, 60 per minute. Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset; 429 adds Retry-After.",
        "name": "X-Api-Key",
        "in": "header"
      }
    }
  },
  "tags": [
    {
      "name": "Games"
    },
    {
      "name": "Discover"
    },
    {
      "name": "Subscriptions"
    },
    {
      "name": "Stores"
    },
    {
      "name": "Meta"
    },
    {
      "name": "Keys"
    },
    {
      "name": "Alerts"
    },
    {
      "name": "Newsletter"
    },
    {
      "name": "Reports"
    },
    {
      "name": "Plan"
    },
    {
      "name": "Admin"
    }
  ]
}