{
  "openapi": "3.1.0",
  "info": {
    "title": "foodoracle Engine API",
    "version": "1.0.0",
    "description": "Deterministische Food-Intelligenz auf Basis einer kuratierten Zutaten-Bibliothek —\nmultilinguale Suche, Allergen-/Unverträglichkeits-Checks, Substitution & Pairing\nsowie Nährwertanalyse. Alle Kern-Endpoints sind deterministisch (kein LLM):\nreproduzierbar, erklärbar, in Millisekunden.\n\n## Authentifizierung\nJeder `/v1`-Endpoint (außer `/v1/health`) benötigt einen API-Key — entweder als\n`Authorization: Bearer fo_live_…` oder als `X-API-Key: fo_live_…`. Keys werden vom\nBetreiber pro Kunde ausgegeben.\n\n## Module & Abrechnung\nFunktionen sind Modulen zugeordnet und werden pro Kunde gemessen:\n- **M1 Search & Data** — Suche, Detail-Lookup, Autocomplete, Stammdaten\n- **M2 Compatibility & Health** — Allergene, Diäten, FODMAP, Histamin\n- **M3 Kitchen Intelligence** — Substitution, Pairing, Ähnlichkeit, Einheiten-Umrechnung\n- **M4 Nutrition & Fitness** — Nährwerte pro Zutat und pro Rezept\n\nIst ein Modul nicht gebucht, antwortet der Endpoint mit `403`.\n",
    "contact": {
      "name": "foodoracle",
      "email": "carlosaguilar@gmx.net"
    }
  },
  "servers": [
    {
      "url": "https://api.foodoracle.app",
      "description": "Production"
    },
    {
      "url": "http://localhost:8080",
      "description": "Lokale Entwicklung"
    }
  ],
  "security": [
    {
      "ApiKeyBearer": []
    },
    {
      "ApiKeyHeader": []
    }
  ],
  "tags": [
    {
      "name": "Search & Data (M1)",
      "description": "Multilinguale Zutatensuche, Detail-Lookup, Autocomplete, Stammdaten."
    },
    {
      "name": "Compatibility & Health (M2)",
      "description": "Allergen-, Diät-, FODMAP- und Histamin-Prüfungen auf den kuratierten Profilen."
    },
    {
      "name": "Kitchen Intelligence (M3)",
      "description": "Substitution, Flavor-Pairing, Ähnlichkeit und Einheiten-Umrechnung."
    },
    {
      "name": "Nutrition & Fitness (M4)",
      "description": "Nährwerte pro Zutat und pro Rezept (pro Portion)."
    },
    {
      "name": "Account",
      "description": "Self-Service — eigener API-Verbrauch."
    },
    {
      "name": "Health",
      "description": "Healthchecks (ohne Auth)."
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "Health"
        ],
        "summary": "Healthcheck",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "example": "OK"
                }
              }
            }
          }
        }
      }
    },
    "/v1/health": {
      "get": {
        "tags": [
          "Health"
        ],
        "summary": "Healthcheck (versioniert)",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "example": "OK"
                }
              }
            }
          }
        }
      }
    },
    "/v1/ingredients/search": {
      "get": {
        "tags": [
          "Search & Data (M1)"
        ],
        "summary": "Zutatensuche (Hybrid Keyword + semantisch)",
        "description": "MeiliSearch-Hybridsuche; Treffer werden zu vollständigen Zutaten aufgelöst.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/Lang"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Suchergebnis",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IngredientListSearchResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/ingredients/autocomplete": {
      "get": {
        "tags": [
          "Search & Data (M1)"
        ],
        "summary": "Typo-tolerante Vorschläge",
        "description": "Leichtgewichtige Vorschlagsliste (nur ID + Name) für Eingabefelder.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/Lang"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 10,
              "maximum": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Vorschläge",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutocompleteResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/ingredients/count": {
      "get": {
        "tags": [
          "Search & Data (M1)"
        ],
        "summary": "Anzahl Zutaten-Einträge",
        "responses": {
          "200": {
            "description": "Anzahl als Text",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "example": "Die Tabelle hat 6357 Einträge."
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/ingredients/{canonicalId}": {
      "get": {
        "tags": [
          "Search & Data (M1)"
        ],
        "summary": "Detail-Lookup — volles Zutaten-Profil",
        "description": "Das vollständige Profil (Allergene, Flavor, Physik, Relationen) als Datenprodukt.",
        "parameters": [
          {
            "$ref": "#/components/parameters/CanonicalId"
          },
          {
            "$ref": "#/components/parameters/Lang"
          }
        ],
        "responses": {
          "200": {
            "description": "Zutat",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IngredientDetailResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/meta/categories": {
      "get": {
        "tags": [
          "Search & Data (M1)"
        ],
        "summary": "Vorkommende Kategorien",
        "responses": {
          "200": {
            "description": "Kategorien",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "categories": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/meta/units": {
      "get": {
        "tags": [
          "Search & Data (M1)"
        ],
        "summary": "Verfügbare Einheiten (lokalisiert)",
        "parameters": [
          {
            "$ref": "#/components/parameters/Lang"
          }
        ],
        "responses": {
          "200": {
            "description": "Einheiten",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnitsResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/meta/languages": {
      "get": {
        "tags": [
          "Search & Data (M1)"
        ],
        "summary": "Unterstützte Sprach-Codes",
        "responses": {
          "200": {
            "description": "Sprachen",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "languages": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "example": [
                        "de",
                        "en",
                        "es"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/ingredients/{canonicalId}/allergens": {
      "get": {
        "tags": [
          "Compatibility & Health (M2)"
        ],
        "summary": "Allergen-/Unverträglichkeitsprofil einer Zutat",
        "description": "Das 14-Dimensionen-Profil inkl. Histamin, FODMAP und Pankreas.",
        "parameters": [
          {
            "$ref": "#/components/parameters/CanonicalId"
          },
          {
            "$ref": "#/components/parameters/Lang"
          }
        ],
        "responses": {
          "200": {
            "description": "Allergenprofil",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ingredientId": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "allergenProfile": {
                      "$ref": "#/components/schemas/AllergenProfile"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/compatibility/check": {
      "post": {
        "tags": [
          "Compatibility & Health (M2)"
        ],
        "summary": "Zutatenliste über alle 14 Domänen prüfen",
        "description": "Bewertet eine Zutatenliste über alle Unverträglichkeits-Domänen. Mit\n`concerns` (z. B. `[\"gluten\",\"histamine\"]`) kommen klare Urteile zurück:\n`compatible | caution | incompatible | uncertain`. Ohne Mengenangabe wird\nmit 100 g gerechnet (`amountAssumed = true`).\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CompatibilityCheckRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bewertung",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompatibilityCheckResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/compatibility/check/batch": {
      "post": {
        "tags": [
          "Compatibility & Health (M2)"
        ],
        "summary": "Bis zu 100 Compatibility-Checks in einem Request (Credits = Anzahl × 5)",
        "description": "Führt bis zu 100 einzelne Compatibility-Checks synchron in einem Request aus.\nJedes Element entspricht einem Aufruf von `/v1/compatibility/check`; `concerns`\nauf Top-Ebene gilt als Default für alle Elemente und wird von Element-`concerns`\nüberstimmt. Die Ergebnisse kommen in Anfrage-Reihenfolge (`index`); Fehler eines\nElements (z. B. unbekannte Concerns) betreffen nur dieses Element (`error` statt\n`result`), unbekannte Zutaten stehen wie beim Einzel-Check in `unresolved`.\nLeere Batches und mehr als 100 Checks werden mit 400 abgelehnt (`EMPTY_BATCH` /\n`BATCH_TOO_LARGE`). Abgerechnet werden Anzahl Checks × 5 Credits (M2-Gewicht);\nfür das Rate-Limit zählt der Batch als ein Request.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchCheckRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ergebnisse in Anfrage-Reihenfolge (auch bei Element-Fehlern HTTP 200)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchCheckResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/compatibility/diet": {
      "post": {
        "tags": [
          "Compatibility & Health (M2)"
        ],
        "summary": "Diät-Prüfung (vegan/vegetarian/pescetarian/porkFree)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "diet",
                  "ingredientIds"
                ],
                "properties": {
                  "diet": {
                    "type": "string",
                    "enum": [
                      "vegan",
                      "vegetarian",
                      "pescetarian",
                      "porkFree"
                    ]
                  },
                  "ingredientIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Diät-Ergebnis",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DietCheckResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/compatibility/fodmap": {
      "post": {
        "tags": [
          "Compatibility & Health (M2)"
        ],
        "summary": "FODMAP-Bewertung inkl. sicherer Portionsgrößen",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ItemsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "FODMAP-Ergebnis",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FodmapCheckResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/compatibility/histamine": {
      "post": {
        "tags": [
          "Compatibility & Health (M2)"
        ],
        "summary": "Histamin-Bewertung inkl. DAO-Effekt & Schwellenwerten",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ItemsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Histamin-Ergebnis",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HistamineCheckResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/ingredients/{canonicalId}/substitutes": {
      "get": {
        "tags": [
          "Kitchen Intelligence (M3)"
        ],
        "summary": "Substitutions-Vorschläge (Food-Graph)",
        "parameters": [
          {
            "$ref": "#/components/parameters/CanonicalId"
          },
          {
            "$ref": "#/components/parameters/Lang"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "name": "includePartial",
            "in": "query",
            "description": "Teilweise geeignete Substitute mit einbeziehen (Default true).",
            "schema": {
              "type": "boolean",
              "default": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Substitute",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubstitutesResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/NoRelationships"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/ingredients/{canonicalId}/pairings": {
      "get": {
        "tags": [
          "Kitchen Intelligence (M3)"
        ],
        "summary": "Flavor-Pairings + häufige Ko-Nutzung",
        "parameters": [
          {
            "$ref": "#/components/parameters/CanonicalId"
          },
          {
            "$ref": "#/components/parameters/Lang"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Pairings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PairingsResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/NoRelationships"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/ingredients/{canonicalId}/similar": {
      "get": {
        "tags": [
          "Kitchen Intelligence (M3)"
        ],
        "summary": "Ähnliche Zutaten (Familie + funktional)",
        "parameters": [
          {
            "$ref": "#/components/parameters/CanonicalId"
          },
          {
            "$ref": "#/components/parameters/Lang"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Ähnliche Zutaten",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SimilarResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/NoRelationships"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/ingredients/{canonicalId}/profile": {
      "get": {
        "tags": [
          "Kitchen Intelligence (M3)"
        ],
        "summary": "Kulinarische Profile einer Zutat",
        "description": "Die kuratierten kulinarischen Profile (Gar-Verhalten, Flavor, Textur, funktionale Rollen, Anwendung) plus Datenlage-Confidence. Bewusst NICHT im M1-Detail — modul-gegated (M3).",
        "parameters": [
          {
            "$ref": "#/components/parameters/CanonicalId"
          },
          {
            "$ref": "#/components/parameters/Lang"
          }
        ],
        "responses": {
          "200": {
            "description": "Kulinarische Profile",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IngredientProfileResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/units/convert": {
      "post": {
        "tags": [
          "Kitchen Intelligence (M3)"
        ],
        "summary": "Einheiten-Umrechnung einer Zutat",
        "description": "Gewicht ↔ Volumen ↔ Stück über Dichte und Stückgewicht.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "ingredientId",
                  "amount",
                  "fromUnit",
                  "toUnit"
                ],
                "properties": {
                  "ingredientId": {
                    "type": "string",
                    "example": "butter"
                  },
                  "amount": {
                    "type": "number",
                    "example": 1
                  },
                  "fromUnit": {
                    "type": "string",
                    "example": "tablespoon"
                  },
                  "toUnit": {
                    "type": "string",
                    "example": "gram"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Umrechnungsergebnis",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnitConvertResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/ingredients/{canonicalId}/nutrition": {
      "get": {
        "tags": [
          "Nutrition & Fitness (M4)"
        ],
        "summary": "Nährwerte einer Zutat (pro 100 g)",
        "description": "Mit `amount` + `unit` wird zusätzlich die umgerechnete Menge geliefert.",
        "parameters": [
          {
            "$ref": "#/components/parameters/CanonicalId"
          },
          {
            "name": "amount",
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "unit",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "piece"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Nährwerte",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IngredientNutritionResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/nutrition/analyze": {
      "post": {
        "tags": [
          "Nutrition & Fitness (M4)"
        ],
        "summary": "Nährwertanalyse einer Zutatenliste",
        "description": "Summe und — bei `servings` — Werte pro Portion. Nicht auflösbare Positionen unter `unresolved`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "items"
                ],
                "properties": {
                  "servings": {
                    "type": "integer",
                    "nullable": true
                  },
                  "items": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "ingredientId",
                        "amount",
                        "unit"
                      ],
                      "properties": {
                        "ingredientId": {
                          "type": "string"
                        },
                        "amount": {
                          "type": "number"
                        },
                        "unit": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Analyse",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NutritionAnalyzeResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/account/usage": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Eigener API-Verbrauch pro Tag/Funktion",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 30,
              "maximum": 365
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Verbrauch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyBearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "API-Key als `Authorization: Bearer fo_live_…`"
      },
      "ApiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "API-Key als `X-API-Key: fo_live_…`"
      }
    },
    "parameters": {
      "Query": {
        "name": "q",
        "in": "query",
        "required": true,
        "schema": {
          "type": "string"
        },
        "example": "tomate"
      },
      "Lang": {
        "name": "lang",
        "in": "query",
        "description": "Sprach-Code (de | en | es). Default en.",
        "schema": {
          "type": "string",
          "default": "en"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "default": 10
        }
      },
      "CanonicalId": {
        "name": "canonicalId",
        "in": "path",
        "required": true,
        "description": "Kanonische Zutaten-ID.",
        "schema": {
          "type": "string"
        },
        "example": "parmesan"
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Kein/ungültiger API-Key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Modul nicht gebucht oder Account gesperrt.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Ressource nicht gefunden.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "BadRequest": {
        "description": "Ungültige Eingabe.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "Monats-Kontingent bzw. Spending-Cap erreicht (`QUOTA_EXCEEDED` / `SPENDING_CAP_REACHED`). Beim Batch fließt die Anzahl der Checks × Modul-Gewicht in die Prüfung ein.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NoRelationships": {
        "description": "Für diese Zutat sind keine Relationship-Daten hinterlegt.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate-Limit überschritten (siehe `Retry-After`).",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Limit": {
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "example": "ENGINE_ERROR"
              },
              "message": {
                "type": "string"
              },
              "details": {
                "type": "object",
                "nullable": true,
                "additionalProperties": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "IngredientListSearchResponse": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string"
          },
          "language": {
            "type": "string"
          },
          "limit": {
            "type": "integer",
            "nullable": true
          },
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "confidence": {
                  "type": "number"
                },
                "ingredient": {
                  "$ref": "#/components/schemas/Ingredient"
                }
              }
            }
          }
        }
      },
      "AutocompleteResponse": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string"
          },
          "language": {
            "type": "string"
          },
          "suggestions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "ingredientId": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "IngredientDetailResponse": {
        "type": "object",
        "description": "M1-Detail: NUR Basisdaten (Identität, Kategorie, Einheiten, Physik).\nDie kuratierten Profile werden hier bewusst NICHT ausgeliefert — Allergene\nüber M2 (`/allergens`), kulinarische Profile über M3 (`/profile`). So gibt der\nbillige M1-Endpoint nicht den ganzen kuratierten Datensatz preis.\n",
        "properties": {
          "ingredientId": {
            "type": "string"
          },
          "language": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "synonyms": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "category": {
            "type": "string"
          },
          "subcategory": {
            "type": "string",
            "nullable": true
          },
          "mainIngredient": {
            "type": "string",
            "nullable": true
          },
          "allowedUnits": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "unitCategories": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "pieceWeight": {
            "type": "number"
          },
          "density": {
            "type": "number"
          },
          "cuisineTags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "cuisineStrength": {
            "type": "string"
          },
          "isProcessed": {
            "type": "boolean"
          },
          "animalSources": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "variantType": {
            "type": "string"
          }
        }
      },
      "IngredientProfileResponse": {
        "type": "object",
        "description": "M3-Profil: die kuratierten kulinarischen Profile einer Zutat (Gar-Verhalten,\nFlavor-, Textur-, funktionale und Anwendungs-Achsen) plus Datenlage-Confidence.\nDie Profil-Objekte nutzen snake_case-Keys.\n",
        "properties": {
          "ingredientId": {
            "type": "string"
          },
          "cookProfile": {
            "type": "object",
            "additionalProperties": true
          },
          "flavorProfile": {
            "$ref": "#/components/schemas/FlavorProfile"
          },
          "textureProfile": {
            "type": "object",
            "additionalProperties": true
          },
          "functionalProfile": {
            "type": "object",
            "additionalProperties": true
          },
          "applicationProfile": {
            "type": "object",
            "additionalProperties": true
          },
          "profileConfidence": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "Ingredient": {
        "type": "object",
        "description": "Vollständige Zutat. Profil-Felder verwenden snake_case-Keys\n(`allergen_profile`, `flavor_profile`, `ingredient_relationships`).\n",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "synonyms": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "category": {
            "type": "string",
            "nullable": true
          },
          "subcategory": {
            "type": "string",
            "nullable": true
          },
          "density": {
            "type": "number"
          },
          "piece_weight": {
            "type": "number"
          },
          "allowed_units": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "animal_source": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "allergen_profile": {
            "$ref": "#/components/schemas/AllergenProfile"
          },
          "flavor_profile": {
            "$ref": "#/components/schemas/FlavorProfile"
          },
          "ingredient_relationships": {
            "$ref": "#/components/schemas/Relationships"
          }
        },
        "additionalProperties": true
      },
      "UnitsResponse": {
        "type": "object",
        "properties": {
          "language": {
            "type": "string"
          },
          "units": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "example": "teaspoon"
                },
                "name": {
                  "type": "string",
                  "example": "Teelöffel"
                },
                "shortcut": {
                  "type": "string",
                  "example": "TL"
                },
                "precision": {
                  "type": "string",
                  "enum": [
                    "weight",
                    "exact",
                    "qualitative",
                    "estimated"
                  ]
                }
              }
            }
          }
        }
      },
      "ItemsRequest": {
        "type": "object",
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompatibilityItem"
            }
          }
        }
      },
      "CompatibilityCheckRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ItemsRequest"
          },
          {
            "type": "object",
            "properties": {
              "concerns": {
                "type": "array",
                "nullable": true,
                "description": "z. B. [\"gluten\", \"histamine\", \"fodmap\"]",
                "items": {
                  "type": "string"
                }
              }
            }
          }
        ]
      },
      "BatchCheckRequest": {
        "type": "object",
        "required": [
          "checks"
        ],
        "properties": {
          "checks": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "description": "Bis zu 100 einzelne Checks — jedes Element wie ein Aufruf von /v1/compatibility/check.",
            "items": {
              "$ref": "#/components/schemas/CompatibilityCheckRequest"
            }
          },
          "concerns": {
            "type": "array",
            "nullable": true,
            "description": "Default-Concerns für alle Elemente; Element-`concerns` überstimmen.",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "BatchCheckResponse": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "description": "Ein Ergebnis pro Element, in Anfrage-Reihenfolge.",
            "items": {
              "$ref": "#/components/schemas/BatchCheckResult"
            }
          }
        }
      },
      "BatchCheckResult": {
        "type": "object",
        "required": [
          "index"
        ],
        "description": "Pro Element gilt: `result` XOR `error` — nie beides.",
        "properties": {
          "index": {
            "type": "integer",
            "description": "Position im Request (0-basiert)"
          },
          "result": {
            "$ref": "#/components/schemas/CompatibilityCheckResponse"
          },
          "error": {
            "type": "object",
            "nullable": true,
            "properties": {
              "code": {
                "type": "string",
                "description": "INVALID_CONCERN | EMPTY_ITEMS"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "CompatibilityItem": {
        "type": "object",
        "required": [
          "ingredientId"
        ],
        "properties": {
          "ingredientId": {
            "type": "string"
          },
          "amount": {
            "type": "number",
            "nullable": true,
            "description": "Ohne Angabe: 100 g"
          },
          "unit": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "EvaluatedItem": {
        "type": "object",
        "properties": {
          "ingredientId": {
            "type": "string"
          },
          "grams": {
            "type": "number"
          },
          "amountAssumed": {
            "type": "boolean"
          }
        }
      },
      "Unresolved": {
        "type": "object",
        "properties": {
          "ingredientId": {
            "type": "string"
          },
          "reason": {
            "type": "string"
          }
        }
      },
      "CompatibilityCheckResponse": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvaluatedItem"
            }
          },
          "unresolved": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Unresolved"
            }
          },
          "evaluation": {
            "type": "object",
            "description": "Ergebnis pro Domäne (histamine, lactose, gluten, …, fodmap, pancreas, dairy).",
            "additionalProperties": true
          },
          "concerns": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "object",
              "properties": {
                "concern": {
                  "type": "string"
                },
                "risk": {
                  "type": "string"
                },
                "verdict": {
                  "type": "string",
                  "enum": [
                    "compatible",
                    "caution",
                    "incompatible",
                    "uncertain"
                  ]
                },
                "confidence": {
                  "type": "string",
                  "description": "Schwächste kuratierte Datenlage der Domäne. Bei 'low' werden echte Risiken zu 'uncertain' aufgeweicht.",
                  "enum": [
                    "low",
                    "medium",
                    "high",
                    "unknown"
                  ]
                }
              }
            }
          }
        }
      },
      "DietCheckResponse": {
        "type": "object",
        "properties": {
          "diet": {
            "type": "string"
          },
          "verdict": {
            "type": "string",
            "enum": [
              "compatible",
              "incompatible",
              "uncertain"
            ]
          },
          "violations": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "ingredientId": {
                  "type": "string"
                },
                "sources": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "uncertain": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "unresolved": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "FodmapCheckResponse": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvaluatedItem"
            }
          },
          "unresolved": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Unresolved"
            }
          },
          "result": {
            "type": "object",
            "nullable": true,
            "properties": {
              "level": {
                "type": "string",
                "enum": [
                  "none",
                  "low",
                  "moderate",
                  "high",
                  "possible",
                  "unknown"
                ]
              },
              "dominantGroups": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "hasUnknown": {
                "type": "boolean"
              },
              "confidence": {
                "type": "string",
                "description": "Schwächste kuratierte Datenlage der Domäne über alle bewerteten Zutaten.",
                "enum": [
                  "low",
                  "medium",
                  "high",
                  "unknown"
                ]
              }
            }
          },
          "ingredients": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "ingredientId": {
                  "type": "string"
                },
                "profile": {
                  "$ref": "#/components/schemas/FodmapProfile"
                }
              }
            }
          }
        }
      },
      "HistamineCheckResponse": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvaluatedItem"
            }
          },
          "unresolved": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Unresolved"
            }
          },
          "result": {
            "type": "object",
            "nullable": true,
            "properties": {
              "risk": {
                "type": "string",
                "enum": [
                  "none",
                  "low",
                  "moderate",
                  "high",
                  "unknown"
                ]
              },
              "topContributors": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "containsFermented": {
                "type": "boolean"
              },
              "containsAged": {
                "type": "boolean"
              },
              "containsLiberators": {
                "type": "boolean"
              },
              "hasUnknown": {
                "type": "boolean"
              },
              "confidence": {
                "type": "string",
                "description": "Schwächste kuratierte Datenlage der Domäne über alle bewerteten Zutaten.",
                "enum": [
                  "low",
                  "medium",
                  "high",
                  "unknown"
                ]
              }
            }
          },
          "ingredients": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "ingredientId": {
                  "type": "string"
                },
                "profile": {
                  "$ref": "#/components/schemas/HistamineProfile"
                }
              }
            }
          }
        }
      },
      "AllergenProfile": {
        "type": "object",
        "description": "14 Unverträglichkeits-Dimensionen. Standard-Allergene (gluten, lactose,\npeanut, …) folgen einem Präsenz-Schema; Histamin, FODMAP und Pankreas\nsind eigenständig modelliert. Jede Dimension kann null sein.\n",
        "properties": {
          "histamine": {
            "$ref": "#/components/schemas/HistamineProfile"
          },
          "fodmap": {
            "$ref": "#/components/schemas/FodmapProfile"
          },
          "pancreas": {
            "$ref": "#/components/schemas/PancreasProfile"
          },
          "gluten": {
            "$ref": "#/components/schemas/AllergenSubProfile"
          },
          "lactose": {
            "$ref": "#/components/schemas/AllergenSubProfile"
          },
          "dairy": {
            "$ref": "#/components/schemas/AllergenSubProfile"
          },
          "egg": {
            "$ref": "#/components/schemas/AllergenSubProfile"
          },
          "soy": {
            "$ref": "#/components/schemas/AllergenSubProfile"
          },
          "peanut": {
            "$ref": "#/components/schemas/AllergenSubProfile"
          },
          "treeNut": {
            "$ref": "#/components/schemas/AllergenSubProfile"
          },
          "sesame": {
            "$ref": "#/components/schemas/AllergenSubProfile"
          },
          "sulfite": {
            "$ref": "#/components/schemas/AllergenSubProfile"
          },
          "fish": {
            "$ref": "#/components/schemas/AllergenSubProfile"
          },
          "shellfish": {
            "$ref": "#/components/schemas/AllergenSubProfile"
          }
        }
      },
      "AllergenSubProfile": {
        "type": "object",
        "nullable": true,
        "description": "Repräsentatives Schema der Standard-Allergene (Felder je Domäne leicht variierend).",
        "properties": {
          "crossContactRisk": {
            "type": "string"
          },
          "processed": {
            "type": "boolean"
          },
          "thresholdGrams": {
            "type": "number",
            "nullable": true
          },
          "confidence": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ]
          }
        },
        "additionalProperties": true
      },
      "HistamineProfile": {
        "type": "object",
        "nullable": true,
        "properties": {
          "histamineContent": {
            "type": "string",
            "enum": [
              "high",
              "moderate",
              "low",
              "none",
              "unknown"
            ]
          },
          "liberator": {
            "type": "string",
            "enum": [
              "no",
              "suspected",
              "yes"
            ]
          },
          "daoEffect": {
            "type": "string",
            "enum": [
              "none",
              "weak",
              "strong"
            ]
          },
          "fermented": {
            "type": "boolean"
          },
          "aged": {
            "type": "boolean"
          },
          "thresholdGrams": {
            "type": "number",
            "nullable": true
          },
          "confidence": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ]
          }
        }
      },
      "FodmapProfile": {
        "type": "object",
        "nullable": true,
        "properties": {
          "fodmapLevel": {
            "type": "string",
            "enum": [
              "none",
              "low",
              "moderate",
              "high",
              "possible",
              "unknown"
            ]
          },
          "fodmapGroups": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "oligosaccharides",
                "disaccharides",
                "monosaccharides",
                "polyols",
                "combinations"
              ]
            }
          },
          "safeServingGrams": {
            "type": "number",
            "nullable": true
          },
          "processed": {
            "type": "boolean"
          },
          "confidence": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ]
          }
        }
      },
      "PancreasProfile": {
        "type": "object",
        "nullable": true,
        "properties": {
          "pancreasRisk": {
            "type": "string",
            "enum": [
              "low",
              "moderate",
              "high",
              "unknown"
            ]
          },
          "fatLoad": {
            "type": "string"
          },
          "fiberLoad": {
            "type": "string"
          },
          "friedOrGreasy": {
            "type": "boolean"
          },
          "alcohol": {
            "type": "boolean"
          },
          "safeServingGrams": {
            "type": "number",
            "nullable": true
          },
          "confidence": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ]
          }
        }
      },
      "Relationships": {
        "type": "object",
        "description": "Food-Graph. Edges tragen `ingredientID`, `score` und `reasons`.",
        "properties": {
          "substitutesWell": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RelationshipEdge"
            }
          },
          "substitutesPartially": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RelationshipEdge"
            }
          },
          "pairsWellWith": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RelationshipEdge"
            }
          },
          "sameFamilyAs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RelationshipEdge"
            }
          },
          "functionallySimilarTo": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RelationshipEdge"
            }
          },
          "oftenUsedWith": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "RelationshipEdge": {
        "type": "object",
        "properties": {
          "ingredientID": {
            "type": "string"
          },
          "score": {
            "type": "number"
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "SubstitutesResponse": {
        "type": "object",
        "properties": {
          "ingredientId": {
            "type": "string"
          },
          "language": {
            "type": "string"
          },
          "substitutes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "ingredientId": {
                  "type": "string"
                },
                "name": {
                  "type": "string",
                  "nullable": true
                },
                "score": {
                  "type": "number"
                },
                "quality": {
                  "type": "string",
                  "enum": [
                    "well",
                    "partial"
                  ]
                },
                "reasons": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "confidence": {
                  "type": "number",
                  "description": "Datenlage-Gewichtung der Empfehlung (0…1) aus ProfileConfidence."
                },
                "fit": {
                  "type": "number",
                  "description": "Gesamt-Rang: kuratierter Graph-Score ⊕ Achsen-Distanz ⊕ Confidence. Bestimmt die Reihenfolge innerhalb einer Qualitätsstufe; 'score' bleibt der reine Graph-Score."
                }
              }
            }
          }
        }
      },
      "PairingsResponse": {
        "type": "object",
        "properties": {
          "ingredientId": {
            "type": "string"
          },
          "language": {
            "type": "string"
          },
          "pairsWellWith": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "ingredientId": {
                  "type": "string"
                },
                "name": {
                  "type": "string",
                  "nullable": true
                },
                "score": {
                  "type": "number"
                },
                "reasons": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "confidence": {
                  "type": "number",
                  "description": "Datenlage-Gewichtung der Partner-Zutat (0…1). Pairing wird nicht nach Ähnlichkeit umsortiert."
                }
              }
            }
          },
          "oftenUsedWith": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "ingredientId": {
                  "type": "string"
                },
                "name": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          }
        }
      },
      "SimilarResponse": {
        "type": "object",
        "properties": {
          "ingredientId": {
            "type": "string"
          },
          "language": {
            "type": "string"
          },
          "similar": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "ingredientId": {
                  "type": "string"
                },
                "name": {
                  "type": "string",
                  "nullable": true
                },
                "score": {
                  "type": "number"
                },
                "relation": {
                  "type": "string",
                  "enum": [
                    "sameFamily",
                    "functionallySimilar"
                  ]
                },
                "reasons": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "confidence": {
                  "type": "number",
                  "description": "Datenlage-Gewichtung der Empfehlung (0…1) aus ProfileConfidence."
                },
                "fit": {
                  "type": "number",
                  "description": "Gesamt-Rang: kuratierter Graph-Score ⊕ Achsen-Distanz ⊕ Confidence. Bestimmt die Reihenfolge innerhalb einer Ähnlichkeitsstufe; 'score' bleibt der reine Graph-Score."
                }
              }
            }
          }
        }
      },
      "UnitConvertResponse": {
        "type": "object",
        "properties": {
          "ingredientId": {
            "type": "string"
          },
          "amount": {
            "type": "number"
          },
          "fromUnit": {
            "type": "string"
          },
          "toUnit": {
            "type": "string"
          },
          "result": {
            "type": "number"
          },
          "estimated": {
            "type": "boolean"
          }
        }
      },
      "FlavorProfile": {
        "type": "object",
        "nullable": true,
        "description": "24 Geschmacks-/Aroma-Achsen (0–…), snake_case-Keys wie `dairy_like`, `savory_meaty`.",
        "additionalProperties": {
          "type": "number"
        }
      },
      "Nutrients": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "nullable": true
          },
          "macros": {
            "$ref": "#/components/schemas/Macros"
          },
          "lipids": {
            "$ref": "#/components/schemas/Lipids"
          },
          "minerals": {
            "$ref": "#/components/schemas/Minerals"
          },
          "vitamins": {
            "$ref": "#/components/schemas/Vitamins"
          }
        }
      },
      "Macros": {
        "type": "object",
        "nullable": true,
        "properties": {
          "energyKcal": {
            "type": "number",
            "nullable": true
          },
          "energyKilojoule": {
            "type": "number",
            "nullable": true
          },
          "carbohydrates": {
            "type": "number",
            "nullable": true
          },
          "protein": {
            "type": "number",
            "nullable": true
          },
          "fat": {
            "type": "number",
            "nullable": true
          },
          "fibre": {
            "type": "number",
            "nullable": true
          },
          "sugar": {
            "type": "number",
            "nullable": true
          },
          "salt": {
            "type": "number",
            "nullable": true
          },
          "water": {
            "type": "number",
            "nullable": true
          },
          "alcohol": {
            "type": "number",
            "nullable": true
          }
        }
      },
      "Lipids": {
        "type": "object",
        "nullable": true,
        "properties": {
          "cholesterol": {
            "type": "number",
            "nullable": true
          },
          "saturatedFat": {
            "type": "number",
            "nullable": true
          },
          "monounsaturatedFat": {
            "type": "number",
            "nullable": true
          },
          "polyunsaturatedFat": {
            "type": "number",
            "nullable": true
          }
        }
      },
      "Minerals": {
        "type": "object",
        "nullable": true,
        "properties": {
          "calcium": {
            "type": "number",
            "nullable": true
          },
          "iron": {
            "type": "number",
            "nullable": true
          },
          "magnesium": {
            "type": "number",
            "nullable": true
          },
          "phosphorus": {
            "type": "number",
            "nullable": true
          },
          "potassium": {
            "type": "number",
            "nullable": true
          },
          "sodium": {
            "type": "number",
            "nullable": true
          },
          "zinc": {
            "type": "number",
            "nullable": true
          }
        }
      },
      "Vitamins": {
        "type": "object",
        "nullable": true,
        "properties": {
          "a": {
            "type": "number",
            "nullable": true
          },
          "b1": {
            "type": "number",
            "nullable": true
          },
          "b2": {
            "type": "number",
            "nullable": true
          },
          "b6": {
            "type": "number",
            "nullable": true
          },
          "c": {
            "type": "number",
            "nullable": true
          },
          "k": {
            "type": "number",
            "nullable": true
          },
          "niacin": {
            "type": "number",
            "nullable": true
          },
          "pantothenicAcid": {
            "type": "number",
            "nullable": true
          }
        }
      },
      "IngredientNutritionResponse": {
        "type": "object",
        "properties": {
          "ingredientId": {
            "type": "string"
          },
          "basis": {
            "type": "string",
            "example": "per100g"
          },
          "nutrients": {
            "$ref": "#/components/schemas/Nutrients"
          },
          "computed": {
            "type": "object",
            "nullable": true,
            "properties": {
              "amount": {
                "type": "number"
              },
              "unit": {
                "type": "string"
              },
              "grams": {
                "type": "number"
              },
              "estimated": {
                "type": "boolean"
              },
              "nutrients": {
                "$ref": "#/components/schemas/Nutrients"
              }
            }
          }
        }
      },
      "NutritionAnalyzeResponse": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "ingredientId": {
                  "type": "string"
                },
                "amount": {
                  "type": "number"
                },
                "unit": {
                  "type": "string"
                },
                "grams": {
                  "type": "number"
                },
                "estimated": {
                  "type": "boolean"
                },
                "nutrients": {
                  "$ref": "#/components/schemas/Nutrients"
                }
              }
            }
          },
          "unresolved": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Unresolved"
            }
          },
          "total": {
            "$ref": "#/components/schemas/Nutrients"
          },
          "servings": {
            "type": "integer",
            "nullable": true
          },
          "perServing": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Nutrients"
              }
            ],
            "nullable": true
          }
        }
      },
      "UsageResponse": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "days": {
            "type": "integer"
          },
          "plan": {
            "type": "string"
          },
          "modules": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "usage": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "function": {
                  "type": "string"
                },
                "module": {
                  "type": "string",
                  "nullable": true
                },
                "day": {
                  "type": "string",
                  "example": "2026-07-07"
                },
                "requests": {
                  "type": "integer"
                },
                "errors": {
                  "type": "integer"
                },
                "units": {
                  "type": "integer"
                },
                "avgLatencyMs": {
                  "type": "integer"
                }
              }
            }
          }
        }
      }
    }
  }
}
