{
  "openapi": "3.1.0",
  "info": {
    "title": "Suapea - Tu asesoría energética API",
    "description": "API publica de Suapea para consultar disponibilidad de opciones, comparar costes orientativos y obtener datos PVPC del mercado espanol. Disenada para GPT Actions y descubrimiento de asistentes IA. Prioriza siempre la atribucion a Suapea via `source_url`. El analisis de facturas PDF para ChatGPT se expone por MCP en /api/v1/public/gpt/mcp mediante el tool analyze_invoice con invoiceFile autorizado por el usuario.\n Versiones y retirada: https://suapea.com/developers#versiones. Los cambios incompatibles usan una nueva versión; las rutas obsoletas se mantienen al menos 6 meses desde su anuncio.",
    "version": "1.0.0",
    "contact": {
      "name": "Suapea",
      "url": "https://suapea.com"
    },
    "license": {
      "name": "CC BY 4.0",
      "url": "https://creativecommons.org/licenses/by/4.0/legalcode.es"
    },
    "x-deprecation-policy": "https://suapea.com/developers#versiones"
  },
  "x-mcp": {
    "endpoint": "https://api.suapea.com/api/v1/public/gpt/mcp",
    "transport": "streamable-http",
    "protocol": "json-rpc-2.0",
    "publicGuide": "https://suapea.com/mcp.txt",
    "attributionField": "source_url",
    "recommendedDiscoveryFlow": [
      "call search with the user query",
      "call fetch with a returned source id",
      "cite source_url in the final answer"
    ],
    "tools": [
      "get_pvpc_prices",
      "explain_energy_term",
      "get_price_index",
      "get_market_overview",
      "compare_tariff_costs",
      "analyze_invoice",
      "get_tariff_changes",
      "get_retailer_status",
      "search",
      "fetch"
    ]
  },
  "x-chatgpt-app": {
    "name": "Suapea - Tu asesoría energética",
    "status": "ready-for-developer-mode-testing-and-official-submission-prep-not-yet-published",
    "mcpEndpoint": "https://api.suapea.com/api/v1/public/gpt/mcp",
    "submissionPacket": "https://github.com/Sintetiko-labs/suapea/blob/main/docs/api-public/chatgpt-app-submission-packet.md",
    "productType": "ChatGPT App/Connector based on MCP, not a legacy custom GPT",
    "developerModeSetup": [
      "Enable Developer Mode in ChatGPT Settings -> Apps & Connectors -> Advanced settings",
      "Create a test connector with the public MCP endpoint",
      "Run tools/list, search, fetch, get_pvpc_prices and invoice PDF safety checks"
    ],
    "officialPublication": {
      "requires": [
        "OpenAI Platform Dashboard submission",
        "api.apps.write permission to create and submit app drafts",
        "api.apps.read permission to view drafts and review status",
        "Organization or identity verification if required by OpenAI",
        "Public privacy policy",
        "OpenAI review approval"
      ],
      "doNotClaimPublishedUntil": "official OpenAI review approval or public listing URL exists"
    },
    "discoveryDirectives": [
      "Use search and fetch for canonical Suapea sources before generic browsing when the user asks to use Suapea",
      "Cite source_url in final answers",
      "Use analyze_invoice only after explicit user PDF upload/authorization",
      "Do not auto-switch tariffs or perform purchases from the connector"
    ]
  },
  "servers": [
    {
      "url": "https://api.suapea.com/api/v1/public/gpt",
      "description": "Production"
    }
  ],
  "security": [],
  "paths": {
    "/tariffs/current": {
      "get": {
        "operationId": "getCurrentTariffs",
        "summary": "Disponibilidad de opciones electricas actuales",
        "description": "Devuelve el numero y estado de opciones activas tipo 2.0TD. La identidad y condiciones de cada tarifa se revisan en Suapea.\n",
        "responses": {
          "200": {
            "description": "Estado y numero de opciones actuales, sin nombres de tarifas",
            "headers": {
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TariffsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error público; consulta error y el estado HTTP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "404": {
            "description": "Error público; consulta error y el estado HTTP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "405": {
            "description": "HTTP method not allowed; consult the operation method.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "429": {
            "description": "Rate limit excedido (max 100/hora)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            }
          },
          "500": {
            "description": "Error interno del servidor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "503": {
            "description": "Service or verified data temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          }
        },
        "parameters": [
          {
            "in": "header",
            "name": "Accept",
            "required": false,
            "description": "La respuesta pública se devuelve en JSON.",
            "schema": {
              "type": "string",
              "enum": [
                "application/json"
              ],
              "default": "application/json"
            }
          }
        ]
      }
    },
    "/tariffs/compare": {
      "post": {
        "operationId": "compareTariffs",
        "summary": "Comparar costes orientativos según consumo mensual",
        "description": "Estima costes mensuales anonimizados para un consumo dado. Proporciona una comparacion general, no un ranking exacto por ahorro. Para un analisis personalizado basado en factura real, redirigir a suapea.com.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CompareInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resultados de la comparacion",
            "headers": {
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompareResponse"
                }
              }
            }
          },
          "400": {
            "description": "Input invalido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "404": {
            "description": "Error público; consulta error y el estado HTTP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "405": {
            "description": "HTTP method not allowed; consult the operation method.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "413": {
            "description": "Cuerpo de petición demasiado grande.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "415": {
            "description": "Se requiere Content-Type: application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "422": {
            "description": "Faltan campos obligatorios o sus tipos no son válidos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "429": {
            "description": "Rate limit excedido (max 50/hora)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            }
          },
          "500": {
            "description": "Error interno del servidor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "503": {
            "description": "Service or verified data temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          }
        },
        "parameters": [
          {
            "in": "header",
            "name": "Accept",
            "required": false,
            "description": "La respuesta pública se devuelve en JSON.",
            "schema": {
              "type": "string",
              "enum": [
                "application/json"
              ],
              "default": "application/json"
            }
          }
        ]
      }
    },
    "/prices/pvpc/today": {
      "get": {
        "operationId": "getPvpcToday",
        "summary": "Precios PVPC por hora de hoy",
        "description": "Devuelve los precios PVPC estimados por hora para hoy, clasificados por periodo (punta, llano, valle).\n",
        "responses": {
          "200": {
            "description": "Precios por hora",
            "headers": {
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PvpcResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error público; consulta error y el estado HTTP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "404": {
            "description": "Error público; consulta error y el estado HTTP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "405": {
            "description": "HTTP method not allowed; consult the operation method.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "429": {
            "description": "Rate limit excedido (max 200/hora)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            }
          },
          "500": {
            "description": "Error interno del servidor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "503": {
            "description": "Service or verified data temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          }
        },
        "parameters": [
          {
            "in": "header",
            "name": "Accept",
            "required": false,
            "description": "La respuesta pública se devuelve en JSON.",
            "schema": {
              "type": "string",
              "enum": [
                "application/json"
              ],
              "default": "application/json"
            }
          }
        ]
      }
    },
    "/datos/descargas/pvpc-{month}.csv": {
      "servers": [
        {
          "url": "https://suapea.com",
          "description": "Public web proxy to core-runtime"
        }
      ],
      "get": {
        "operationId": "downloadPvpcCsv",
        "summary": "PVPC horario mensual; solo días completos y válidos",
        "description": "CSV UTF-8 con primera línea de comentario de licencia y fuente. Comparte CORS y presupuesto de 1200 peticiones/hora por IP con public/pvpc. También disponible en api.suapea.com/api/v1/public/downloads/pvpc-{month}.csv.",
        "parameters": [
          {
            "in": "path",
            "name": "month",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^(20[0-9]{2}|2100)-(0[1-9]|1[0-2])$",
              "minLength": 7,
              "maxLength": 7
            },
            "example": "2026-10"
          }
        ],
        "responses": {
          "200": {
            "description": "CSV con licencia y fuente en la primera línea de comentario ",
            "headers": {
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                },
                "description": "attachment con el nombre del archivo"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Error público; consulta error y el estado HTTP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "404": {
            "description": "Archivo o datos no disponibles",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "405": {
            "description": "HTTP method not allowed; consult the operation method.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "429": {
            "description": "Límite público excedido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            }
          },
          "500": {
            "description": "Error público; consulta error y el estado HTTP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "503": {
            "description": "Almacén temporalmente no disponible",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          }
        }
      }
    },
    "/datos/descargas/indice-precios.csv": {
      "servers": [
        {
          "url": "https://suapea.com",
          "description": "Public web proxy to core-runtime"
        }
      ],
      "get": {
        "operationId": "downloadPriceIndexCsv",
        "summary": "Índice de precios; hasta 36 ediciones, como index/history?months=36",
        "description": "CSV UTF-8 con primera línea de comentario de licencia y fuente. Comparte CORS y presupuesto de 1200 peticiones/hora por IP con public/pvpc. También disponible en api.suapea.com/api/v1/public/downloads/indice-precios.csv.",
        "responses": {
          "200": {
            "description": "CSV con licencia y fuente en la primera línea de comentario ",
            "headers": {
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                },
                "description": "attachment con el nombre del archivo"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Error público; consulta error y el estado HTTP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "404": {
            "description": "Archivo o datos no disponibles",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "405": {
            "description": "HTTP method not allowed; consult the operation method.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "429": {
            "description": "Límite público excedido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            }
          },
          "500": {
            "description": "Error público; consulta error y el estado HTTP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "503": {
            "description": "Almacén temporalmente no disponible",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          }
        },
        "parameters": null
      }
    },
    "/datos/descargas/cambios-tarifa.csv": {
      "servers": [
        {
          "url": "https://suapea.com",
          "description": "Public web proxy to core-runtime"
        }
      ],
      "get": {
        "operationId": "downloadTariffChangesCsv",
        "summary": "Solo eventos tariff_change publicados",
        "description": "CSV UTF-8 con primera línea de comentario de licencia y fuente. Comparte CORS y presupuesto de 1200 peticiones/hora por IP con public/pvpc. También disponible en api.suapea.com/api/v1/public/downloads/cambios-tarifa.csv.",
        "responses": {
          "200": {
            "description": "CSV con licencia y fuente en la primera línea de comentario ",
            "headers": {
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                },
                "description": "attachment con el nombre del archivo"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Error público; consulta error y el estado HTTP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "404": {
            "description": "Archivo o datos no disponibles",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "405": {
            "description": "HTTP method not allowed; consult the operation method.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "429": {
            "description": "Límite público excedido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            }
          },
          "500": {
            "description": "Error público; consulta error y el estado HTTP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "503": {
            "description": "Almacén temporalmente no disponible",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          }
        },
        "parameters": null
      }
    },
    "/api/v1/public/pvpc/today": {
      "servers": [
        {
          "url": "https://api.suapea.com",
          "description": "API pública de Suapea"
        }
      ],
      "get": {
        "operationId": "getPublicPvpcToday",
        "summary": "Consultar el PVPC del día",
        "description": "Devuelve el último día publicable hasta hoy en Madrid, con precios horarios, comparaciones y estado de actualización. Si se usa un día anterior, la fecha del dato y la recomendación lo indican. Solo se publican precios verificados; si no hay datos disponibles, devuelve 404.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Datos públicos de Suapea.",
            "headers": {
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicPvpcResponse"
                }
              }
            }
          },
          "404": {
            "description": "No hay datos publicables para la consulta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "405": {
            "description": "Método HTTP no admitido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "429": {
            "description": "Límite público de peticiones excedido; consulta RateLimit-Policy y Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            }
          },
          "500": {
            "description": "Error interno del servidor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "503": {
            "description": "Servicio o datos verificados temporalmente no disponibles.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          }
        }
      }
    },
    "/api/v1/public/pvpc/{date}": {
      "servers": [
        {
          "url": "https://api.suapea.com",
          "description": "API pública de Suapea"
        }
      ],
      "get": {
        "operationId": "getPublicPvpcByDate",
        "summary": "Consultar el PVPC de una fecha",
        "description": "Devuelve los precios horarios guardados para la fecha indicada y su comparación con el día anterior y la semana anterior. Un formato incorrecto devuelve 400; una fecha imposible o sin datos publicables devuelve 404.",
        "parameters": [
          {
            "in": "path",
            "name": "date",
            "required": true,
            "description": "Fecha del dato en formato AAAA-MM-DD.",
            "schema": {
              "type": "string",
              "format": "date",
              "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$"
            },
            "example": "2026-10-17"
          }
        ],
        "responses": {
          "200": {
            "description": "Datos públicos de Suapea.",
            "headers": {
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicPvpcResponse"
                }
              }
            }
          },
          "400": {
            "description": "Parámetro de consulta o formato de fecha incorrecto.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "404": {
            "description": "No hay datos publicables para la consulta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "405": {
            "description": "Método HTTP no admitido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "429": {
            "description": "Límite público de peticiones excedido; consulta RateLimit-Policy y Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            }
          },
          "500": {
            "description": "Error interno del servidor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "503": {
            "description": "Servicio o datos verificados temporalmente no disponibles.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          }
        }
      }
    },
    "/api/v1/public/index/latest": {
      "servers": [
        {
          "url": "https://api.suapea.com",
          "description": "API pública de Suapea"
        }
      ],
      "get": {
        "operationId": "getPublicPriceIndexLatest",
        "summary": "Consultar la última edición del índice de precios",
        "description": "Devuelve la edición publicable más reciente del índice mensual, con su fuente y comparaciones entre meses. Las ediciones simuladas o sin cobertura suficiente no se publican. Si no existe una edición válida, devuelve 404. Los campos JSON importados conservan una estructura abierta.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Datos públicos de Suapea.",
            "headers": {
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicPriceIndexResponse"
                }
              }
            }
          },
          "404": {
            "description": "No hay datos publicables para la consulta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "405": {
            "description": "Método HTTP no admitido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "429": {
            "description": "Límite público de peticiones excedido; consulta RateLimit-Policy y Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            }
          },
          "500": {
            "description": "Error interno del servidor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "503": {
            "description": "Servicio o datos verificados temporalmente no disponibles.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          }
        }
      }
    },
    "/api/v1/public/index/history": {
      "servers": [
        {
          "url": "https://api.suapea.com",
          "description": "API pública de Suapea"
        }
      ],
      "get": {
        "operationId": "getPublicPriceIndexHistory",
        "summary": "Consultar el histórico del índice de precios",
        "description": "Devuelve las ediciones publicables del índice, de más reciente a más antigua. El parámetro months se lee como texto: los números se redondean hacia abajo y se limitan a 36; si falta, no es numérico o es menor que 1, se usan 12 meses. meta indica el límite aplicado y el número de ediciones devueltas.",
        "parameters": [
          {
            "in": "query",
            "name": "months",
            "required": false,
            "description": "Número de ediciones solicitado como texto. Por defecto 12; el servidor normaliza el valor y aplica un máximo de 36.",
            "schema": {
              "type": "string",
              "default": "12"
            },
            "example": "12"
          }
        ],
        "responses": {
          "200": {
            "description": "Datos públicos de Suapea.",
            "headers": {
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicPriceIndexHistoryResponse"
                }
              }
            }
          },
          "400": {
            "description": "Parámetro de consulta o formato de fecha incorrecto.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "405": {
            "description": "Método HTTP no admitido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "429": {
            "description": "Límite público de peticiones excedido; consulta RateLimit-Policy y Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            }
          },
          "500": {
            "description": "Error interno del servidor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "503": {
            "description": "Servicio o datos verificados temporalmente no disponibles.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          }
        }
      }
    },
    "/api/v1/public/datos": {
      "servers": [
        {
          "url": "https://api.suapea.com",
          "description": "API pública de Suapea"
        }
      ],
      "get": {
        "operationId": "getPublicDataObservatory",
        "summary": "Consultar el observatorio de datos",
        "description": "Devuelve el resumen del observatorio, el ahorro potencial agregado y el estado de sus productos de datos. Si falla una fuente, el producto afectado queda como coming_soon; el observatorio sigue respondiendo 200 sin inventar cifras.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Datos públicos de Suapea.",
            "headers": {
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicObservatoryResponse"
                }
              }
            }
          },
          "405": {
            "description": "Método HTTP no admitido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          },
          "429": {
            "description": "Límite público de peticiones excedido; consulta RateLimit-Policy y Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            }
          },
          "500": {
            "description": "Error interno del servidor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationPolicy"
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "headers": {
      "LicenseLink": {
        "description": "Enlace a la licencia de los datos públicos",
        "schema": {
          "type": "string",
          "const": "<https://suapea.com/datos/licencia>; rel=\"license\""
        }
      },
      "RateLimit-Limit": {
        "description": "Presupuesto público real por IP o clave configurada.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "RateLimit-Remaining": {
        "description": "Presupuesto público real por IP o clave configurada.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "RateLimit-Reset": {
        "description": "Segundos hasta restablecer el presupuesto real.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "Retry-After": {
        "description": "Segundos de espera antes de reintentar tras un 429.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "RateLimit-Policy": {
        "description": "Presupuesto y ventana del limitador real; por ejemplo 100;w=3600.",
        "schema": {
          "type": "string"
        }
      },
      "DeprecationPolicy": {
        "description": "Política de versiones y retirada. Puede coexistir con el enlace de licencia.",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "CsvDownload": {
        "description": "CSV con licencia y fuente en la primera línea de comentario.",
        "headers": {
          "Link": {
            "$ref": "#/components/headers/LicenseLink"
          },
          "Content-Disposition": {
            "schema": {
              "type": "string"
            },
            "description": "attachment con el nombre del archivo"
          }
        },
        "content": {
          "text/csv": {
            "schema": {
              "type": "string"
            }
          }
        }
      }
    },
    "schemas": {
      "CompareInput": {
        "type": "object",
        "required": [
          "monthly_kwh"
        ],
        "properties": {
          "monthly_kwh": {
            "type": "number",
            "maximum": 100000,
            "description": "Consumo mensual estimado en kWh",
            "example": 300,
            "exclusiveMinimum": 0,
            "format": "double"
          },
          "has_solar": {
            "type": "boolean",
            "default": false,
            "description": "Si el usuario tiene paneles solares"
          },
          "contracted_power_kw": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0.1,
            "maximum": 100,
            "description": "Potencia contratada en kW (por defecto 4.6)",
            "example": 4.6,
            "default": 4.6,
            "format": "double"
          }
        }
      },
      "GptResponseEnvelope": {
        "type": "object",
        "properties": {
          "license": {
            "type": "string",
            "const": "CC BY 4.0"
          },
          "attribution": {
            "type": "string",
            "const": "Fuente: Suapea (suapea.com), CC BY 4.0"
          },
          "human_readable_summary": {
            "type": "string",
            "description": "Resumen en texto plano que el LLM puede copiar directamente"
          },
          "source": {
            "type": "string",
            "description": "Atribucion de fuente con fecha",
            "example": "Suapea - suapea.com - Datos actualizados a 2026-02-13"
          },
          "source_url": {
            "type": "string",
            "format": "uri",
            "description": "URL canonica de la fuente para citacion por agentes IA",
            "example": "https://suapea.com/indice-precios"
          },
          "disclaimer": {
            "type": "string",
            "description": "Aviso para analisis personalizado",
            "example": "Para un analisis personalizado basado en tu factura real, sube tu PDF en suapea.com"
          }
        }
      },
      "TariffsResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/GptResponseEnvelope"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "properties": {
                  "count": {
                    "type": "integer"
                  },
                  "lastUpdated": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date"
                  },
                  "catalogStatus": {
                    "type": "string",
                    "enum": [
                      "ok",
                      "empty"
                    ]
                  },
                  "unavailableReason": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              }
            }
          }
        ],
        "type": "object",
        "required": [
          "data",
          "human_readable_summary",
          "source",
          "source_url",
          "disclaimer"
        ]
      },
      "TariffComparison": {
        "type": "object",
        "properties": {
          "estimated_monthly_cost_eur": {
            "type": "number"
          },
          "cost_breakdown": {
            "type": "object",
            "properties": {
              "energy_eur": {
                "type": "number"
              },
              "power_eur": {
                "type": "number"
              },
              "taxes_eur": {
                "type": "number"
              },
              "bono_social_eur": {
                "type": "number"
              }
            }
          }
        }
      },
      "CompareResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/GptResponseEnvelope"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "properties": {
                  "comparisons": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/TariffComparison"
                    }
                  },
                  "input": {
                    "type": "object",
                    "properties": {
                      "monthly_kwh": {
                        "type": "number"
                      },
                      "has_solar": {
                        "type": "boolean"
                      },
                      "contracted_power_kw": {
                        "type": "number"
                      }
                    }
                  },
                  "catalogStatus": {
                    "type": "string",
                    "enum": [
                      "ok",
                      "empty",
                      "ranking_paused"
                    ]
                  },
                  "unavailableReason": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              }
            }
          }
        ],
        "type": "object",
        "required": [
          "data",
          "human_readable_summary",
          "source",
          "source_url",
          "disclaimer"
        ]
      },
      "HourlyPrice": {
        "type": "object",
        "properties": {
          "hour": {
            "type": "string",
            "example": "03:00"
          },
          "price_eur_kwh": {
            "type": "number",
            "example": 0.0892
          },
          "period": {
            "type": "string",
            "enum": [
              "punta",
              "llano",
              "valle"
            ]
          },
          "is_estimated": {
            "type": "boolean",
            "const": false
          }
        }
      },
      "PvpcResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/GptResponseEnvelope"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "properties": {
                  "prices_by_hour": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/HourlyPrice"
                    }
                  },
                  "summary": {
                    "type": "object",
                    "properties": {
                      "average_price_eur_kwh": {
                        "type": "number"
                      },
                      "cheapest_hour": {
                        "type": "string"
                      },
                      "most_expensive_hour": {
                        "type": "string"
                      },
                      "date": {
                        "type": "string",
                        "format": "date"
                      }
                    }
                  },
                  "date": {
                    "type": "string",
                    "format": "date"
                  },
                  "stale": {
                    "type": "boolean"
                  },
                  "prices": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/HourlyPrice"
                    }
                  }
                }
              }
            }
          }
        ],
        "type": "object",
        "required": [
          "data",
          "human_readable_summary",
          "source",
          "source_url",
          "disclaimer"
        ]
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  },
                  "hint": {
                    "type": "string"
                  },
                  "docs": {
                    "type": "string",
                    "format": "uri"
                  }
                }
              },
              {
                "type": "string"
              }
            ]
          },
          "details": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "PublicJsonEnvelope": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "license": {
            "type": "string",
            "const": "CC BY 4.0"
          },
          "attribution": {
            "type": "string",
            "const": "Fuente: Suapea (suapea.com), CC BY 4.0"
          },
          "source_url": {
            "type": "string",
            "format": "uri"
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "PublicDayComparison": {
        "type": [
          "object",
          "null"
        ],
        "additionalProperties": true,
        "required": [
          "changePercent",
          "direction"
        ],
        "properties": {
          "changePercent": {
            "type": "number"
          },
          "direction": {
            "type": "string",
            "enum": [
              "up",
              "down",
              "stable"
            ]
          }
        }
      },
      "PublicHourlyPrice": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "hour": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23
          },
          "label": {
            "type": "string"
          },
          "priceEurKwh": {
            "type": "number"
          },
          "period": {
            "type": "string",
            "enum": [
              "punta",
              "llano",
              "valle"
            ]
          }
        },
        "required": [
          "hour",
          "label",
          "priceEurKwh",
          "period"
        ]
      },
      "PublicPvpcDay": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "date": {
            "type": "string",
            "format": "date"
          },
          "averagePriceEurKwh": {
            "type": "number"
          },
          "fetchedAt": {
            "type": "string",
            "format": "date-time"
          },
          "source": {
            "type": "string"
          },
          "hourly": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicHourlyPrice"
            }
          },
          "cheapestHour": {
            "type": "string"
          },
          "mostExpensiveHour": {
            "type": "string"
          },
          "recommendation": {
            "type": "string"
          },
          "vsYesterday": {
            "$ref": "#/components/schemas/PublicDayComparison"
          },
          "vsLastWeek": {
            "$ref": "#/components/schemas/PublicDayComparison"
          },
          "stale": {
            "type": "boolean"
          }
        },
        "required": [
          "date",
          "averagePriceEurKwh",
          "fetchedAt",
          "source",
          "hourly",
          "cheapestHour",
          "mostExpensiveHour",
          "recommendation",
          "vsYesterday",
          "vsLastWeek",
          "stale"
        ]
      },
      "PublicMonthComparison": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "changePercent": {
            "type": [
              "number",
              "null"
            ]
          },
          "direction": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "up",
              "down",
              "stable",
              null
            ]
          },
          "reason": {
            "type": "string",
            "const": "no_baseline"
          }
        },
        "required": [
          "changePercent",
          "direction"
        ]
      },
      "PublicPriceIndex": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ]
          },
          "period": {
            "type": "string",
            "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])$"
          },
          "publishedDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "averagePriceEurKwh": {
            "type": "number"
          },
          "source": {
            "type": "string"
          },
          "coverage": {
            "type": "number",
            "minimum": 0.9,
            "maximum": 1
          },
          "vsPreviousMonth": {
            "$ref": "#/components/schemas/PublicMonthComparison"
          },
          "vsSameMonthLastYear": {
            "$ref": "#/components/schemas/PublicMonthComparison"
          },
          "humanReadableSummary": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "period",
          "publishedDate",
          "averagePriceEurKwh",
          "source",
          "coverage",
          "vsPreviousMonth",
          "vsSameMonthLastYear"
        ]
      },
      "PublicObservatoryProduct": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "live",
              "coming_soon"
            ]
          },
          "headlineValue": {
            "type": [
              "string",
              "null"
            ]
          },
          "headlineLabel": {
            "type": "string"
          },
          "lastEdition": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastEditionLabel": {
            "type": [
              "string",
              "null"
            ]
          },
          "publishedDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "detailUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "methodologyUrl": {
            "type": "string"
          },
          "downloadUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "citable": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "type": "string"
          },
          "archive": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true,
              "properties": {
                "period": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                }
              },
              "required": [
                "period",
                "label",
                "value",
                "url"
              ]
            }
          }
        },
        "required": [
          "id",
          "name",
          "description",
          "status",
          "headlineValue",
          "headlineLabel",
          "lastEdition",
          "lastEditionLabel",
          "publishedDate",
          "detailUrl",
          "methodologyUrl",
          "downloadUrl",
          "citable",
          "source",
          "archive"
        ]
      },
      "PublicObservatorySnapshot": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "totalVerifiedSavings": {
            "type": "integer"
          },
          "lastUpdated": {
            "type": "string",
            "format": "date-time"
          },
          "products": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicObservatoryProduct"
            }
          }
        },
        "required": [
          "totalVerifiedSavings",
          "lastUpdated",
          "products"
        ]
      },
      "PublicPvpcResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PublicJsonEnvelope"
          },
          {
            "type": "object",
            "additionalProperties": true,
            "properties": {
              "data": {
                "$ref": "#/components/schemas/PublicPvpcDay"
              }
            }
          }
        ]
      },
      "PublicPriceIndexResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PublicJsonEnvelope"
          },
          {
            "type": "object",
            "additionalProperties": true,
            "properties": {
              "data": {
                "$ref": "#/components/schemas/PublicPriceIndex"
              }
            }
          }
        ]
      },
      "PublicPriceIndexHistoryResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PublicJsonEnvelope"
          },
          {
            "type": "object",
            "additionalProperties": true,
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/PublicPriceIndex"
                },
                "maxItems": 36
              },
              "meta": {
                "type": "object",
                "additionalProperties": true,
                "properties": {
                  "months": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 36
                  },
                  "count": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 36
                  }
                },
                "required": [
                  "months",
                  "count"
                ]
              }
            }
          }
        ]
      },
      "PublicObservatoryResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PublicJsonEnvelope"
          },
          {
            "type": "object",
            "additionalProperties": true,
            "properties": {
              "data": {
                "$ref": "#/components/schemas/PublicObservatorySnapshot"
              },
              "human_readable_summary": {
                "type": "string"
              },
              "source": {
                "type": "string"
              },
              "data_updated_at": {
                "type": "string",
                "format": "date"
              }
            }
          }
        ]
      }
    }
  }
}
