{
  "openapi": "3.1.0",
  "info": {
    "title": "VamosAlicante Public API",
    "version": "1.0.0",
    "description": "API pública de lectura sobre Alicante (España): parkings con plazas libres, TRAM, playas y resolución de consultas a URLs canónicas. Sin clave de acceso, solo GET. Todas las marcas temporales se publican en hora de Europe/Madrid y cada respuesta declara la naturaleza del dato (LIVE, CALCULATED, CATALOG, STATIC) y su caducidad.",
    "termsOfService": "https://vamosalicante.com/aviso-legal",
    "contact": {
      "name": "VamosAlicante",
      "url": "https://vamosalicante.com/contacto"
    },
    "license": {
      "name": "Atribución requerida",
      "url": "https://vamosalicante.com/data-methodology"
    },
    "x-license-note": "Uso libre para lectura y citación con atribución y enlace a la URL canónica. Reventa de los datos no autorizada.",
    "x-api-version": "1",
    "x-access-policy": {
      "url": "https://vamosalicante.com/api/public/v1/policy",
      "principle": "El acceso resuelve una necesidad concreta del usuario. Se permite el uso de los datos; no se permite la exportación del inventario.",
      "proportionality": "El volumen entregado es proporcional a la necesidad. Sin parámetro de acotación se devuelve una respuesta resumida con la URL canónica, no el inventario.",
      "cumulativeControl": "Cada cliente se evalúa por su historial reciente (ventana de 60 minutos): número de consultas, entidades únicas por sector, proporción de entidades distintas y patrón de enumeración.",
      "windowMinutes": 60,
      "blocked": [
        "Enumerar todas las paradas, fichas o cartas de un sector.",
        "Recorrer identificadores de forma secuencial para reconstruir un catálogo.",
        "Descargas o exportaciones masivas de contenido, fotografías o multimedia.",
        "Series de consultas que, acumuladas, extraen un sector completo."
      ]
    }
  },
  "servers": [
    {
      "url": "https://vamosalicante.com",
      "description": "Producción"
    }
  ],
  "externalDocs": {
    "description": "Metodología y fuentes",
    "url": "https://vamosalicante.com/data-methodology"
  },
  "paths": {
    "/api/public/v1": {
      "get": {
        "x-access-class": "meta",
        "x-access-bounding-params": [],
        "x-access-max-limit": 0,
        "x-access-high-value": false,
        "operationId": "api_public_v1",
        "summary": "Índice de la API",
        "description": "Descubrimiento: secciones en el orden de la portada, endpoints estables, naturaleza del dato, TTL recomendado y enlace al esquema OpenAPI.\n\nNaturaleza del dato: STATIC. Fuente: VamosAlicante. Refresco recomendado: 3600s.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/openapi.json": {
      "get": {
        "x-access-class": "meta",
        "x-access-bounding-params": [],
        "x-access-max-limit": 0,
        "x-access-high-value": false,
        "operationId": "api_public_v1_openapi_json",
        "summary": "Esquema OpenAPI 3.1",
        "description": "Esquema completo de la API pública, apto para Actions de GPT, herramientas de agentes y clientes generados.\n\nNaturaleza del dato: STATIC. Fuente: VamosAlicante. Refresco recomendado: 3600s.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/policy": {
      "get": {
        "x-access-class": "meta",
        "x-access-bounding-params": [],
        "x-access-max-limit": 0,
        "x-access-high-value": false,
        "operationId": "api_public_v1_policy",
        "summary": "Política de acceso y salida de datos para agentes IA",
        "description": "Reglas de uso: se permite resolver necesidades concretas y no se permite la exportación del inventario. Publica la clase de acceso de cada endpoint, los parámetros de acotación exigidos, los topes por respuesta y los límites acumulados por cliente.\n\nNaturaleza del dato: STATIC. Fuente: VamosAlicante. Refresco recomendado: 3600s.\nPágina canónica: https://vamosalicante.com/data-methodology",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/search": {
      "get": {
        "x-llm-usage": "Úsalo cuando el usuario busca una página, un tema o contenido del sitio y no una entidad medible. Si lo que necesitas es el identificador de una parada, un parking o una playa para consultar su estado, usa /resolve en su lugar. No lo uses para recorrer el sitio.",
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q"
        ],
        "x-access-max-limit": 20,
        "x-access-high-value": false,
        "operationId": "api_public_v1_search",
        "summary": "Buscador del sitio",
        "description": "Resolución de una consulta en lenguaje natural a URLs canónicas del sitio, agrupadas por categoría. Punto de entrada recomendado para un agente que no conoce la ruta exacta.\n\nCuándo usar este endpoint (instrucción para agentes): Úsalo cuando el usuario busca una página, un tema o contenido del sitio y no una entidad medible. Si lo que necesitas es el identificador de una parada, un parking o una playa para consultar su estado, usa /resolve en su lugar. No lo uses para recorrer el sitio.\n\nNaturaleza del dato: CATALOG. Fuente: VamosAlicante. Refresco recomendado: 600s.\nPágina canónica: https://vamosalicante.com/buscar\n\nAcceso: clase inventory. Máximo 20 elementos por consulta (por defecto 10). Acota la consulta con: q; sin acotación la respuesta se resume a 5 elementos. Pedir más de 20 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Consulta libre.",
            "schema": {
              "type": "string",
              "example": "parking cerca del teatro"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/resolve": {
      "get": {
        "x-llm-usage": "Primer paso obligatorio cuando el usuario nombra un lugar y necesitas datos vivos de él («¿cuándo pasa el TRAM por Paradís?», «¿hay sitio en el parking del mercado?»). Devuelve el entityId que luego pasas a /state. Si `ambiguous` es true, pregunta al usuario cuál quería. No lo uses para listar entidades ni para contenido editorial.",
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q",
          "type"
        ],
        "x-access-max-limit": 10,
        "x-access-high-value": false,
        "operationId": "api_public_v1_resolve",
        "summary": "Resolución de entidades por nombre aproximado",
        "description": "Convierte un nombre en lenguaje natural, con tildes, alias o erratas, en un identificador estable `entityId` (`<tipo>:<id>`) con su tipo, coordenadas, URL canónica y endpoint de estado. Tipos: parking, tram-stop, bus-stop, beach.\n\nCuándo usar este endpoint (instrucción para agentes): Primer paso obligatorio cuando el usuario nombra un lugar y necesitas datos vivos de él («¿cuándo pasa el TRAM por Paradís?», «¿hay sitio en el parking del mercado?»). Devuelve el entityId que luego pasas a /state. Si `ambiguous` es true, pregunta al usuario cuál quería. No lo uses para listar entidades ni para contenido editorial.\n\nNaturaleza del dato: CATALOG. Fuente: VamosAlicante. Refresco recomendado: 3600s.\nPágina canónica: https://vamosalicante.com/buscar\n\nAcceso: clase inventory. Máximo 10 elementos por consulta (por defecto 5). Acota la consulta con: q, type; sin acotación la respuesta se resume a 3 elementos. Pedir más de 10 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Nombre aproximado de la entidad, tal como lo dice el usuario.",
            "schema": {
              "type": "string",
              "example": "paradis"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Restringe el tipo: parking, tram-stop, bus-stop o beach.",
            "schema": {
              "type": "string",
              "example": "tram-stop"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 5. Máximo 10 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10,
              "default": 5
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/state": {
      "get": {
        "x-llm-usage": "Úsalo únicamente con un entity_id ya resuelto, cuando el usuario pregunta por el estado ahora mismo de ese lugar concreto. Si `dataType` es STALE, no afirmes que es el estado actual: di cuándo se midió por última vez o remite a la URL canónica. No lo uses para tarifas, horarios completos ni descripciones estáticas.",
        "x-access-class": "entity",
        "x-access-bounding-params": [
          "entity_id"
        ],
        "x-access-max-limit": 40,
        "x-access-high-value": false,
        "operationId": "api_public_v1_state",
        "summary": "Estado actual de una entidad concreta",
        "description": "Estado vivo de una sola entidad identificada por `entity_id`, sea del sector que sea: plazas libres de un parking, próximas llegadas de una parada de bus, próximas salidas de una estación de TRAM o bandera de una playa. La respuesta declara `dataType`, `observedAt`, `validThrough` y `freshness`; un dato caducado se entrega como STALE.\n\nCuándo usar este endpoint (instrucción para agentes): Úsalo únicamente con un entity_id ya resuelto, cuando el usuario pregunta por el estado ahora mismo de ese lugar concreto. Si `dataType` es STALE, no afirmes que es el estado actual: di cuándo se midió por última vez o remite a la URL canónica. No lo uses para tarifas, horarios completos ni descripciones estáticas.\n\nNaturaleza del dato: LIVE. Fuente: VamosAlicante (agrega fuentes oficiales por sector). Refresco recomendado: 60s.\nPágina canónica: https://vamosalicante.com/buscar\n\nAcceso: clase entity. Máximo 40 elementos por consulta (por defecto 20). Acota la consulta con: entity_id; sin acotación la respuesta se resume a 20 elementos. Pedir más de 40 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "entity_id",
            "in": "query",
            "required": true,
            "description": "Identificador `<tipo>:<id>` devuelto por /api/public/v1/resolve.",
            "schema": {
              "type": "string",
              "example": "parking:mercado"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/parkings": {
      "get": {
        "x-access-class": "bounded",
        "x-access-bounding-params": [],
        "x-access-max-limit": 40,
        "x-access-high-value": false,
        "operationId": "api_public_v1_parkings",
        "summary": "Plazas libres en parkings de Alicante",
        "description": "Ocupación de los parkings públicos. `dataType` es LIVE cuando hay observación de sensores vigente y CATALOG cuando solo hay ficha (capacidad, dirección) sin medición.\n\nNaturaleza del dato: LIVE. Fuente: Ayuntamiento de Alicante (movilidad.alicante.es). Refresco recomendado: 180s.\nPágina canónica: https://vamosalicante.com/parkings-libres-ahora\n\nAcceso: clase bounded. Máximo 40 elementos por consulta (por defecto 40). Pedir más de 40 devuelve 403 scope_required.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/weather/now": {
      "get": {
        "x-access-class": "bounded",
        "x-access-bounding-params": [],
        "x-access-max-limit": 1,
        "x-access-high-value": false,
        "operationId": "api_public_v1_weather_now",
        "summary": "Tiempo actual, previsión y mar",
        "description": "Observación actual (temperatura, viento, humedad, UV), previsión horaria y diaria, temperatura del mar y altura de ola.\n\nNaturaleza del dato: LIVE. Fuente: Open-Meteo (modelo) y Open-Meteo Marine. Refresco recomendado: 600s.\nPágina canónica: https://vamosalicante.com/weather\n\nAcceso: clase bounded. Máximo 1 elementos por consulta (por defecto 1). Pedir más de 1 devuelve 403 scope_required.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/weather/alerts": {
      "get": {
        "x-access-class": "bounded",
        "x-access-bounding-params": [],
        "x-access-max-limit": 20,
        "x-access-high-value": false,
        "operationId": "api_public_v1_weather_alerts",
        "summary": "Avisos meteorológicos oficiales (AEMET)",
        "description": "Avisos vigentes y próximos publicados por AEMET para la provincia de Alicante, con nivel, fenómeno y ventana temporal.\n\nNaturaleza del dato: LIVE. Fuente: AEMET. Refresco recomendado: 600s.\nPágina canónica: https://vamosalicante.com/weather\n\nAcceso: clase bounded. Máximo 20 elementos por consulta (por defecto 20). Pedir más de 20 devuelve 403 scope_required.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/food/places": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q",
          "cuisine"
        ],
        "x-access-max-limit": 15,
        "x-access-high-value": true,
        "operationId": "api_public_v1_food_places",
        "summary": "Restaurantes y locales de comida",
        "description": "Catálogo de restaurantes con cocina, dirección, coordenadas, horario publicado y rango de precio cuando la fuente lo publica.\n\nNaturaleza del dato: CATALOG. Fuente: VamosAlicante sobre fichas públicas de los locales. Refresco recomendado: 3600s.\nPágina canónica: https://vamosalicante.com/food-guide\n\nAcceso: clase inventory (recurso de alto valor). Máximo 15 elementos por consulta (por defecto 5). Acota la consulta con: q, cuisine; sin acotación la respuesta se resume a 3 elementos. Pedir más de 15 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda por nombre.",
            "schema": {
              "type": "string",
              "example": "arroz"
            }
          },
          {
            "name": "cuisine",
            "in": "query",
            "required": false,
            "description": "Filtra por cocina.",
            "schema": {
              "type": "string",
              "example": "italiana"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 5. Máximo 15 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 15,
              "default": 5
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/food/search": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q"
        ],
        "x-access-max-limit": 15,
        "x-access-high-value": true,
        "operationId": "api_public_v1_food_search",
        "summary": "Buscador gastronómico (plato o cocina)",
        "description": "Resuelve una consulta gastronómica en lenguaje natural con el mismo motor que la web: devuelve locales elegibles y, cuando existe, el plato y su precio en carta.\n\nNaturaleza del dato: CALCULATED. Fuente: Motor gastronómico de VamosAlicante sobre cartas publicadas. Refresco recomendado: 600s.\nPágina canónica: https://vamosalicante.com/food-guide\n\nAcceso: clase inventory (recurso de alto valor). Máximo 15 elementos por consulta (por defecto 5). Acota la consulta con: q; sin acotación la respuesta se resume a 3 elementos. Pedir más de 15 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Consulta gastronómica.",
            "schema": {
              "type": "string",
              "example": "arroz a banda"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 5. Máximo 15 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 15,
              "default": 5
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/accommodation/hotels": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q",
          "stars",
          "zone"
        ],
        "x-access-max-limit": 20,
        "x-access-high-value": false,
        "operationId": "api_public_v1_accommodation_hotels",
        "summary": "Hoteles y alojamientos",
        "description": "Catálogo de alojamientos con categoría, barrio, coordenadas y, cuando hay lectura vigente del proveedor, precio actual y disponibilidad.\n\nNaturaleza del dato: CATALOG. Fuente: VamosAlicante sobre fichas de alojamiento y proveedores de reserva. Refresco recomendado: 1800s.\nPágina canónica: https://vamosalicante.com/accommodation\n\nAcceso: clase inventory. Máximo 20 elementos por consulta (por defecto 6). Acota la consulta con: q, stars, zone; sin acotación la respuesta se resume a 3 elementos. Pedir más de 20 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda por nombre.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "stars",
            "in": "query",
            "required": false,
            "description": "Categoría mínima en estrellas.",
            "schema": {
              "type": "string",
              "example": "4"
            }
          },
          {
            "name": "zone",
            "in": "query",
            "required": false,
            "description": "Barrio o zona del alojamiento.",
            "schema": {
              "type": "string",
              "example": "centro"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 6. Máximo 20 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 6
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/beaches": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q",
          "municipality"
        ],
        "x-access-max-limit": 25,
        "x-access-high-value": false,
        "operationId": "api_public_v1_beaches",
        "summary": "Playas y calas de la provincia",
        "description": "Catálogo de playas y calas con municipio, coordenadas y URL canónica de su ficha.\n\nNaturaleza del dato: CATALOG. Fuente: VamosAlicante. Refresco recomendado: 86400s.\nPágina canónica: https://vamosalicante.com/beach-guide\n\nAcceso: clase inventory. Máximo 25 elementos por consulta (por defecto 10). Acota la consulta con: q, municipality; sin acotación la respuesta se resume a 6 elementos. Pedir más de 25 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda por nombre de playa o cala.",
            "schema": {
              "type": "string",
              "example": "Postiguet"
            }
          },
          {
            "name": "municipality",
            "in": "query",
            "required": false,
            "description": "Filtra por municipio.",
            "schema": {
              "type": "string",
              "example": "Alicante"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 10. Máximo 25 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/beaches/flags": {
      "get": {
        "x-access-class": "bounded",
        "x-access-bounding-params": [],
        "x-access-max-limit": 60,
        "x-access-high-value": false,
        "operationId": "api_public_v1_beaches_flags",
        "summary": "Banderas de las playas de Alicante",
        "description": "Estado de bandera por playa (verde, amarilla, roja o sin dato) según la publicación municipal del día.\n\nNaturaleza del dato: LIVE. Fuente: Ayuntamiento de Alicante. Refresco recomendado: 900s.\nPágina canónica: https://vamosalicante.com/beach-guide\n\nAcceso: clase bounded. Máximo 60 elementos por consulta (por defecto 60). Pedir más de 60 devuelve 403 scope_required.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/shopping/zones": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q"
        ],
        "x-access-max-limit": 25,
        "x-access-high-value": false,
        "operationId": "api_public_v1_shopping_zones",
        "summary": "Zonas comerciales",
        "description": "Zonas y barrios comerciales de Alicante usados para agrupar los comercios.\n\nNaturaleza del dato: CATALOG. Fuente: VamosAlicante. Refresco recomendado: 86400s.\nPágina canónica: https://vamosalicante.com/shopping\n\nAcceso: clase inventory. Máximo 25 elementos por consulta (por defecto 12). Acota la consulta con: q; sin acotación la respuesta se resume a 8 elementos. Pedir más de 25 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda por nombre de zona comercial.",
            "schema": {
              "type": "string",
              "example": "centro"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 12. Máximo 25 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25,
              "default": 12
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/shopping/businesses": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q",
          "zone",
          "sector"
        ],
        "x-access-max-limit": 20,
        "x-access-high-value": false,
        "operationId": "api_public_v1_shopping_businesses",
        "summary": "Comercios por zona y sector",
        "description": "Comercios con sector, zona, dirección, coordenadas, teléfono y horario publicado cuando existe.\n\nNaturaleza del dato: CATALOG. Fuente: VamosAlicante sobre fichas públicas de los comercios. Refresco recomendado: 3600s.\nPágina canónica: https://vamosalicante.com/shopping\n\nAcceso: clase inventory. Máximo 20 elementos por consulta (por defecto 6). Acota la consulta con: q, zone, sector; sin acotación la respuesta se resume a 3 elementos. Pedir más de 20 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda por nombre o marca.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "zone",
            "in": "query",
            "required": false,
            "description": "Slug de la zona comercial.",
            "schema": {
              "type": "string",
              "example": "centro"
            }
          },
          {
            "name": "sector",
            "in": "query",
            "required": false,
            "description": "Slug del sector comercial (ver /api/public/v1/shopping/zones y la guía de compras).",
            "schema": {
              "type": "string",
              "example": "comercio-servicios"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 6. Máximo 20 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 6
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/nightlife/places": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q",
          "kind"
        ],
        "x-access-max-limit": 20,
        "x-access-high-value": false,
        "operationId": "api_public_v1_nightlife_places",
        "summary": "Bares, cervecerías, cocktelerías y discotecas",
        "description": "Locales de copas y ocio nocturno con tipo (bar, cocktelería, cervecería, discoteca), dirección, coordenadas y horario publicado.\n\nNaturaleza del dato: CATALOG. Fuente: VamosAlicante sobre fichas públicas de los locales. Refresco recomendado: 3600s.\nPágina canónica: https://vamosalicante.com/nightlife\n\nAcceso: clase inventory. Máximo 20 elementos por consulta (por defecto 6). Acota la consulta con: q, kind; sin acotación la respuesta se resume a 3 elementos. Pedir más de 20 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda por nombre.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "description": "Tipo de local: bar | cocktail_bar | cerveceria | discoteca.",
            "schema": {
              "type": "string",
              "example": "cocktail_bar"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 6. Máximo 20 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 6
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/bus/lines": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q",
          "line"
        ],
        "x-access-max-limit": 20,
        "x-access-high-value": false,
        "operationId": "api_public_v1_bus_lines",
        "summary": "Líneas de bus urbano de Alicante",
        "description": "Catálogo de líneas del autobús urbano con código, nombre, color y operador.\n\nNaturaleza del dato: CATALOG. Fuente: Vectalia / Alicante se mueve. Refresco recomendado: 86400s.\nPágina canónica: https://vamosalicante.com/citybus\n\nAcceso: clase inventory. Máximo 20 elementos por consulta (por defecto 10). Acota la consulta con: q, line; sin acotación la respuesta se resume a 3 elementos. Pedir más de 20 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda por nombre de línea.",
            "schema": {
              "type": "string",
              "example": "playa"
            }
          },
          {
            "name": "line",
            "in": "query",
            "required": false,
            "description": "Código de una línea concreta.",
            "schema": {
              "type": "string",
              "example": "2"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 10. Máximo 20 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/bus/stops": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q",
          "line",
          "stop"
        ],
        "x-access-max-limit": 20,
        "x-access-high-value": false,
        "operationId": "api_public_v1_bus_stops",
        "summary": "Paradas de bus urbano",
        "description": "Paradas con código oficial, nombre, líneas que pasan y coordenadas.\n\nNaturaleza del dato: CATALOG. Fuente: Vectalia / Alicante se mueve. Refresco recomendado: 86400s.\nPágina canónica: https://vamosalicante.com/citybus\n\nAcceso: clase inventory. Máximo 20 elementos por consulta (por defecto 8). Acota la consulta con: q, line, stop; sin acotación la respuesta se resume a 3 elementos. Pedir más de 20 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda por nombre de parada.",
            "schema": {
              "type": "string",
              "example": "Luceros"
            }
          },
          {
            "name": "line",
            "in": "query",
            "required": false,
            "description": "Filtra las paradas de una línea.",
            "schema": {
              "type": "string",
              "example": "2"
            }
          },
          {
            "name": "stop",
            "in": "query",
            "required": false,
            "description": "Código oficial de una parada concreta.",
            "schema": {
              "type": "string",
              "example": "1010"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 8. Máximo 20 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 8
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/bus/arrivals": {
      "get": {
        "x-llm-usage": "Úsalo cuando el usuario pregunta cuánto falta para el próximo bus en una parada concreta. Necesitas el código de parada: obténlo con /resolve o /bus/stops, nunca lo inventes. Si el usuario no indica sentido, no elijas por él: presenta `directions` con el destino de cada sentido. Si `arrivals` viene vacío, di que el operador no publica llegadas ahora, no estimes.",
        "x-access-class": "entity",
        "x-access-bounding-params": [
          "stop"
        ],
        "x-access-max-limit": 40,
        "x-access-high-value": false,
        "operationId": "api_public_v1_bus_arrivals",
        "summary": "Próximas llegadas de bus a una parada",
        "description": "Llegadas por parada. LIVE cuando hay lectura de tiempo real del operador vigente; si no hay lectura, `data` viene vacío y se declara la ausencia. Sin `direction` se devuelven los dos sentidos, resumidos en `directions` con su destino y la primera llegada de cada uno.\n\nCuándo usar este endpoint (instrucción para agentes): Úsalo cuando el usuario pregunta cuánto falta para el próximo bus en una parada concreta. Necesitas el código de parada: obténlo con /resolve o /bus/stops, nunca lo inventes. Si el usuario no indica sentido, no elijas por él: presenta `directions` con el destino de cada sentido. Si `arrivals` viene vacío, di que el operador no publica llegadas ahora, no estimes.\n\nNaturaleza del dato: LIVE. Fuente: Vectalia / Alicante se mueve (tiempo real). Refresco recomendado: 30s.\nPágina canónica: https://vamosalicante.com/citybus\n\nAcceso: clase entity. Máximo 40 elementos por consulta (por defecto 20). Acota la consulta con: stop; sin acotación la respuesta se resume a 20 elementos. Pedir más de 40 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "stop",
            "in": "query",
            "required": true,
            "description": "Código oficial de la parada.",
            "schema": {
              "type": "string",
              "example": "1010"
            }
          },
          {
            "name": "line",
            "in": "query",
            "required": false,
            "description": "Filtra por línea.",
            "schema": {
              "type": "string",
              "example": "2"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "description": "Filtra el sentido tal como lo publica el operador. Omítelo para recibir ambos sentidos.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/bus/live-arrivals": {
      "get": {
        "x-llm-usage": "Úsalo cuando el usuario pregunta por el próximo bus AHORA. Responde en ~2,5 s por defecto; si quieres insistir en el dato en vivo añade `wait=<segundos>` (máx. 15). Sin LIVE llega `estimated_eta` del motor propio y `retry_after_seconds` para una segunda lectura. Si status=success responde con live_eta indicando que es tiempo real. Si status=timeout di literalmente que no ha sido posible obtener el dato en tiempo real; si status=no_data di que la fuente no publica pasos ahora. Nunca presentes estimated_eta como tiempo real ni inventes minutos.",
        "x-access-class": "entity",
        "x-access-bounding-params": [
          "stop"
        ],
        "x-access-max-limit": 40,
        "x-access-high-value": true,
        "operationId": "api_public_v1_bus_live_arrivals",
        "summary": "Próximo paso de bus consultado en tiempo real bajo demanda",
        "description": "Consulta puntual a la fuente oficial en el momento de la petición (sin caché ni polling). `status` vale live_query, success, timeout, error o no_data y `data_status` vale live, unavailable o pending. `live_eta` existe únicamente con status=success; la estimación del motor propio viaja aparte en `estimated_eta` y nunca se presenta como tiempo real.\n\nCuándo usar este endpoint (instrucción para agentes): Úsalo cuando el usuario pregunta por el próximo bus AHORA. Responde en ~2,5 s por defecto; si quieres insistir en el dato en vivo añade `wait=<segundos>` (máx. 15). Sin LIVE llega `estimated_eta` del motor propio y `retry_after_seconds` para una segunda lectura. Si status=success responde con live_eta indicando que es tiempo real. Si status=timeout di literalmente que no ha sido posible obtener el dato en tiempo real; si status=no_data di que la fuente no publica pasos ahora. Nunca presentes estimated_eta como tiempo real ni inventes minutos.\n\nNaturaleza del dato: LIVE. Fuente: Alicante se mueve (movilidad.alicante.es, tiempo real bajo demanda). Refresco recomendado: 30s.\nPágina canónica: https://vamosalicante.com/citybus\n\nAcceso: clase entity (recurso de alto valor). Máximo 40 elementos por consulta (por defecto 20). Acota la consulta con: stop; sin acotación la respuesta se resume a 20 elementos. Pedir más de 40 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "stop",
            "in": "query",
            "required": true,
            "description": "Código oficial de la parada o su nombre (por ejemplo `Franciscanas`).",
            "schema": {
              "type": "string",
              "example": "5101"
            }
          },
          {
            "name": "line",
            "in": "query",
            "required": false,
            "description": "Filtra por línea.",
            "schema": {
              "type": "string",
              "example": "2"
            }
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "description": "`announce` devuelve status=live_query sin tocar la fuente, para avisar de que la consulta está en curso.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "estimate",
            "in": "query",
            "required": false,
            "description": "`1` añade `estimated_eta` del motor propio cuando la fuente no publica dato en vivo.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/mobility/catch-decision": {
      "get": {
        "x-llm-usage": "Úsalo cuando el usuario pregunta si llega a tiempo al bus. Necesitas su posición: pídesela, no la inventes. Di siempre si el ETA es tiempo real (`etaKind=live`) o estimación propia (`etaKind=estimated`). El tiempo a pie es una estimación en línea recta con factor de callejero: no lo presentes como ruta medida. Con decision=MISS ofrece `nextViablePass`.",
        "x-access-class": "entity",
        "x-access-bounding-params": [
          "stop"
        ],
        "x-access-max-limit": 40,
        "x-access-high-value": true,
        "operationId": "api_public_v1_mobility_catch_decision",
        "summary": "¿Me da tiempo a coger este bus?",
        "description": "Cruza el ETA del vehículo (tiempo real de la fuente oficial o, si no llega, estimación del motor propio) con el tiempo a pie del usuario desde sus coordenadas hasta la parada. Devuelve `decision` = CATCH | TIGHT | MISS | UNKNOWN, el margen en segundos y el siguiente paso alcanzable.\n\nCuándo usar este endpoint (instrucción para agentes): Úsalo cuando el usuario pregunta si llega a tiempo al bus. Necesitas su posición: pídesela, no la inventes. Di siempre si el ETA es tiempo real (`etaKind=live`) o estimación propia (`etaKind=estimated`). El tiempo a pie es una estimación en línea recta con factor de callejero: no lo presentes como ruta medida. Con decision=MISS ofrece `nextViablePass`.\n\nNaturaleza del dato: LIVE. Fuente: Alicante se mueve (tiempo real) + motor propio de VamosAlicante. Refresco recomendado: 30s.\nPágina canónica: https://vamosalicante.com/citybus\n\nAcceso: clase entity (recurso de alto valor). Máximo 40 elementos por consulta (por defecto 20). Acota la consulta con: stop; sin acotación la respuesta se resume a 20 elementos. Pedir más de 40 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "stop",
            "in": "query",
            "required": true,
            "description": "Código oficial de la parada o su nombre.",
            "schema": {
              "type": "string",
              "example": "5101"
            }
          },
          {
            "name": "lat",
            "in": "query",
            "required": false,
            "description": "Latitud actual del usuario (alternativa a `walk_minutes`).",
            "schema": {
              "type": "string",
              "example": "38.3452"
            }
          },
          {
            "name": "lng",
            "in": "query",
            "required": false,
            "description": "Longitud actual del usuario (alternativa a `walk_minutes`).",
            "schema": {
              "type": "string",
              "example": "-0.4815"
            }
          },
          {
            "name": "walk_minutes",
            "in": "query",
            "required": false,
            "description": "Minutos a pie hasta la parada, si el agente no tiene coordenadas.",
            "schema": {
              "type": "string",
              "example": "4"
            }
          },
          {
            "name": "line",
            "in": "query",
            "required": false,
            "description": "Línea concreta.",
            "schema": {
              "type": "string",
              "example": "12"
            }
          },
          {
            "name": "walk_speed_ms",
            "in": "query",
            "required": false,
            "description": "Velocidad de marcha en m/s (0.5-2.5). Por defecto 1.35.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "buffer_seconds",
            "in": "query",
            "required": false,
            "description": "Colchón de seguridad en segundos. Por defecto 30.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/tram/stations": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q",
          "line_id",
          "stop_id"
        ],
        "x-access-max-limit": 25,
        "x-access-high-value": false,
        "operationId": "api_public_v1_tram_stations",
        "summary": "Estaciones del TRAM de Alicante",
        "description": "Catálogo de paradas del TRAM (GTFS oficial de FGV) con identificador y coordenadas. Acepta búsqueda por nombre o filtro por línea.\n\nNaturaleza del dato: CATALOG. Fuente: FGV (GTFS oficial). Refresco recomendado: 86400s.\nPágina canónica: https://vamosalicante.com/citytrain\n\nAcceso: clase inventory. Máximo 25 elementos por consulta (por defecto 10). Acota la consulta con: q, line_id, stop_id; sin acotación la respuesta se resume a 3 elementos. Pedir más de 25 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda por nombre de estación.",
            "schema": {
              "type": "string",
              "example": "Luceros"
            }
          },
          {
            "name": "line_id",
            "in": "query",
            "required": false,
            "description": "Filtra las estaciones de una línea.",
            "schema": {
              "type": "string",
              "example": "L1"
            }
          },
          {
            "name": "stop_id",
            "in": "query",
            "required": false,
            "description": "Consulta directa por identificador GTFS.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 10. Máximo 25 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/tram/departures": {
      "get": {
        "x-llm-usage": "Úsalo cuando el usuario pregunta por el próximo TRAM en una estación concreta. Resuelve antes el stop_id con /resolve o /tram/stations. El usuario casi nunca dice el sentido: no lo supongas, muestra `directions` con el destino de cada sentido. Declara siempre que es horario oficial calculado, no posición real del tren.",
        "x-access-class": "entity",
        "x-access-bounding-params": [
          "stop_id"
        ],
        "x-access-max-limit": 40,
        "x-access-high-value": false,
        "operationId": "api_public_v1_tram_departures",
        "summary": "Próximas salidas de TRAM por estación",
        "description": "Salidas calculadas sobre el horario oficial de FGV para la fecha indicada. Es un cálculo (CALCULATED), no una observación de posición del tren. Sin `direction` se devuelven los dos sentidos, resumidos en `directions` con su destino (headsign) y la primera salida de cada uno.\n\nCuándo usar este endpoint (instrucción para agentes): Úsalo cuando el usuario pregunta por el próximo TRAM en una estación concreta. Resuelve antes el stop_id con /resolve o /tram/stations. El usuario casi nunca dice el sentido: no lo supongas, muestra `directions` con el destino de cada sentido. Declara siempre que es horario oficial calculado, no posición real del tren.\n\nNaturaleza del dato: CALCULATED. Fuente: FGV (GTFS oficial). Refresco recomendado: 60s.\nPágina canónica: https://vamosalicante.com/citytrain\n\nAcceso: clase entity. Máximo 40 elementos por consulta (por defecto 20). Acota la consulta con: stop_id; sin acotación la respuesta se resume a 20 elementos. Pedir más de 40 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "stop_id",
            "in": "query",
            "required": true,
            "description": "Identificador GTFS de la estación.",
            "schema": {
              "type": "string",
              "example": "1"
            }
          },
          {
            "name": "date",
            "in": "query",
            "required": false,
            "description": "Fecha YYYY-MM-DD en hora de Madrid; por defecto hoy.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Hora de inicio HH:MM; por defecto ahora.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "description": "Sentido GTFS (0 o 1) o texto del destino. Omítelo para recibir ambos sentidos y decidir con el usuario.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 20. Máximo 40 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 40,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/train/departures": {
      "get": {
        "x-access-class": "bounded",
        "x-access-bounding-params": [],
        "x-access-max-limit": 40,
        "x-access-high-value": false,
        "operationId": "api_public_v1_train_departures",
        "summary": "Salidas y llegadas de tren en Alicante Terminal",
        "description": "Cartelera de la estación de Alicante Terminal publicada por ADIF: hora, tren, operador, destino u origen y vía cuando la fuente la publica.\n\nNaturaleza del dato: LIVE. Fuente: ADIF (cartelera de Alicante Terminal). Refresco recomendado: 120s.\nPágina canónica: https://vamosalicante.com/railway\n\nAcceso: clase bounded. Máximo 40 elementos por consulta (por defecto 40). Pedir más de 40 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "departures | arrivals | all (por defecto all).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 40. Máximo 40 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 40,
              "default": 40
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/coach/routes": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q",
          "destination"
        ],
        "x-access-max-limit": 20,
        "x-access-high-value": false,
        "operationId": "api_public_v1_coach_routes",
        "summary": "Rutas de autobús de larga distancia",
        "description": "Corredores y rutas interurbanas con origen, destino y operadores.\n\nNaturaleza del dato: CATALOG. Fuente: Operadores de larga distancia (ALSA y otros). Refresco recomendado: 86400s.\nPágina canónica: https://vamosalicante.com/coach-travel\n\nAcceso: clase inventory. Máximo 20 elementos por consulta (por defecto 8). Acota la consulta con: q, destination; sin acotación la respuesta se resume a 3 elementos. Pedir más de 20 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda por corredor o etiqueta de la ruta.",
            "schema": {
              "type": "string",
              "example": "Madrid"
            }
          },
          {
            "name": "destination",
            "in": "query",
            "required": false,
            "description": "Código o nombre del destino.",
            "schema": {
              "type": "string",
              "example": "VLC"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 8. Máximo 20 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 8
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/coach/departures": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "destination",
          "date"
        ],
        "x-access-max-limit": 25,
        "x-access-high-value": false,
        "operationId": "api_public_v1_coach_departures",
        "summary": "Salidas de autobús de larga distancia",
        "description": "Servicios programados por fecha con hora de salida y llegada, duración, operador y precio desde cuando el operador lo publica.\n\nNaturaleza del dato: CALCULATED. Fuente: Operadores de larga distancia (ALSA y otros). Refresco recomendado: 900s.\nPágina canónica: https://vamosalicante.com/coach-travel\n\nAcceso: clase inventory. Máximo 25 elementos por consulta (por defecto 10). Acota la consulta con: destination, date; sin acotación la respuesta se resume a 3 elementos. Pedir más de 25 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "date",
            "in": "query",
            "required": false,
            "description": "Fecha YYYY-MM-DD en hora de Madrid; por defecto hoy.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "destination",
            "in": "query",
            "required": false,
            "description": "Búsqueda por destino.",
            "schema": {
              "type": "string",
              "example": "Valencia"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 10. Máximo 25 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/car-rental/offices": {
      "get": {
        "x-access-class": "bounded",
        "x-access-bounding-params": [],
        "x-access-max-limit": 20,
        "x-access-high-value": false,
        "operationId": "api_public_v1_car_rental_offices",
        "summary": "Puntos de recogida de alquiler de vehículos",
        "description": "Estructura estable para las oficinas de alquiler. El dataset propio aún no se publica: la respuesta devuelve `offices` vacío y enlaza a las páginas canónicas del comparador.\n\nNaturaleza del dato: CATALOG. Fuente: VamosAlicante. Refresco recomendado: 3600s.\nPágina canónica: https://vamosalicante.com/car-rental\n\nAcceso: clase bounded. Máximo 20 elementos por consulta (por defecto 20). Pedir más de 20 devuelve 403 scope_required.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/fuel/prices": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q",
          "fuel"
        ],
        "x-access-max-limit": 20,
        "x-access-high-value": false,
        "operationId": "api_public_v1_fuel_prices",
        "summary": "Precios de carburante en estaciones de servicio",
        "description": "Última lectura oficial de precios por estación y carburante, con la fecha de captura publicada por el Ministerio.\n\nNaturaleza del dato: LIVE. Fuente: Ministerio para la Transición Ecológica (precios de carburantes). Refresco recomendado: 3600s.\nPágina canónica: https://vamosalicante.com/gasolineras-alicante\n\nAcceso: clase inventory. Máximo 20 elementos por consulta (por defecto 8). Acota la consulta con: q, fuel; sin acotación la respuesta se resume a 3 elementos. Pedir más de 20 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "fuel",
            "in": "query",
            "required": false,
            "description": "Filtra por carburante.",
            "schema": {
              "type": "string",
              "example": "gasolina95"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda por rótulo o municipio.",
            "schema": {
              "type": "string",
              "example": "Repsol"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 8. Máximo 20 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 8
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/flights": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "city",
          "type",
          "date"
        ],
        "x-access-max-limit": 40,
        "x-access-high-value": false,
        "operationId": "api_public_v1_flights",
        "summary": "Vuelos del aeropuerto Alicante-Elche (ALC)",
        "description": "Salidas y llegadas con hora programada, hora estimada, estado, compañía, terminal y puerta según la publicación de Aena.\n\nNaturaleza del dato: LIVE. Fuente: Aena. Refresco recomendado: 120s.\nPágina canónica: https://vamosalicante.com/air-travel\n\nAcceso: clase inventory. Máximo 40 elementos por consulta (por defecto 15). Acota la consulta con: city, type, date; sin acotación la respuesta se resume a 8 elementos. Pedir más de 40 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "departures | arrivals | all (por defecto all).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "city",
            "in": "query",
            "required": false,
            "description": "Búsqueda por ciudad de origen o destino.",
            "schema": {
              "type": "string",
              "example": "Madrid"
            }
          },
          {
            "name": "date",
            "in": "query",
            "required": false,
            "description": "Fecha YYYY-MM-DD en hora de Madrid; por defecto hoy.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 15. Máximo 40 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 40,
              "default": 15
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/health/pharmacies": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q",
          "on_duty"
        ],
        "x-access-max-limit": 20,
        "x-access-high-value": false,
        "operationId": "api_public_v1_health_pharmacies",
        "summary": "Farmacias y farmacias de guardia",
        "description": "Farmacias con dirección, teléfono, horario y marca de guardia o 24 horas cuando la fuente la publica.\n\nNaturaleza del dato: LIVE. Fuente: Colegio Oficial de Farmacéuticos de Alicante. Refresco recomendado: 900s.\nPágina canónica: https://vamosalicante.com/medical-care\n\nAcceso: clase inventory. Máximo 20 elementos por consulta (por defecto 8). Acota la consulta con: q, on_duty; sin acotación la respuesta se resume a 3 elementos. Pedir más de 20 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "on_duty",
            "in": "query",
            "required": false,
            "description": "true para devolver solo las de guardia.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda por nombre o dirección.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 8. Máximo 20 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 8
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/health/centers": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q"
        ],
        "x-access-max-limit": 20,
        "x-access-high-value": false,
        "operationId": "api_public_v1_health_centers",
        "summary": "Hospitales y centros sanitarios",
        "description": "Hospitales, urgencias y centros de salud con especialidades, teléfono, horario, departamento de salud y coordenadas.\n\nNaturaleza del dato: CATALOG. Fuente: Conselleria de Sanitat (Generalitat Valenciana). Refresco recomendado: 86400s.\nPágina canónica: https://vamosalicante.com/medical-care\n\nAcceso: clase inventory. Máximo 20 elementos por consulta (por defecto 6). Acota la consulta con: q; sin acotación la respuesta se resume a 3 elementos. Pedir más de 20 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda por nombre, especialidad o municipio.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 6. Máximo 20 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 6
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/events": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "category",
          "from",
          "q"
        ],
        "x-access-max-limit": 20,
        "x-access-high-value": false,
        "operationId": "api_public_v1_events",
        "summary": "Conciertos, teatro y eventos",
        "description": "Eventos activos con categoría, recinto, fecha y hora de inicio, precios y URL de entradas cuando existe.\n\nNaturaleza del dato: CATALOG. Fuente: Recintos y promotores locales. Refresco recomendado: 1800s.\nPágina canónica: https://vamosalicante.com/entertainment\n\nAcceso: clase inventory. Máximo 20 elementos por consulta (por defecto 8). Acota la consulta con: category, from, q; sin acotación la respuesta se resume a 3 elementos. Pedir más de 20 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Filtra por categoría.",
            "schema": {
              "type": "string",
              "example": "concierto"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Fecha mínima YYYY-MM-DD; por defecto hoy.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda por título, artista o recinto.",
            "schema": {
              "type": "string",
              "example": "teatro"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 8. Máximo 20 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 8
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/cinema/showtimes": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "cinema",
          "film"
        ],
        "x-access-max-limit": 25,
        "x-access-high-value": false,
        "operationId": "api_public_v1_cinema_showtimes",
        "summary": "Cartelera de cines de Alicante",
        "description": "Sesiones futuras con película, cine, hora de inicio, sala, versión, formato y precio cuando el cine lo publica.\n\nNaturaleza del dato: CALCULATED. Fuente: Cines de Alicante. Refresco recomendado: 1800s.\nPágina canónica: https://vamosalicante.com/entertainment\n\nAcceso: clase inventory. Máximo 25 elementos por consulta (por defecto 10). Acota la consulta con: cinema, film; sin acotación la respuesta se resume a 3 elementos. Pedir más de 25 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "cinema",
            "in": "query",
            "required": false,
            "description": "Slug del cine.",
            "schema": {
              "type": "string",
              "example": "yelmo-vialia"
            }
          },
          {
            "name": "film",
            "in": "query",
            "required": false,
            "description": "Búsqueda por título de película.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 10. Máximo 25 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/tourism/places": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q",
          "category"
        ],
        "x-access-max-limit": 20,
        "x-access-high-value": false,
        "operationId": "api_public_v1_tourism_places",
        "summary": "Qué hacer: lugares y visitas",
        "description": "Lugares publicados de la guía del viajero (monumentos, museos, miradores, barrios) con dirección, coordenadas, horario y ficha canónica.\n\nNaturaleza del dato: CATALOG. Fuente: VamosAlicante. Refresco recomendado: 86400s.\nPágina canónica: https://vamosalicante.com/tourists\n\nAcceso: clase inventory. Máximo 20 elementos por consulta (por defecto 6). Acota la consulta con: q, category; sin acotación la respuesta se resume a 3 elementos. Pedir más de 20 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda por nombre.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Filtra por categoría.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 6. Máximo 20 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 6
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/taxi/stops": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q"
        ],
        "x-access-max-limit": 20,
        "x-access-high-value": false,
        "operationId": "api_public_v1_taxi_stops",
        "summary": "Paradas de taxi",
        "description": "Paradas de taxi de Alicante con nombre, dirección y coordenadas.\n\nNaturaleza del dato: CATALOG. Fuente: Ayuntamiento de Alicante. Refresco recomendado: 86400s.\nPágina canónica: https://vamosalicante.com/cab-service\n\nAcceso: clase inventory. Máximo 20 elementos por consulta (por defecto 6). Acota la consulta con: q; sin acotación la respuesta se resume a 3 elementos. Pedir más de 20 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda por nombre o dirección.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 6. Máximo 20 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 6
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/taxi/tariffs": {
      "get": {
        "x-access-class": "bounded",
        "x-access-bounding-params": [],
        "x-access-max-limit": 20,
        "x-access-high-value": false,
        "operationId": "api_public_v1_taxi_tariffs",
        "summary": "Tarifas oficiales de taxi",
        "description": "Tarifas vigentes con bajada de bandera, precio por kilómetro, espera, mínimos, franjas horarias y suplementos.\n\nNaturaleza del dato: CATALOG. Fuente: Ayuntamiento de Alicante (ordenanza del taxi). Refresco recomendado: 86400s.\nPágina canónica: https://vamosalicante.com/cab-service\n\nAcceso: clase bounded. Máximo 20 elementos por consulta (por defecto 20). Pedir más de 20 devuelve 403 scope_required.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/bus/c6": {
      "get": {
        "x-access-class": "bounded",
        "x-access-bounding-params": [],
        "x-access-max-limit": 40,
        "x-access-high-value": false,
        "operationId": "api_public_v1_bus_c6",
        "summary": "Autobús C6 al aeropuerto: estado y próximas salidas",
        "description": "Estado calculado de la línea C6 (aeropuerto): buses en circulación, próximas salidas por terminal y llegadas estimadas por parada.\n\nNaturaleza del dato: CALCULATED. Fuente: Vectalia / Alicante se mueve (horario oficial C6). Refresco recomendado: 30s.\nPágina canónica: https://vamosalicante.com/autobus-c6-aeropuerto-alicante\n\nAcceso: clase bounded. Máximo 40 elementos por consulta (por defecto 40). Pedir más de 40 devuelve 403 scope_required.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/luggage-storage": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "q"
        ],
        "x-access-max-limit": 20,
        "x-access-high-value": false,
        "operationId": "api_public_v1_luggage_storage",
        "summary": "Consignas de equipaje",
        "description": "Consignas con dirección, coordenadas, horario publicado, precio desde y condiciones cuando el proveedor las publica.\n\nNaturaleza del dato: CATALOG. Fuente: Operadores de consigna. Refresco recomendado: 3600s.\nPágina canónica: https://vamosalicante.com/consignas-equipaje-alicante\n\nAcceso: clase inventory. Máximo 20 elementos por consulta (por defecto 6). Acota la consulta con: q; sin acotación la respuesta se resume a 3 elementos. Pedir más de 20 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda por nombre o marca de la consigna.",
            "schema": {
              "type": "string",
              "example": "bounce"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 6. Máximo 20 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 6
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/favorite-stop": {
      "get": {
        "x-access-class": "entity",
        "x-access-bounding-params": [
          "stop"
        ],
        "x-access-max-limit": 40,
        "x-access-high-value": false,
        "operationId": "api_public_v1_favorite_stop",
        "summary": "Ficha de una parada de bus con sus llegadas",
        "description": "Devuelve la parada (nombre, coordenadas, líneas) junto con sus próximas llegadas. Es el endpoint equivalente a la pantalla «Mi parada favorita».\n\nNaturaleza del dato: LIVE. Fuente: Vectalia / Alicante se mueve. Refresco recomendado: 30s.\nPágina canónica: https://vamosalicante.com/preferred-bus-stop\n\nAcceso: clase entity. Máximo 40 elementos por consulta (por defecto 20). Acota la consulta con: stop; sin acotación la respuesta se resume a 20 elementos. Pedir más de 40 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "stop",
            "in": "query",
            "required": true,
            "description": "Código oficial de la parada.",
            "schema": {
              "type": "string",
              "example": "1010"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/maps3d/layers": {
      "get": {
        "x-access-class": "inventory",
        "x-access-bounding-params": [
          "layer"
        ],
        "x-access-max-limit": 60,
        "x-access-high-value": true,
        "operationId": "api_public_v1_maps3d_layers",
        "summary": "Capas del mapa 3D de Alicante",
        "description": "Resumen de las capas del mapa interactivo (paradas, parkings, playas, salud, locales) con el número de puntos de cada una y su ficha canónica.\n\nNaturaleza del dato: CATALOG. Fuente: VamosAlicante. Refresco recomendado: 3600s.\nPágina canónica: https://vamosalicante.com/virtual-map\n\nAcceso: clase inventory (recurso de alto valor). Máximo 60 elementos por consulta (por defecto 25). Acota la consulta con: layer; sin acotación la respuesta se resume a 6 elementos. Pedir más de 60 devuelve 403 scope_required.",
        "parameters": [
          {
            "name": "layer",
            "in": "query",
            "required": false,
            "description": "Devuelve los puntos de una sola capa en lugar del resumen.",
            "schema": {
              "type": "string",
              "example": "parkings"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por respuesta. Por defecto 25. Máximo 60 elementos. Pedir más devuelve 403 scope_required.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 60,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta con el sobre estándar de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o consulta sin acotar (bounding_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Volumen desproporcionado (scope_required) o patrón acumulado de extracción masiva (bulk_extraction_blocked).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota de la ventana agotada (quota_exceeded). Respeta la cabecera Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Timestamp": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "epochMs": {
            "type": "integer"
          },
          "madrid": {
            "type": "string",
            "description": "ISO 8601 con offset de Europe/Madrid."
          },
          "utc": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Envelope": {
        "type": "object",
        "required": [
          "api",
          "version",
          "endpoint",
          "ok",
          "generatedAt",
          "dataType",
          "source",
          "data"
        ],
        "properties": {
          "api": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "endpoint": {
            "type": "string"
          },
          "ok": {
            "type": "boolean"
          },
          "generatedAt": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "timeZone": {
            "type": "string",
            "const": "Europe/Madrid"
          },
          "dataType": {
            "type": "string",
            "enum": [
              "LIVE",
              "CALCULATED",
              "CATALOG",
              "STATIC",
              "STALE"
            ],
            "description": "Naturaleza del dato entregado. STALE lo aplica la propia API cuando una observación (LIVE) o un cálculo (CALCULATED) ha superado su validThrough: en ese caso `declaredDataType` conserva la naturaleza original y el dato NO debe presentarse como estado actual."
          },
          "declaredDataType": {
            "type": "string",
            "enum": [
              "LIVE",
              "CALCULATED",
              "CATALOG",
              "STATIC"
            ]
          },
          "freshness": {
            "type": "object",
            "properties": {
              "state": {
                "type": "string",
                "enum": [
                  "CURRENT",
                  "EXPIRING",
                  "EXPIRED",
                  "UNDATED"
                ]
              },
              "ageSeconds": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "ttlRemainingSeconds": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            }
          },
          "staleWarning": {
            "type": "string"
          },
          "observedAt": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "validThrough": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "ttlSeconds": {
            "type": "integer"
          },
          "source": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "url": {
                "type": "string"
              },
              "canonical": {
                "type": "string"
              }
            }
          },
          "attribution": {
            "type": "string"
          },
          "license": {
            "type": "string"
          },
          "notes": {
            "type": "string"
          },
          "data": {}
        }
      },
      "ApiError": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  },
  "x-endpoints-index": "https://vamosalicante.com/api/public/v1"
}