{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/getcheapgames-com/json-schema/getcheapgames-com-game-card-out-schema.json",
  "title": "GameCardOut",
  "description": "A game as a listing renders it.\n\nDeclared rather than derived from `GameOut` so the wire shape of a listing\nis readable in one place. The fields a listing never fills — description,\nthe edition family, package contents — are simply absent instead of being\nserialised as a screenful of nulls.",
  "x-generated": "2026-11-10",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/getcheapgames-com-openapi.json#/components/schemas/GameCardOut",
  "properties": {
    "slug": {
      "type": "string",
      "title": "Slug"
    },
    "title": {
      "type": "string",
      "title": "Title"
    },
    "releaseYear": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Releaseyear"
    },
    "releaseDate": {
      "anyOf": [
        {
          "type": "string",
          "format": "date"
        },
        {
          "type": "null"
        }
      ],
      "title": "Releasedate"
    },
    "publisher": {
      "type": "string",
      "title": "Publisher"
    },
    "genres": {
      "items": {
        "type": "string"
      },
      "type": "array",
      "title": "Genres"
    },
    "steamAppId": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Steamappid"
    },
    "coverBase": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Coverbase"
    },
    "logo": {
      "anyOf": [
        {
          "$ref": "#/$defs/LogoOut"
        },
        {
          "type": "null"
        }
      ]
    },
    "priceHistory": {
      "items": {
        "type": "integer"
      },
      "type": "array",
      "title": "Pricehistory",
      "default": []
    },
    "kind": {
      "type": "string",
      "title": "Kind",
      "default": "game"
    },
    "isFree": {
      "type": "boolean",
      "title": "Isfree",
      "default": false
    },
    "freeToKeep": {
      "type": "boolean",
      "title": "Freetokeep",
      "default": false
    },
    "giveaway": {
      "anyOf": [
        {
          "$ref": "#/$defs/gcg__schemas__GiveawayOut"
        },
        {
          "type": "null"
        }
      ]
    },
    "playUrl": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Playurl"
    },
    "edition": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Edition"
    },
    "franchise": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Franchise"
    },
    "offers": {
      "items": {
        "$ref": "#/$defs/OfferCardOut"
      },
      "type": "array",
      "title": "Offers",
      "default": []
    }
  },
  "type": "object",
  "required": [
    "slug",
    "title",
    "releaseYear",
    "publisher",
    "genres"
  ],
  "$defs": {
    "LogoOut": {
      "properties": {
        "src": {
          "type": "string",
          "title": "Src"
        },
        "width": {
          "type": "integer",
          "title": "Width"
        },
        "height": {
          "type": "integer",
          "title": "Height"
        }
      },
      "type": "object",
      "required": [
        "src",
        "width",
        "height"
      ],
      "title": "LogoOut",
      "description": "A game's official logo (`services/game_logos.py`): the served path of its\nWebP, transparent and trimmed to the drawn pixels, and its size in pixels,\nso a page reserves the box before it loads."
    },
    "OfferCardOut": {
      "properties": {
        "store": {
          "$ref": "#/$defs/StoreRefOut"
        },
        "currency": {
          "type": "string",
          "title": "Currency"
        },
        "listPriceCents": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Listpricecents"
        },
        "feeCents": {
          "type": "integer",
          "title": "Feecents"
        },
        "totalCents": {
          "type": "integer",
          "title": "Totalcents"
        },
        "platform": {
          "type": "string",
          "title": "Platform"
        },
        "inStock": {
          "type": "boolean",
          "title": "Instock"
        },
        "regionLocked": {
          "type": "boolean",
          "title": "Regionlocked",
          "default": false
        }
      },
      "type": "object",
      "required": [
        "store",
        "currency",
        "listPriceCents",
        "feeCents",
        "totalCents",
        "platform",
        "inStock"
      ],
      "title": "OfferCardOut",
      "description": "The part of an offer a CARD renders, and nothing else.\n\nA listing sends one of these per offer per game, and the cards read only\nfour things from the set: the cheapest total, its currency and store, and\nthe ceiling the saving badge is measured against. Sending the full\n`OfferOut` there put 24,901 bytes of offer into a 27,704-byte `/deals`\nresponse — **89% of the payload** — to compute those four numbers, and it\nsat on the critical path: nothing paints until it arrives.\n\nField NAMES match `OfferOut` exactly so `bestOfferIn` and `priceSpread` in\nthe browser work against either shape unchanged. The detail page keeps the\nfull object, which is where the dropped fields are actually rendered."
    },
    "StoreRefOut": {
      "properties": {
        "code": {
          "type": "string",
          "title": "Code"
        },
        "name": {
          "type": "string",
          "title": "Name"
        },
        "accent": {
          "type": "string",
          "title": "Accent"
        },
        "kind": {
          "type": "string",
          "title": "Kind"
        }
      },
      "type": "object",
      "required": [
        "code",
        "name",
        "accent",
        "kind"
      ],
      "title": "StoreRefOut",
      "description": "The store fields a client needs to render an offer.\n\nEmbedded in each offer rather than referenced by id: it removes a\nclient-side join and a second request, and six short records across a\nresponse is nothing next to the round trip it saves."
    },
    "gcg__schemas__GiveawayOut": {
      "properties": {
        "platform": {
          "type": "string",
          "title": "Platform"
        },
        "claimUrl": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Claimurl"
        },
        "endsAt": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time"
            },
            {
              "type": "null"
            }
          ],
          "title": "Endsat"
        }
      },
      "type": "object",
      "required": [
        "platform"
      ],
      "title": "GiveawayOut",
      "description": "A giveaway on another store than Steam: what the claim button says and\nwhere it goes (`catalog.GIVEAWAY_ELSEWHERE`). Steam's own is the game's\n`free_to_keep`, whose link the client builds from the appid."
    }
  }
}