{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://choicera.com/integration/v1/json-feed.schema.json",
  "title": "Choicera JSON-Feed v1",
  "type": "object",
  "properties": {
    "generated_at": {
      "type": "string",
      "description": "Erzeugungszeitpunkt des Feeds (ISO 8601). Wird zur Frischepruefung ausgewertet."
    },
    "shop": {
      "type": "string",
      "description": "Storefront-Origin, etwa https://www.beispielshop.de"
    },
    "currency": {
      "type": "string",
      "description": "Informativ. Verrechnet wird in der fuer die Marke hinterlegten Waehrung."
    },
    "product_count": {
      "type": "number",
      "description": "Informativ. Dient dem Abgleich gegen die Anzahl der Eintraege in products."
    },
    "products": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/product"
      }
    }
  },
  "required": [
    "products"
  ],
  "additionalProperties": true,
  "description": "Produkt-Feed, den ein Partner-Shop unter einer HTTPS-URL bereitstellt und den Choicera regelmaessig abruft. Ein Dokument mit allen Produkten, Antwortzeit unter 20 Sekunden.",
  "$defs": {
    "product": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "product_number": {
          "type": "string",
          "minLength": 1,
          "description": "Artikelnummer, Schluessel des Produkts. Muss ueber Feed-Generierungen hinweg stabil bleiben. Fehlt sie, wird das Produkt verworfen."
        },
        "title": {
          "type": "string",
          "minLength": 1,
          "description": "Anzeigename. Fehlt er, wird das Produkt verworfen."
        },
        "url": {
          "type": "string",
          "description": "Produktdetailseite auf der vereinbarten Storefront-Domain. Liegt die URL auf einer anderen Domain oder laesst sie sich nicht parsen, wird nur der Produktlink verworfen, nicht das Produkt."
        },
        "vendor": {
          "type": "string"
        },
        "darreichungsform": {
          "type": "string",
          "description": "Kapseln, Tabletten, Pulver, Tropfen, Spray, Sachets, Set, Testkit."
        },
        "tags": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "image": {
          "type": "string",
          "description": "Titelbild."
        },
        "images": {
          "description": "Rohformat des Shops (z. B. Liste von Bildobjekten), wird unveraendert durchgereicht."
        },
        "body_html": {
          "type": "string",
          "description": "Beschreibung als HTML, wie auf der Detailseite: Zutaten, Naehrwerttabelle als echtes table-Element, Verzehrempfehlung unter einer h3-Zwischenueberschrift."
        },
        "has_nutrient_table": {
          "type": "boolean"
        },
        "health_claims": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "status": {
                  "description": "\"done\" und \"keine_claims\" sind empfehlbare Nahrungsergaenzung, \"bundle\" ist ein Set, \"kein_nem\" deckt Testkits, Zubehoer und Kosmetik ab. Jeder andere Wert und ein fehlendes health_claims-Objekt werden wie \"kein_nem\" behandelt: das Produkt wird importiert, aber nie empfohlen (kind=\"other\", needs-review)."
                }
              },
              "additionalProperties": true
            },
            {
              "type": "null"
            }
          ]
        },
        "variants": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/variant"
          },
          "description": "Auch ein Produkt ohne echte Varianten hat idealerweise genau einen Eintrag. Ohne mindestens eine Variante mit numerischem Preis wird das Produkt verworfen."
        }
      },
      "required": [
        "product_number",
        "title"
      ],
      "additionalProperties": true
    },
    "variant": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "description": "Interne ID oder UUID des Shops."
        },
        "product_number": {
          "type": "string"
        },
        "sku": {
          "type": "string",
          "description": "Eindeutig je Variante und derselbe Wert, den Choicera im Warenkorb-Link zuruecksendet."
        },
        "title": {
          "type": "string",
          "description": "Groessenbezeichnung, etwa 120 Kapseln oder 500 g."
        },
        "price": {
          "type": [
            "number",
            "string"
          ],
          "description": "Bruttopreis. Eine JSON-Zahl groesser 0 gilt direkt; Zeichenketten der Form \"3.95\" oder \"3,95\" werden zur Sicherheit angenommen. Jeder andere Wert zaehlt als kein Preis, die Variante wird dann ohne Preis, aber nicht das Produkt verworfen."
        },
        "currency": {
          "type": "string"
        },
        "available": {
          "type": "boolean",
          "description": "Lieferbar. Fehlt das Feld, gilt die Variante als nicht lieferbar."
        },
        "servings_per_container": {
          "type": "number",
          "description": "Portionen je Packung. Fehlt das Feld, wird die Zahl aus dem Produkttitel gelesen, was bei Pulvern und Fluessigkeiten scheitert."
        },
        "serving_size": {
          "type": "string"
        },
        "net_quantity": {
          "type": "object",
          "properties": {
            "value": {
              "type": "number"
            },
            "unit": {
              "type": "string"
            }
          },
          "required": [
            "value",
            "unit"
          ],
          "additionalProperties": true,
          "description": "Fuellmenge einer Variante, Grundlage des Preis-pro-Portion-Vergleichs."
        },
        "gtin": {
          "type": "string"
        },
        "ean": {
          "type": "string"
        }
      },
      "additionalProperties": true
    }
  }
}
