{
  "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\". 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`).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "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",
        "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 (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`. `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"
            }
          }
        ],
        "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/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`.",
        "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"
            }
          }
        ],
        "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`.",
        "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"
            }
          }
        ],
        "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`.",
        "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"
            }
          }
        ],
        "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`.",
        "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"
            }
          }
        ],
        "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, top-1,000 titles in the catalog, 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": "Last successful fetch time per data source",
        "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/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/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/{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\"}`.",
        "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/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/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"
            ]
          }
        }
      },
      "ApiResponseOfDiscoverPageModel": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds"
        ],
        "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"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          }
        }
      },
      "ApiResponseOfGameDetailModel": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds"
        ],
        "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"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          }
        }
      },
      "ApiResponseOfGameIndexPage": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds"
        ],
        "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"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          }
        }
      },
      "ApiResponseOfIReadOnlyListOfDealModel": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds"
        ],
        "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"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          }
        }
      },
      "ApiResponseOfIReadOnlyListOfGameSummary": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds"
        ],
        "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"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          }
        }
      },
      "ApiResponseOfIReadOnlyListOfHistoryPoint": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds"
        ],
        "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"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          }
        }
      },
      "ApiResponseOfIReadOnlyListOfSourceStatus": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds"
        ],
        "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"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          }
        }
      },
      "ApiResponseOfIReadOnlyListOfStoreView": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds"
        ],
        "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"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          }
        }
      },
      "ApiResponseOfIReadOnlyListOfSubscriptionSummaryModel": {
        "required": [
          "data",
          "fetched_at",
          "data_age_seconds"
        ],
        "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"
          },
          "region": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Region"
              }
            ]
          },
          "currency": {
            "type": [
              "null",
              "string"
            ]
          },
          "region_fallback": {
            "type": [
              "null",
              "boolean"
            ]
          }
        }
      },
      "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"
        ],
        "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"
          }
        }
      },
      "DiscoverPageModel": {
        "required": [
          "rows",
          "total",
          "at_low_count",
          "best_value",
          "page",
          "page_size"
        ],
        "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"
          }
        }
      },
      "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"
          }
        }
      },
      "FreshnessKind": {
        "enum": [
          "fresh",
          "stale",
          "hidden"
        ]
      },
      "GameDetailModel": {
        "required": [
          "game",
          "release_date",
          "genres",
          "review_percent",
          "editions",
          "time_to_beat_minutes",
          "offers",
          "subscriptions",
          "cheapest_way",
          "lows"
        ],
        "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"
          }
        }
      },
      "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"
            ]
          }
        }
      },
      "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"
              }
            }
          }
        }
      },
      "LowLabel": {
        "required": [
          "kind"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "$ref": "#/components/schemas/LowLabelKind"
          },
          "above_percent": {
            "type": "integer",
            "format": "int32",
            "default": 0
          }
        }
      },
      "LowLabelKind": {
        "enum": [
          "unknown",
          "at_low",
          "above_low"
        ]
      },
      "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"
        ],
        "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"
              }
            ]
          }
        }
      },
      "PaymentMethod": {
        "enum": [
          "card",
          "paypal",
          "wallet",
          "other"
        ]
      },
      "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"
            ]
          }
        }
      },
      "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"
          }
        }
      },
      "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",
          "top_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"
          },
          "top_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"
          }
        }
      },
      "TokenResult": {
        "required": [
          "status"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string"
          }
        }
      }
    },
    "securitySchemes": {
      "ApiKey": {
        "type": "apiKey",
        "description": "Get a key with POST /v1/keys. 1,000 requests per day, 60 per minute.",
        "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": "Admin"
    }
  ]
}