{
  "openapi": "3.0.3",
  "info": {
    "title": "Lenzerheide Partner API",
    "version": "1.3.0",
    "description": "API-Endpunkte fuer Partner:innen der world of wonders Lenzerheide. Diese Endpunkte benoetigen einen API-Key und freigegebene Scopes."
  },
  "servers": [
    {
      "url": "https://api.lenzerheide.swiss",
      "description": "Produktive API"
    }
  ],
  "tags": [
    {
      "name": "Status",
      "description": "Technischer API-Status."
    },
    {
      "name": "Traffic",
      "description": "Verkehrsdaten fuer Partner:innen mit Scope traffic:read."
    },
    {
      "name": "Weather",
      "description": "Wetter-Tageswerte fuer Partner:innen mit Scope weather:read. Source of Truth ist MeteoSwiss OGD Local Forecast fuer Punkt 707800 Lenzerheide/Lai."
    }
  ],
  "paths": {
    "/v1/status.php": {
      "get": {
        "tags": ["Status"],
        "summary": "API Status",
        "description": "Liefert den technischen Status der API. Dieser Endpunkt benoetigt keinen API-Key.",
        "operationId": "getApiStatus",
        "responses": {
          "200": {
            "description": "API ist erreichbar.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/StatusResponse" },
                "example": {
                  "meta": {
                    "apiVersion": "v1",
                    "generatedAt": "2026-08-18T16:30:00+02:00",
                    "source": "lenzerheide_data_layer",
                    "visibility": "public",
                    "module": "status"
                  },
                  "data": { "status": "ok" }
                }
              }
            }
          },
          "500": { "$ref": "#/components/responses/ServerError" },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" }
        }
      }
    },
    "/v1/partner/traffic/summary.php": {
      "get": {
        "tags": ["Traffic"],
        "summary": "Partner Traffic Summary",
        "description": "Liefert eine freigegebene Verkehrszusammenfassung fuer Partner:innen. Benoetigt API-Key mit Scope traffic:read.",
        "operationId": "getPartnerTrafficSummary",
        "security": [
          { "ApiKeyAuth": [] },
          { "BearerAuth": [] }
        ],
        "parameters": [
          { "$ref": "#/components/parameters/PartnerTrafficMaxAgeMinutesParam" }
        ],
        "responses": {
          "200": {
            "description": "Verkehrszusammenfassung erfolgreich geladen.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/TrafficPartnerResponse" },
                "example": {
                  "meta": {
                    "apiVersion": "v1",
                    "generatedAt": "2026-08-18T16:30:00+02:00",
                    "source": "lenzerheide_data_layer",
                    "visibility": "partner",
                    "module": "traffic"
                  },
                  "data": {
                    "status": "busy",
                    "level": "high",
                    "summary": "Erhoehte Verkehrsbelastung rund um Lenzerheide. Fuer Partner und Leistungstraeger sind spuerbare Verzoegerungen zu beruecksichtigen.",
                    "metrics": { "routeCount": 1, "maxDelayMinutes": 8, "avgDelayMinutes": 8 },
                    "routes": [ { "routeKey": "chur_lenzerheide", "routeName": "Chur - Lenzerheide", "delayMinutes": 8, "level": "high", "status": "busy" } ]
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/ServerError" },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" }
        }
      }
    },
    "/v1/partner/weather/summary.php": {
      "get": {
        "tags": ["Weather"],
        "summary": "Partner Weather Daily Summary",
        "description": "Liefert den freigegebenen kanonischen Wetter-Tageswert fuer Partner:innen. Seit 18. August 2026 ist MeteoSwiss OGD Local Forecast fuer Punkt 707800 Lenzerheide/Lai die Weather Source of Truth. Produktive Tageswerte sind Forecast-Werte und keine Echtzeit-Stationsmessungen. Nicht vom Daily Forecast gelieferte Legacy-Felder bleiben null. observedAt bleibt als Legacy-Alias fuer capturedAt erhalten. Benoetigt API-Key mit Scope weather:read.",
        "operationId": "getPartnerWeatherSummary",
        "security": [
          { "ApiKeyAuth": [] },
          { "BearerAuth": [] }
        ],
        "parameters": [
          { "$ref": "#/components/parameters/DateParam" },
          { "$ref": "#/components/parameters/LocationKeyParam" }
        ],
        "responses": {
          "200": {
            "description": "Wetter-Tageswert erfolgreich geladen.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/WeatherPartnerResponse" },
                "example": {
                  "meta": {
                    "apiVersion": "v1",
                    "generatedAt": "2026-08-18T16:30:00+02:00",
                    "source": "lenzerheide_data_layer",
                    "visibility": "partner",
                    "module": "weather"
                  },
                  "data": {
                    "module": "weather",
                    "date": "2026-08-18",
                    "locationKey": "lenzerheide",
                    "locationName": "Lenzerheide",
                    "hasWeatherData": true,
                    "dataType": "forecast",
                    "isForecast": true,
                    "source": "meteoswiss",
                    "sourceProduct": "meteoswiss-local-forecast",
                    "status": "good",
                    "level": "good",
                    "score": 65,
                    "summary": "Gutes Outdoor-Wetter. 0 mm Niederschlag, Tal 11 bis 21 Grad.",
                    "temperature": {
                      "valleyMin": 11,
                      "valleyMax": 21,
                      "mountainMin": null,
                      "mountainMax": null
                    },
                    "sunshineHours": null,
                    "precipitationMm": 0,
                    "windGustsKmh": null,
                    "weatherIcon": "3",
                    "sourceCreatedAt": "202608180600",
                    "capturedAt": "2026-08-18T08:21:03+02:00",
                    "observedAt": "2026-08-18T08:21:03+02:00",
                    "updatedAt": "2026-08-18T08:21:03+02:00",
                    "partner": {},
                    "partnerUse": {
                      "suitableFor": ["guest_information", "operational_planning", "partner_cockpit", "content_context"],
                      "notSuitableFor": ["individual_tracking", "enforcement", "billing"]
                    },
                    "details": {
                      "region": "lenzerheide",
                      "isForecast": true,
                      "sourceCreatedAt": "202608180600",
                      "windStrength": null,
                      "windDirection": null,
                      "feelsLike": null,
                      "freezingLevel": null,
                      "snowMountainCm": null,
                      "snowValleyCm": null,
                      "newSnowCm": null,
                      "snowCondition": null
                    }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/ServerError" },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Partner API-Key. Empfohlene Authentifizierung fuer Partner-Endpunkte."
      },
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Alternative Authentifizierung mit Bearer Token."
      }
    },
    "parameters": {
      "PartnerTrafficMaxAgeMinutesParam": {
        "name": "max_age_minutes",
        "in": "query",
        "required": false,
        "description": "Maximales Alter der Verkehrsbeobachtungen in Minuten. Standard 120; Eingaben werden serverseitig auf 1 bis 360 begrenzt.",
        "schema": { "type": "integer", "minimum": 1, "maximum": 360, "default": 120 }
      },
      "DateParam": {
        "name": "date",
        "in": "query",
        "required": false,
        "description": "Datum im Format YYYY-MM-DD. Standard ist das aktuelle lokale Datum. Im produktiven MeteoSwiss-Scope wird der kanonische Forecast-Tageswert fuer dieses Datum geliefert.",
        "schema": { "type": "string", "format": "date", "example": "2026-08-18" }
      },
      "LocationKeyParam": {
        "name": "location_key",
        "in": "query",
        "required": false,
        "description": "Produktiv freigegebener Standortschluessel. Aktuell ist ausschliesslich lenzerheide dokumentiert und produktiv verifiziert.",
        "schema": {
          "type": "string",
          "enum": ["lenzerheide"],
          "example": "lenzerheide"
        }
      }
    },
    "responses": {
      "BadRequest": { "description": "Ungueltige Anfrage oder ungueltige Parameter.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
      "Unauthorized": { "description": "API-Key fehlt oder ist ungueltig.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
      "Forbidden": { "description": "API-Key hat keinen passenden Scope fuer diesen Endpunkt.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
      "TooManyRequests": { "description": "Rate Limit erreicht.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
      "ServerError": { "description": "Unerwarteter Serverfehler.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
      "ServiceUnavailable": { "description": "Service oder Datenquelle temporaer nicht verfuegbar.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }
    },
    "schemas": {
      "Meta": { "type": "object", "additionalProperties": true },
      "StatusResponse": { "type": "object", "required": ["meta", "data"], "properties": { "meta": { "$ref": "#/components/schemas/Meta" }, "data": { "type": "object", "additionalProperties": true } } },
      "TrafficPartnerResponse": { "type": "object", "required": ["meta", "data"], "properties": { "meta": { "$ref": "#/components/schemas/Meta" }, "data": { "type": "object", "additionalProperties": true } } },
      "WeatherTemperature": {
        "type": "object",
        "properties": {
          "valleyMin": { "type": "integer", "nullable": true },
          "valleyMax": { "type": "integer", "nullable": true },
          "mountainMin": { "type": "integer", "nullable": true, "description": "Im aktuellen MeteoSwiss Daily Forecast nicht geliefert und deshalb null." },
          "mountainMax": { "type": "integer", "nullable": true, "description": "Im aktuellen MeteoSwiss Daily Forecast nicht geliefert und deshalb null." }
        }
      },
      "WeatherCurrentData": {
        "type": "object",
        "required": ["module", "date", "locationKey", "hasWeatherData", "dataType", "isForecast", "source", "status", "level", "score", "summary", "temperature", "sunshineHours", "precipitationMm", "windGustsKmh", "weatherIcon", "sourceCreatedAt", "capturedAt", "observedAt", "updatedAt"],
        "properties": {
          "module": { "type": "string", "enum": ["weather"] },
          "date": { "type": "string", "format": "date" },
          "locationKey": { "type": "string", "enum": ["lenzerheide"] },
          "locationName": { "type": "string", "nullable": true },
          "hasWeatherData": { "type": "boolean" },
          "dataType": { "type": "string", "enum": ["forecast", "observation", "unknown"] },
          "isForecast": { "type": "boolean", "nullable": true },
          "source": { "type": "string", "enum": ["meteoswiss", "legacy_weather_csv", "unknown"] },
          "sourceProduct": { "type": "string", "nullable": true },
          "status": { "type": "string", "nullable": true, "enum": ["good", "changeable", "limited"] },
          "level": { "type": "string", "nullable": true, "enum": ["excellent", "good", "medium", "poor"] },
          "score": { "type": "integer", "nullable": true, "minimum": 0, "maximum": 100, "description": "Deterministische W.O.W.-Wetterbewertung aus den im Tagesprodukt verfuegbaren Eingabefeldern." },
          "summary": { "type": "string" },
          "temperature": { "$ref": "#/components/schemas/WeatherTemperature" },
          "sunshineHours": { "type": "number", "format": "float", "nullable": true, "description": "Im aktuellen MeteoSwiss Daily Forecast nicht geliefert und deshalb null." },
          "precipitationMm": { "type": "number", "format": "float", "nullable": true },
          "windGustsKmh": { "type": "integer", "nullable": true, "description": "Im aktuellen MeteoSwiss Daily Forecast nicht geliefert und deshalb null." },
          "weatherIcon": { "type": "string", "nullable": true },
          "sourceCreatedAt": { "type": "string", "nullable": true, "pattern": "^[0-9]{12}$" },
          "capturedAt": { "type": "string", "format": "date-time", "nullable": true },
          "observedAt": { "type": "string", "format": "date-time", "nullable": true, "deprecated": true, "description": "Legacy-Alias fuer capturedAt. Keine Stationsmesszeit." },
          "updatedAt": { "type": "string", "format": "date-time", "nullable": true }
        }
      },
      "WeatherPartnerData": {
        "allOf": [
          { "$ref": "#/components/schemas/WeatherCurrentData" },
          {
            "type": "object",
            "required": ["partner", "partnerUse", "details"],
            "properties": {
              "partner": { "type": "object", "additionalProperties": true },
              "partnerUse": {
                "type": "object",
                "required": ["suitableFor", "notSuitableFor"],
                "properties": {
                  "suitableFor": { "type": "array", "items": { "type": "string" } },
                  "notSuitableFor": { "type": "array", "items": { "type": "string" } }
                }
              },
              "details": {
                "type": "object",
                "required": ["region", "isForecast", "sourceCreatedAt", "windStrength", "windDirection", "feelsLike", "freezingLevel", "snowMountainCm", "snowValleyCm", "newSnowCm", "snowCondition"],
                "properties": {
                  "region": { "type": "string", "nullable": true },
                  "isForecast": { "type": "boolean", "nullable": true },
                  "sourceCreatedAt": { "type": "string", "nullable": true, "pattern": "^[0-9]{12}$" },
                  "windStrength": { "type": "number", "format": "float", "nullable": true },
                  "windDirection": { "type": "string", "nullable": true },
                  "feelsLike": { "type": "number", "format": "float", "nullable": true },
                  "freezingLevel": { "type": "integer", "nullable": true },
                  "snowMountainCm": { "type": "integer", "nullable": true },
                  "snowValleyCm": { "type": "integer", "nullable": true },
                  "newSnowCm": { "type": "integer", "nullable": true },
                  "snowCondition": { "type": "string", "nullable": true }
                }
              }
            }
          }
        ]
      },
      "WeatherPartnerResponse": {
        "type": "object",
        "required": ["meta", "data"],
        "properties": {
          "meta": { "$ref": "#/components/schemas/Meta" },
          "data": { "$ref": "#/components/schemas/WeatherPartnerData" }
        }
      },
      "ErrorResponse": { "type": "object", "required": ["error"], "properties": { "error": { "type": "object", "required": ["code", "message", "details"], "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": { "type": "object", "additionalProperties": true } } } } }
    }
  }
}
