{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/glowjakt-se/json-schema/glowjakt-se-product-detail-schema.json",
  "title": "ProductDetail",
  "x-generated": "2026-11-10",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/glowjakt-se-openapi.json#/components/schemas/ProductDetail",
  "allOf": [
    {
      "$ref": "#/$defs/ProductSummary"
    },
    {
      "type": "object",
      "properties": {
        "category": {
          "$ref": "#/$defs/Category"
        },
        "subcategory_path": {
          "type": [
            "string",
            "null"
          ]
        },
        "description": {
          "type": [
            "string",
            "null"
          ]
        },
        "inci": {
          "type": [
            "string",
            "null"
          ],
          "description": "Full INCI ingredient list (comma-separated). Null for devices/accessories."
        },
        "key_ingredients": {
          "type": "array",
          "description": "Notable ingredients with localized name, INCI term and function.",
          "items": {
            "type": "object",
            "properties": {
              "slug": {
                "type": "string"
              },
              "name": {
                "type": "string",
                "description": "Localized display name."
              },
              "note": {
                "type": "string",
                "description": "INCI term(s) matched."
              },
              "category": {
                "type": "string",
                "description": "Function, e.g. Hydration, Exfoliation."
              },
              "tier": {
                "type": "integer"
              }
            }
          }
        },
        "gallery": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Product image URLs."
        },
        "certifications": {
          "type": "array",
          "description": "Third-party verified brand certifications from the Glowpedia fact base (Ecocert, Leaping Bunny, The Vegan Society...). Localized note explains each cert.",
          "items": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "note": {
                "type": "string"
              }
            }
          }
        },
        "claims": {
          "type": [
            "array",
            "null"
          ],
          "description": "The brand's OWN unverified marketing claims (localized strings). Always attribute to the brand — never present as verified facts.",
          "items": {
            "type": "string"
          }
        },
        "glowpedia_url": {
          "type": [
            "string",
            "null"
          ],
          "description": "Market-localized URL to the brand article on Glowpedia (the beauty-brand encyclopedia). Null until the market edition is launched."
        },
        "guide_slug": {
          "type": [
            "string",
            "null"
          ],
          "description": "Slug of the price guide for the product's brand when one exists in this market (GET /api/guides/{slug}); null otherwise."
        }
      }
    }
  ],
  "$defs": {
    "Category": {
      "type": "string",
      "enum": [
        "skincare",
        "makeup",
        "haircare",
        "fragrance",
        "nails",
        "body",
        "accessories"
      ]
    },
    "ProductSummary": {
      "type": "object",
      "properties": {
        "id": {
          "type": "integer"
        },
        "name": {
          "type": "string"
        },
        "brand": {
          "type": "string"
        },
        "brand_slug": {
          "type": "string"
        },
        "slug": {
          "type": "string",
          "description": "Use with /api/products/{slug}/offers. Product page: https://<site>/<country>/product/<slug>/"
        },
        "price": {
          "type": "number",
          "description": "Lowest price among stores that currently have the product in stock (or the lowest listed price if it is sold out everywhere), in local currency (EUR prices have decimals)."
        },
        "currency": {
          "type": "string"
        },
        "original_price": {
          "type": [
            "number",
            "null"
          ]
        },
        "image_url": {
          "type": [
            "string",
            "null"
          ]
        },
        "in_stock": {
          "type": "boolean"
        },
        "store_name": {
          "type": "string",
          "description": "Store offering that price."
        },
        "affiliate_url": {
          "type": "string",
          "description": "Tracked buy link to the store offering that price (https://{market-domain}/go/{id}?t=...). Signed and valid for 48 hours from the response; it redirects (302) to the store. Do not store or replay expired links — fetch fresh ones."
        },
        "offer_count": {
          "type": "integer",
          "description": "Number of stores with this product in stock right now. For a barcode match (see q on /api/products), the number of store listings with that barcode, including sold-out ones."
        },
        "listing_count": {
          "type": "integer",
          "description": "Number of stores carrying this product, including those currently sold out. Equals the number of entries returned by the offers endpoint. Not set for a barcode match (see q on /api/products)."
        },
        "subcategory_path": {
          "type": [
            "string",
            "null"
          ],
          "description": "Product type as category/subcategory path, e.g. \"makeup/lipstick\"."
        },
        "content_value": {
          "type": [
            "number",
            "null"
          ],
          "description": "Package quantity in base unit (ml or g), parsed from the product name. Served by the German market (country=de) only; used for the legal unit price (Grundpreis per 1 l / 1 kg, PAngV). Null when the quantity is unknown or the product is sold by piece."
        },
        "content_unit": {
          "type": [
            "string",
            "null"
          ],
          "description": "Base unit for content_value: \"ml\" or \"g\". German market only."
        }
      }
    }
  }
}