{
  "openapi": "3.1.0",
  "info": {
    "title": "Glowjakt Public API",
    "version": "1.0.0",
    "summary": "Nordic beauty price comparison — read-only public API",
    "description": "Glowjakt compares beauty product prices across 40+ stores in Sweden, Norway, Denmark, Finland and Germany (the German market is branded Glowscout). Products are matched by EAN barcode, so comparisons are exact. Prices refresh daily from store feeds. Each market has its own API host (see servers) with its own catalog and currency. Glowjakt never charges stores for listing and sells no sponsored placements. This specification covers the curated read-only surface intended for AI agents and integrations. Please respect rate limits; contact us for high-volume use. An MCP server is also available at https://mcp.glowjakt.se/mcp — see https://mcp.glowjakt.se/docs.",
    "contact": {
      "name": "Glowjakt (Sejder AB)",
      "email": "kontakt@glowjakt.se",
      "url": "https://glowjakt.se/se/om-oss/"
    }
  },
  "security": [],
  "servers": [
    {
      "url": "https://api.glowjakt.se",
      "description": "Sweden (SEK) — glowjakt.se"
    },
    {
      "url": "https://api.glowjakt.no",
      "description": "Norway (NOK) — glowjakt.no"
    },
    {
      "url": "https://api.glowjagt.dk",
      "description": "Denmark (DKK) — glowjagt.dk"
    },
    {
      "url": "https://api.glowjakt.fi",
      "description": "Finland (EUR) — glowjakt.fi"
    },
    {
      "url": "https://api.glowscout.de",
      "description": "Germany (EUR) — glowscout.de (Glowscout)"
    }
  ],
  "paths": {
    "/api/products": {
      "get": {
        "operationId": "searchProducts",
        "summary": "Search or list products (EAN-grouped, lowest price first)",
        "description": "Each product appears once, at the lowest price among stores that currently have it in stock (or the lowest listed price if it is sold out everywhere). listing_count is the number of stores carrying the product (including sold out); offer_count is the number with it in stock right now. For a barcode query (see q), offer_count counts the store listings with that barcode, including sold-out ones, and listing_count is not set.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Free-text search (product, brand, type). A query of only digits (spaces and hyphens allowed) with 8-14 digits is first looked up as a barcode: EAN-8, EAN-13, UPC-A or GTIN-14, leading zeros optional, no check-digit validation. A match returns that product regardless of other filters and pagination. Without a match, a query of exactly 8 or 13 digits returns no products and any other digit query is treated as free text. For a barcode match, offer_count counts the store listings with that barcode, including sold-out ones, and listing_count is not set.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/Category"
            }
          },
          {
            "name": "max_price",
            "in": "query",
            "description": "Max price in the market's local currency.",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "key_ingredient",
            "in": "query",
            "description": "Only products containing any of these key-ingredient groups (slugs from /api/ingredients, comma-separated = OR). E.g. \"retinol\" or \"retinol,niacinamide\".",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "exclude_ingredient",
            "in": "query",
            "description": "Exclude products containing any of these key-ingredient groups (comma-separated). E.g. \"fragrance\" or \"sls,silicones\" for targeted free-from filtering.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "exclude_controversial",
            "in": "query",
            "description": "If true, exclude products containing any commonly-avoided ingredient (fragrance, parabens, silicones, SLS, mineral oil, formaldehyde, phenoxyethanol, propylene glycol, alcohol denat). Shortcut for exclude_ingredient with all 9 Controversial slugs.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "size",
            "in": "query",
            "description": "Results per page (default 20).",
            "schema": {
              "type": "integer",
              "maximum": 100
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "attribute",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Brand-level ethics/values filter (merges third-party certifications and the brand's own claims per bucket). Slugs: vegansk, djurtestfri, ekologisk, skonsam, hallbar. Comma-separated = AND. See /api/attributes for localized names and counts."
          }
        ],
        "responses": {
          "200": {
            "description": "Paged product list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductPage"
                }
              }
            }
          }
        }
      }
    },
    "/api/products/deals": {
      "get": {
        "operationId": "listDeals",
        "summary": "Products currently on sale, biggest genuine discounts first",
        "description": "Discounts are computed against real previous prices; stores known for inflated recommended prices are excluded from discount claims.",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/Category"
            }
          },
          {
            "name": "size",
            "in": "query",
            "schema": {
              "type": "integer",
              "maximum": 100
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paged deal list (same shape as product search)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductPage"
                }
              }
            }
          }
        }
      }
    },
    "/api/products/{slug}": {
      "get": {
        "operationId": "getProduct",
        "summary": "Single product with description and category",
        "parameters": [
          {
            "$ref": "#/components/parameters/Slug"
          }
        ],
        "responses": {
          "200": {
            "description": "Product detail",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductDetail"
                }
              }
            }
          },
          "404": {
            "description": "Unknown product slug"
          }
        }
      }
    },
    "/api/products/{slug}/offers": {
      "get": {
        "operationId": "getProductOffers",
        "summary": "Store offers for one product (the price comparison)",
        "description": "Offers are aggregated across the product's EAN group: the stores selling the same barcode. Listings the catalog classifies as outside its beauty scope are left out whenever the group also has in-scope listings. Sorted by price, in-stock first at equal price. is_cheapest marks the lowest-priced in-stock offer (the lowest-priced offer when none is in stock), the same price the product endpoint shows.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Slug"
          }
        ],
        "responses": {
          "200": {
            "description": "Offer list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OfferList"
                }
              }
            }
          },
          "404": {
            "description": "Unknown product slug"
          }
        }
      }
    },
    "/api/products/{slug}/price-history": {
      "get": {
        "operationId": "getPriceHistory",
        "summary": "Daily lowest-price history for a product",
        "description": "Default: daily lowest price across the product's store offers for the last 30 days (daily values follow the same scope rule as /offers). Pass `days` (7-90) for a different daily window, or `range` for long-horizon weekly resolution (weekly lowest price per week; the response then includes `resolution: \"weekly\"`). The final data point is the product's current price: the lowest in-stock offer when one exists.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Slug"
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 30,
              "minimum": 7,
              "maximum": 365
            },
            "description": "Daily-resolution window in days. Raw daily data is retained for 90 days; values above 90 return what exists. Ignored when `range` is set."
          },
          {
            "name": "range",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "6m",
                "1y",
                "max"
              ]
            },
            "description": "Long-horizon mode: weekly lowest price for the last 6 months, 1 year, or the full recorded history. Overrides `days`."
          }
        ],
        "responses": {
          "200": {
            "description": "Price history",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PriceHistory"
                }
              }
            }
          },
          "404": {
            "description": "Unknown product slug"
          }
        }
      }
    },
    "/api/brands": {
      "get": {
        "operationId": "listBrands",
        "summary": "Top brands by product count",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 12
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Brand list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BrandSummary"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/brands/{slug}": {
      "get": {
        "operationId": "getBrand",
        "summary": "Single brand with description",
        "parameters": [
          {
            "$ref": "#/components/parameters/Slug"
          }
        ],
        "responses": {
          "200": {
            "description": "Brand detail",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrandDetail"
                }
              }
            }
          },
          "404": {
            "description": "Unknown brand slug"
          }
        }
      }
    },
    "/api/brands/{slug}/market": {
      "get": {
        "operationId": "getBrandMarket",
        "summary": "Brand presence on this market: product count, stock, stores, price range, categories, cheapest product",
        "description": "Aggregated view of one brand on this country's market (the API host decides the country). Counts are EAN-grouped products; 'in_stock' is the subset currently in stock somewhere. 404 when the brand has no product rows on this market. Refreshed nightly.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Slug"
          }
        ],
        "responses": {
          "200": {
            "description": "Market summary for the brand",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrandMarket"
                }
              }
            }
          },
          "404": {
            "description": "Unknown brand slug, or no products on this market"
          }
        }
      }
    },
    "/api/categories": {
      "get": {
        "operationId": "listCategories",
        "summary": "Product categories with counts",
        "responses": {
          "200": {
            "description": "Category list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "category": {
                        "$ref": "#/components/schemas/Category"
                      },
                      "count": {
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/guides": {
      "get": {
        "operationId": "listGuides",
        "summary": "Brand price guides (editorial price overviews per brand)",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 2000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Guide list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/GuideSummary"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/guides/{slug}": {
      "get": {
        "operationId": "getGuide",
        "summary": "Single price guide with data and FAQ",
        "parameters": [
          {
            "$ref": "#/components/parameters/Slug"
          }
        ],
        "responses": {
          "200": {
            "description": "Guide detail (data and faq are JSON-encoded strings)"
          },
          "404": {
            "description": "Unknown guide slug"
          }
        }
      }
    },
    "/api/ingredients": {
      "get": {
        "operationId": "listIngredientGroups",
        "summary": "All 67 key-ingredient groups (for the key_ingredient filter)",
        "description": "Slugs are universal across all four markets; display names are localized. category='Controversial' is the avoid-bucket used by exclude_controversial.",
        "responses": {
          "200": {
            "description": "Ingredient group list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "slug": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string",
                        "description": "Localized display name."
                      },
                      "category": {
                        "type": "string",
                        "description": "Functional taxonomy, e.g. Hydration, Anti-aging, Controversial."
                      },
                      "tier": {
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/ingredients/{slug}": {
      "get": {
        "operationId": "getIngredientGroup",
        "summary": "Single ingredient group with description, FAQ and INCI variants",
        "parameters": [
          {
            "$ref": "#/components/parameters/Slug"
          }
        ],
        "responses": {
          "200": {
            "description": "Ingredient group content (name, description, meta_description, faq, inci_variants)"
          },
          "404": {
            "description": "Unknown ingredient slug"
          }
        }
      }
    },
    "/api/stats": {
      "get": {
        "operationId": "getStats",
        "summary": "Catalog statistics for this market",
        "responses": {
          "200": {
            "description": "Stats",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total_products": {
                      "type": "integer"
                    },
                    "total_brands": {
                      "type": "integer"
                    },
                    "total_stores": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/attributes": {
      "get": {
        "summary": "Attribute facets for the brand-level ethics/values filter",
        "description": "The five attribute buckets (slug, localized name for this market, number of brands). Drives the ?attribute= filter on /api/products.",
        "operationId": "getAttributes",
        "responses": {
          "200": {
            "description": "Attribute facet list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "attributes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "slug": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "brand_count": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "Slug": {
        "name": "slug",
        "in": "path",
        "required": true,
        "description": "URL slug as returned by search/list endpoints.",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "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."
          }
        }
      },
      "ProductPage": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer"
          },
          "page": {
            "type": "integer"
          },
          "size": {
            "type": "integer"
          },
          "products": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductSummary"
            }
          }
        }
      },
      "ProductDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ProductSummary"
          },
          {
            "type": "object",
            "properties": {
              "category": {
                "$ref": "#/components/schemas/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."
              }
            }
          }
        ]
      },
      "OfferList": {
        "type": "object",
        "properties": {
          "ean": {
            "type": [
              "string",
              "null"
            ]
          },
          "product_name": {
            "type": "string"
          },
          "brand": {
            "type": "string"
          },
          "image_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "offer_count": {
            "type": "integer",
            "description": "Total number of store offers listed below, including those out of stock."
          },
          "offers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "store_name": {
                  "type": "string"
                },
                "price": {
                  "type": "number"
                },
                "original_price": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "in_stock": {
                  "type": "boolean"
                },
                "affiliate_url": {
                  "type": "string",
                  "description": "Tracked buy link to this store (https://{market-domain}/go/{id}?t=...). Signed and valid for 48 hours from the response; it redirects (302) to the store."
                },
                "is_cheapest": {
                  "type": "boolean"
                },
                "content_value": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Package quantity in base unit (ml or g). German market (country=de) only — basis for the Grundpreis unit price (PAngV)."
                },
                "content_unit": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Base unit for content_value: \"ml\" or \"g\". German market only."
                }
              }
            }
          }
        }
      },
      "PriceHistory": {
        "type": "object",
        "properties": {
          "current_price": {
            "type": "number"
          },
          "lowest_price": {
            "type": "number"
          },
          "highest_price": {
            "type": "number"
          },
          "resolution": {
            "type": "string",
            "enum": [
              "weekly"
            ],
            "description": "Present only when `range` was used: each history point is the lowest price of that ISO week (dated by its Monday)."
          },
          "history": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string",
                  "format": "date"
                },
                "price": {
                  "type": "number"
                }
              }
            }
          }
        }
      },
      "BrandSummary": {
        "type": "object",
        "properties": {
          "brand": {
            "type": "string"
          },
          "brand_slug": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          },
          "website": {
            "type": [
              "string",
              "null"
            ]
          },
          "primary_logo_url": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "BrandMarket": {
        "type": "object",
        "description": "One brand's presence on this country's market (source: the same nightly price data as the product listings).",
        "properties": {
          "country": { "type": "string", "description": "Market code: se, no, dk, fi or de" },
          "currency": { "type": "string", "description": "SEK, NOK, DKK or EUR" },
          "slug": { "type": "string" },
          "name": { "type": "string" },
          "products": { "type": "integer", "description": "EAN-grouped products listed on this market" },
          "in_stock": { "type": "integer", "description": "Products currently in stock at at least one store" },
          "listings": { "type": "integer", "description": "Store listings across all products" },
          "on_sale": { "type": "integer", "description": "Products whose lowest current price is below the store's own reference price" },
          "stores": { "type": "integer" },
          "store_names": { "type": "array", "items": { "type": "string" }, "description": "Up to eight stores, most listings first" },
          "price": {
            "type": "object",
            "properties": {
              "min": { "type": ["number", "null"] },
              "median": { "type": ["number", "null"] },
              "max": { "type": ["number", "null"] }
            }
          },
          "categories": { "type": "array", "items": { "type": "object", "properties": { "slug": { "type": "string" }, "count": { "type": "integer" } } } },
          "subcategories": { "type": "array", "items": { "type": "object", "properties": { "path": { "type": "string" }, "count": { "type": "integer" } } } },
          "cheapest": {
            "type": ["object", "null"],
            "properties": {
              "name": { "type": "string" },
              "price": { "type": ["number", "null"] },
              "store": { "type": ["string", "null"] },
              "in_stock": { "type": "boolean" },
              "url": { "type": "string", "description": "Product page on the market site" }
            }
          },
          "certifications": { "type": "array", "items": { "type": "object" } },
          "claims": { "type": "array", "items": { "type": "string" } },
          "attributes": { "type": "array", "items": { "type": "string" } },
          "brand_url": { "type": "string", "description": "Brand page on the market site" },
          "ingredients": {
            "type": ["object", "null"],
            "description": "Ingredient profile across the brand's products on this market, from parsed INCI lists. Shares use products with a known ingredient list as denominator.",
            "properties": {
              "products": { "type": "integer" },
              "with_inci": { "type": "integer", "description": "Products with a parsed ingredient list" },
              "with_keys": { "type": "integer" },
              "top": { "type": "array", "description": "Most common key-ingredient groups (non-controversial), up to 8", "items": { "$ref": "#/components/schemas/IngredientRef" } },
              "controversial": { "type": "array", "description": "Groups flagged as possibly affecting sensitive skin, up to 5", "items": { "$ref": "#/components/schemas/IngredientRef" } },
              "shares": {
                "type": "object",
                "properties": {
                  "fragrance_free": { "type": ["number", "null"] },
                  "animal_free": { "type": ["number", "null"] },
                  "controversial_free": { "type": ["number", "null"] }
                }
              },
              "hub_url": { "type": ["string", "null"], "description": "Ingredient hub on the market site" }
            }
          },
          "as_of": { "type": "string", "format": "date", "description": "Day the block was computed (cached up to 1 h)." },
          "observed_at": { "type": ["string", "null"], "format": "date", "description": "Latest store-feed date among the brand's listings on this market — the data's observation day, distinct from as_of." }
        }
      },
      "IngredientRef": {
        "type": "object",
        "properties": {
          "slug": { "type": "string" },
          "name": { "type": "string", "description": "Localized group name" },
          "category": { "type": ["string", "null"] },
          "tier": { "type": ["integer", "null"] },
          "count": { "type": "integer", "description": "Products in the brand's range containing the group" },
          "url": { "type": ["string", "null"], "description": "Ingredient page on the market site, when published" }
        }
      },
      "BrandDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "website": {
            "type": [
              "string",
              "null"
            ]
          },
          "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 this brand's price guide when one exists in this market (GET /api/guides/{slug}); null otherwise."
          }
        }
      },
      "GuideSummary": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "guide_type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "brand_slug": {
            "type": "string"
          }
        }
      }
    }
  }
}