{
  "openapi": "3.1.0",
  "info": {
    "title": "Energy Price Forecast EU Integration API",
    "version": "1.0.0",
    "description": "Compact contract for Home Assistant and ioBroker integrations. Official day-ahead prices are preferred where available; forecast values extend the remaining horizon. Free access currently supports up to 48 hours and an entitled API key up to 120 hours. The effective limit is returned in meta.allowed_horizon_hours."
  },
  "servers": [
    {
      "url": "https://api.energypriceforecast.eu"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Optional Private Pro API key. Keep the real key local and send it as Authorization: Bearer YOUR_API_KEY."
      }
    },
    "parameters": {
      "country": {
        "name": "country",
        "in": "query",
        "required": true,
        "schema": { "type": "string", "example": "de" },
        "description": "Country or bidding zone, for example de, nl, at, dk1, dk2 or no1."
      },
      "hours": {
        "name": "hours",
        "in": "query",
        "schema": { "type": "integer", "minimum": 1, "maximum": 120, "default": 48 },
        "description": "Requested horizon. Access without a key is currently limited to 48 hours; an entitled key may allow up to 120 hours."
      },
      "windowHours": {
        "name": "window_hours",
        "in": "query",
        "schema": { "type": "integer", "minimum": 1, "maximum": 24, "default": 4 },
        "description": "Length of the best continuous time window."
      }
    },
    "schemas": {
      "AccessMeta": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "plan": { "type": "string" },
          "api_key_state": { "type": "string" },
          "allowed_horizon_hours": { "type": "integer" },
          "requested_horizon_hours": { "type": "integer" },
          "used_horizon_hours": { "type": "integer" },
          "rate_limit_daily": { "type": ["integer", "null"] },
          "used_calls_today": { "type": ["integer", "null"] }
        }
      },
      "Summary": {
        "type": "object",
        "additionalProperties": true,
        "required": ["api_version", "integration", "generated_at", "country", "meta", "flat", "price", "co2"],
        "properties": {
          "api_version": { "type": "string" },
          "integration": { "type": "string" },
          "generated_at": { "type": "string", "format": "date-time" },
          "country": { "type": "string" },
          "timezone": { "type": "string" },
          "meta": { "$ref": "#/components/schemas/AccessMeta" },
          "flat": { "type": "object", "additionalProperties": true },
          "price": { "type": "object", "additionalProperties": true },
          "co2": { "type": "object", "additionalProperties": true },
          "combined": { "type": "object", "additionalProperties": true },
          "source": { "type": "object", "additionalProperties": true }
        }
      },
      "PriceEntry": {
        "type": "object",
        "additionalProperties": true,
        "required": ["start", "end", "value", "unit", "source"],
        "properties": {
          "start": { "type": "string", "format": "date-time" },
          "end": { "type": "string", "format": "date-time" },
          "value": { "type": "number" },
          "unit": { "type": "string" },
          "source": { "type": "string", "description": "Identifies official day-ahead or forecast data." },
          "slot_minutes": { "type": "integer" },
          "native_resolution_minutes": { "type": "integer" },
          "expansion_method": { "type": ["string", "null"] }
        }
      },
      "PriceSeries": {
        "type": "object",
        "additionalProperties": true,
        "required": ["api_version", "generated_at", "country", "unit", "entries", "meta"],
        "properties": {
          "api_version": { "type": "string" },
          "generated_at": { "type": "string", "format": "date-time" },
          "country": { "type": "string" },
          "currency": { "type": "string" },
          "unit": { "type": "string" },
          "entries": { "type": "array", "items": { "$ref": "#/components/schemas/PriceEntry" } },
          "request": { "type": "object", "additionalProperties": true },
          "meta": { "$ref": "#/components/schemas/AccessMeta" }
        }
      },
      "Error": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "error": { "type": ["string", "object"] },
          "message": { "type": "string" }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid or unsupported request",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Unauthorized": {
        "description": "Missing, invalid or disabled API key when protected access is requested",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    }
  },
  "paths": {
    "/api/v1/home-assistant/summary": {
      "get": {
        "summary": "Home Assistant automation summary",
        "description": "Returns current price and CO2 values, useful windows, flat helper fields and access metadata.",
        "security": [{}, { "bearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/country" },
          { "$ref": "#/components/parameters/hours" },
          { "$ref": "#/components/parameters/windowHours" },
          {
            "name": "summary_hours",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 120, "default": 24 },
            "description": "Horizon used for compact summary calculations."
          }
        ],
        "responses": {
          "200": { "description": "Summary response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Summary" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/v1/iobroker/summary": {
      "get": {
        "summary": "ioBroker automation summary",
        "description": "Returns a compact structure for JavaScript-adapter states and automations.",
        "security": [{}, { "bearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/country" },
          { "$ref": "#/components/parameters/hours" },
          { "$ref": "#/components/parameters/windowHours" },
          {
            "name": "summary_hours",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 120, "default": 24 }
          }
        ],
        "responses": {
          "200": { "description": "Summary response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Summary" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/v1/iobroker/prices": {
      "get": {
        "summary": "ioBroker price series",
        "description": "Returns a price series for custom scripts. In mixed mode official day-ahead data is used first and forecast data extends the open horizon. With resolution=15m, native quarter-hour day-ahead slots remain native while hourly forecast values are repeated across four slots without invented interpolation.",
        "security": [{}, { "bearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/country" },
          { "$ref": "#/components/parameters/hours" },
          {
            "name": "mode",
            "in": "query",
            "schema": { "type": "string", "enum": ["mixed", "forecast_only"], "default": "mixed" },
            "description": "Use mixed for day-ahead-first data followed by forecast values."
          },
          {
            "name": "resolution",
            "in": "query",
            "schema": { "type": "string", "enum": ["native", "15m"], "default": "15m" }
          },
          {
            "name": "price_mode",
            "in": "query",
            "schema": { "type": "string", "enum": ["base", "retail"], "default": "base" },
            "description": "Base/market price or an assumption-based retail price. Retail is not an exact supplier tariff."
          },
          {
            "name": "plz",
            "in": "query",
            "schema": { "type": "string", "pattern": "^[0-9]{5}$", "example": "10115" },
            "description": "German postal code; required for German retail pricing."
          }
        ],
        "responses": {
          "200": { "description": "Price-series response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PriceSeries" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    }
  }
}
