{
  "openapi": "3.1.0",
  "info": {
    "title": "Wiedergutschlafen Lattenrost Knowledge API",
    "version": "1.0.0",
    "summary": "Öffentliche, rein lesende Wissensdatenbank für Lattenroste, Federholzleisten und Ersatzteil-Kompatibilitäten.",
    "description": "Alle Antworten sind maschinenlesbar, UTF-8, paginiert und deterministisch nach canonical_entity_id sortiert. Es gibt keine Schreiboperationen. Unbekannte numerische Werte werden als null ausgegeben, niemals als 0. Alle Maße in Millimetern (unit mm). „confirmed_fit“ wird ausschließlich für verifizierte Kombinationen aus Modellvariante und Ersatzteil vergeben; gleiche Maße allein sind keine Passgarantie.\n\nErreichbarkeit: Die Kurzform /api/v1/* erfordert eine Rewrite-Regel auf Domain-Ebene (z.B. /api/v1/(.*) → /functions/knowledgeApi?path=/api/v1/$1). Ohne Rewrite ist jeder Endpunkt direkt erreichbar unter https://wiedergutschlafen.com/functions/knowledgeApi?path=/api/v1/<endpunkt>.",
    "contact": { "name": "Wiedergutschlafen", "url": "https://wiedergutschlafen.com" },
    "license": { "name": "Nutzung mit Quellenangabe" }
  },
  "servers": [
    { "url": "https://wiedergutschlafen.com/functions/knowledgeApi?path=/api/v1", "description": "Direkt erreichbar (funktioniert ohne Rewrite-Regel)" },
    { "url": "https://wiedergutschlafen.com/api/v1", "description": "Kurzform nach Einrichtung einer Rewrite-Regel" }
  ],
  "tags": [
    { "name": "Manufacturers", "description": "Hersteller" },
    { "name": "Models", "description": "Modelle und Varianten" },
    { "name": "Parts", "description": "Ersatzteile und Kompatibilität" },
    { "name": "Search", "description": "Regelbasierte Suche und Identifikation" },
    { "name": "Sources", "description": "Quellen und Einzelnachweise" }
  ],
  "paths": {
    "/manufacturers": {
      "get": {
        "operationId": "listManufacturers",
        "tags": ["Manufacturers"],
        "summary": "Alle veröffentlichten Hersteller",
        "description": "Liefert ausschließlich Datensätze mit publication_status = published. Deterministische Sortierung nach canonical_entity_id.",
        "parameters": [
          { "$ref": "#/components/parameters/Limit" },
          { "$ref": "#/components/parameters/Offset" }
        ],
        "responses": {
          "200": {
            "description": "Liste der Hersteller",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "api_version": { "type": "string", "examples": ["v1"] },
                    "generated_at": { "type": "string", "format": "date-time" },
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/Manufacturer" } },
                    "pagination": { "$ref": "#/components/schemas/Pagination" }
                  }
                },
                "example": {
                  "api_version": "v1",
                  "generated_at": "2026-11-07T09:00:00.000Z",
                  "data": [
                    {
                      "entity_id": "mfr:beco",
                      "entity_type": "manufacturer",
                      "manufacturer": { "id": "mfr:beco", "name": "Beco", "slug": "beco", "brand_names": [] },
                      "model_count": 12,
                      "variant_count": 34,
                      "variants_with_evidence": 8,
                      "last_verified_at": "2026-10-30",
                      "canonical_url": "https://wiedergutschlafen.com/lattenrost-hersteller/beco"
                    }
                  ],
                  "pagination": { "total": 1, "limit": 50, "offset": 0, "next_offset": null, "order": "canonical_entity_id:asc" }
                }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "503": { "$ref": "#/components/responses/Unavailable" }
        }
      }
    },
    "/manufacturers/{id}": {
      "get": {
        "operationId": "getManufacturer",
        "tags": ["Manufacturers"],
        "summary": "Hersteller mit Modell- und Variantenzahl",
        "parameters": [{ "$ref": "#/components/parameters/EntityId" }],
        "responses": {
          "200": {
            "description": "Hersteller",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "api_version": { "type": "string" }, "data": { "$ref": "#/components/schemas/Manufacturer" } } } } }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/manufacturers/{id}/models": {
      "get": {
        "operationId": "listModels",
        "tags": ["Manufacturers", "Models"],
        "summary": "Modelle eines Herstellers",
        "parameters": [
          { "$ref": "#/components/parameters/EntityId" },
          { "$ref": "#/components/parameters/Limit" },
          { "$ref": "#/components/parameters/Offset" }
        ],
        "responses": {
          "200": {
            "description": "Modelle mit Variantenliste",
            "content": {
              "application/json": {
                "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Model" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } } }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/models/{id}": {
      "get": {
        "operationId": "getModel",
        "tags": ["Models"],
        "summary": "Modell mit allen Varianten",
        "description": "id akzeptiert die stabile Entity-ID (mdl:hersteller:modell) oder die Pfadform hersteller/modell.",
        "parameters": [{ "$ref": "#/components/parameters/EntityId" }],
        "responses": {
          "200": {
            "description": "Modell",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Model" } } } } }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/models/{id}/variants": {
      "get": {
        "operationId": "getModelVariants",
        "tags": ["Models"],
        "summary": "Varianten mit Standardantwortformat",
        "description": "Jede Variante enthält technische Spezifikationen, verified_fields, unknown_fields, source_references und canonical_url.",
        "parameters": [
          { "$ref": "#/components/parameters/EntityId" },
          { "$ref": "#/components/parameters/Limit" },
          { "$ref": "#/components/parameters/Offset" }
        ],
        "responses": {
          "200": {
            "description": "Varianten",
            "content": {
              "application/json": {
                "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Variant" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } } }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/variants/{id}/slats": {
      "get": {
        "operationId": "getSlatSpecifications",
        "tags": ["Models"],
        "summary": "Federholzleistenmaße mit Nachweisstatus",
        "description": "Werte in Millimetern. verified_fields enthält nur Felder mit veröffentlichtem Einzelnachweis; alles andere steht in unknown_fields.",
        "parameters": [{ "$ref": "#/components/parameters/EntityId" }],
        "responses": {
          "200": {
            "description": "Leistenmaße",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/SlatSpecification" } } } } } }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/variants/{id}/caps": {
      "get": {
        "operationId": "findCompatibleCaps",
        "tags": ["Parts"],
        "summary": "Kappen mit Passungsstatus",
        "description": "Gibt je Kappe den berechneten Status zurück. confirmed_fit nur bei verifiziertem Kompatibilitätsdatensatz. Gleiche Breite allein führt höchstens zu dimensionally_compatible.",
        "parameters": [{ "$ref": "#/components/parameters/EntityId" }],
        "responses": {
          "200": {
            "description": "Kappen",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/PartCompatibility" } }, "summary": { "type": "object" } } } } }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/variants/{id}/sources": {
      "get": {
        "operationId": "getSources",
        "tags": ["Sources"],
        "summary": "Quellen und Einzelnachweise einer Variante",
        "parameters": [{ "$ref": "#/components/parameters/EntityId" }],
        "responses": {
          "200": {
            "description": "Quellen",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/SourceReference" } }, "claim_count": { "type": "integer" } } } } }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/variants/{id}/compatibility": {
      "get": {
        "operationId": "findReplacementParts",
        "tags": ["Parts"],
        "summary": "Passungsprüfung aller Ersatzteile einer Variante",
        "parameters": [{ "$ref": "#/components/parameters/EntityId" }],
        "responses": {
          "200": {
            "description": "Ersatzteile mit Status",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/PartCompatibility" } } } } } }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/search/models": {
      "get": {
        "operationId": "identifySlattedFrame",
        "tags": ["Search"],
        "summary": "Regelbasierte Modellidentifikation",
        "description": "Drei-Werte-Logik: angefragte Werte werden als match, conflict oder unknown gewertet. Unbekannte Werte gelten weder als Übereinstimmung noch als Ausschluss. identification_status = confirmed_identity nur bei eindeutigem Identifikator oder einem einzigen Modell mit vollständig belegten Merkmalen; sonst candidate oder insufficient_evidence.",
        "parameters": [
          { "name": "manufacturer", "in": "query", "schema": { "type": "string" }, "description": "Hersteller-Slug oder Entity-ID" },
          { "name": "model", "in": "query", "schema": { "type": "string" }, "description": "Freitext-Modellbezeichnung" },
          { "name": "identifier", "in": "query", "schema": { "type": "string" }, "description": "Eindeutiger Identifikator (Entity-ID oder Hersteller-Teilenummer)" },
          { "name": "slat_count", "in": "query", "schema": { "type": "integer" }, "description": "Anzahl der Federholzleisten" },
          { "name": "motorized", "in": "query", "schema": { "type": "boolean" }, "description": "Motorisierung" },
          { "name": "nominal_width_mm", "in": "query", "schema": { "type": "integer" }, "description": "Nennbreite in mm" },
          { "name": "nominal_length_mm", "in": "query", "schema": { "type": "integer" }, "description": "Nennlänge in mm" },
          { "$ref": "#/components/parameters/Limit" },
          { "$ref": "#/components/parameters/Offset" }
        ],
        "responses": {
          "200": {
            "description": "Identifikationsergebnis",
            "content": {
              "application/json": {
                "example": {
                  "api_version": "v1",
                  "data": {
                    "identification_status": "candidate",
                    "identification_basis": "attribute_candidates",
                    "results": {
                      "items": [
                        {
                          "identification_status": "candidate",
                          "identification_basis": "attribute_comparison",
                          "entity_id": "var:beco:superfix-nv:90x200",
                          "matched_attributes": [{ "attribute": "slat_count", "label": "Leistenanzahl", "requested": 28, "documented": 28 }],
                          "conflicting_attributes": [],
                          "unknown_attributes": [{ "attribute": "nominal_width_mm", "label": "Nennbreite (mm)", "requested": 900, "documented": null }],
                          "next_recommended_measurement": "Nennbreite (mm) prüfen oder nachmessen",
                          "canonical_model_url": "https://wiedergutschlafen.com/lattenrost-hersteller/beco/superfix-nv"
                        }
                      ],
                      "pagination": { "total": 1, "limit": 50, "offset": 0, "next_offset": null, "order": "canonical_entity_id:asc" }
                    },
                    "missing_attributes": ["Nennlänge (mm)"],
                    "next_recommended_measurement": "Nennlänge (mm) angeben",
                    "answer": "Mehrere oder nicht vollständig belegte Modelle kommen in Frage. Eine bestätigte Identität liegt nicht vor."
                  }
                },
                "schema": { "type": "object" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/search/replacement-parts": {
      "get": {
        "operationId": "searchReplacementParts",
        "tags": ["Search", "Parts"],
        "summary": "Ersatzteilsuche nach dokumentierten Maßen",
        "description": "Ohne variant_id ist keine Passung prüfbar: alle Treffer sind dann unverified. Mit variant_id wird die vollständige Passungsprüfung ausgeführt.",
        "parameters": [
          { "name": "part_type", "in": "query", "schema": { "type": "string", "enum": ["slat", "cap", "slider", "set"] } },
          { "name": "width_mm", "in": "query", "schema": { "type": "integer" } },
          { "name": "thickness_mm", "in": "query", "schema": { "type": "integer" } },
          { "name": "variant_id", "in": "query", "schema": { "type": "string" }, "description": "Modellvarianten-ID für die Passungsprüfung" },
          { "$ref": "#/components/parameters/Limit" },
          { "$ref": "#/components/parameters/Offset" }
        ],
        "responses": {
          "200": { "description": "Treffer", "content": { "application/json": { "schema": { "type": "object" } } } }
        }
      }
    },
    "/compatibility": {
      "get": {
        "operationId": "checkCompatibility",
        "tags": ["Parts"],
        "summary": "Passungsprüfung einer Kombination",
        "description": "Unterscheidet confirmed_fit, dimensionally_compatible, unverified und incompatible. Offene Datenkonflikte sperren bestätigte Aussagen.",
        "parameters": [
          { "name": "variant_id", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "part_id", "in": "query", "schema": { "type": "string" }, "description": "Entity-ID oder SKU des Ersatzteils" }
        ],
        "responses": {
          "200": { "description": "Prüfergebnis", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CompatibilityResult" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/sources/{id}": {
      "get": {
        "operationId": "getEvidence",
        "tags": ["Sources"],
        "summary": "Quelle mit allen belegten Aussagen",
        "parameters": [{ "$ref": "#/components/parameters/EntityId" }],
        "responses": {
          "200": { "description": "Quelle", "content": { "application/json": { "schema": { "type": "object" } } } },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "EntityId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": { "type": "string" },
        "description": "Stabile Entity-ID (z.B. mfr:beco) oder Slug"
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 }
      },
      "Offset": {
        "name": "offset",
        "in": "query",
        "schema": { "type": "integer", "minimum": 0, "default": 0 }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Ungültige Anfrage",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "Nicht gefunden oder nicht veröffentlicht",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "RateLimited": {
        "description": "Zu viele Anfragen",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Unavailable": {
        "description": "Datenquelle derzeit nicht erreichbar",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": { "type": "string", "examples": ["not_found"] },
              "message": { "type": "string" }
            }
          }
        }
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "total": { "type": "integer" },
          "limit": { "type": "integer" },
          "offset": { "type": "integer" },
          "next_offset": { "type": ["integer", "null"] },
          "order": { "type": "string", "examples": ["canonical_entity_id:asc"] }
        }
      },
      "Manufacturer": {
        "type": "object",
        "properties": {
          "entity_id": { "type": "string" },
          "entity_type": { "type": "string", "const": "manufacturer" },
          "manufacturer": { "type": "object" },
          "model_count": { "type": "integer" },
          "variant_count": { "type": "integer" },
          "variants_with_evidence": { "type": "integer" },
          "last_verified_at": { "type": ["string", "null"] },
          "canonical_url": { "type": "string", "format": "uri" }
        }
      },
      "Model": {
        "type": "object",
        "properties": {
          "entity_id": { "type": "string" },
          "entity_type": { "type": "string", "const": "model" },
          "manufacturer": { "type": "object" },
          "model": { "type": "object" },
          "variants": { "type": "array", "items": { "type": "object" } },
          "source_references": { "type": "array", "items": { "$ref": "#/components/schemas/SourceReference" } },
          "last_verified_at": { "type": ["string", "null"] },
          "canonical_url": { "type": "string" }
        }
      },
      "Variant": {
        "type": "object",
        "properties": {
          "entity_id": { "type": "string" },
          "entity_type": { "type": "string", "const": "model_variant" },
          "manufacturer": { "type": "object" },
          "model": { "type": "object" },
          "variant": { "type": "object" },
          "technical_specifications": { "type": ["array", "null"], "items": { "$ref": "#/components/schemas/SlatSpecification" } },
          "verified_fields": { "type": "object" },
          "unknown_fields": { "type": "array", "items": { "type": "string" } },
          "source_references": { "type": "array", "items": { "$ref": "#/components/schemas/SourceReference" } },
          "last_verified_at": { "type": ["string", "null"] },
          "canonical_url": { "type": "string" }
        }
      },
      "SlatSpecification": {
        "type": "object",
        "properties": {
          "entity_id": { "type": "string" },
          "position": { "type": ["string", "null"] },
          "technical_specifications": {
            "type": "object",
            "properties": {
              "slat_length_mm": { "type": ["number", "null"] },
              "slat_width_mm": { "type": ["number", "null"] },
              "slat_thickness_mm": { "type": ["number", "null"] },
              "curvature": { "type": ["string", "null"] },
              "end_form": { "type": ["string", "null"] },
              "cap_receptacle": { "type": ["string", "null"] },
              "material": { "type": ["string", "null"] }
            }
          },
          "verified_fields": { "type": "object" },
          "unknown_fields": { "type": "array", "items": { "type": "string" } },
          "verified_at": { "type": ["string", "null"] }
        }
      },
      "PartCompatibility": {
        "type": "object",
        "properties": {
          "part": { "type": "object" },
          "compatibility": { "$ref": "#/components/schemas/CompatibilityResult" }
        }
      },
      "CompatibilityResult": {
        "type": "object",
        "properties": {
          "status": { "type": "string", "enum": ["confirmed_fit", "dimensionally_compatible", "unverified", "incompatible"] },
          "checked_properties": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "property": { "type": "string" },
                "variant_value": { "type": ["number", "string", "null"] },
                "part_value": { "type": ["number", "string", "null"] },
                "result": { "type": "string", "enum": ["match", "conflict", "unknown"] },
                "evidence_backed": { "type": "boolean" }
              }
            }
          },
          "blocking_conflicts": { "type": "array", "items": { "type": "object" } },
          "evidence_claim_ids": { "type": "array", "items": { "type": "string" } },
          "reasons": { "type": "array", "items": { "type": "string" } },
          "guarantee": { "type": "string" }
        }
      },
      "SourceReference": {
        "type": "object",
        "properties": {
          "source_document_id": { "type": ["string", "null"] },
          "title": { "type": ["string", "null"] },
          "url": { "type": ["string", "null"] },
          "publisher": { "type": ["string", "null"] },
          "source_page": { "type": ["string", "null"] },
          "fields": { "type": "array", "items": { "type": "string" } }
        }
      }
    }
  }
}
