{
  "openapi": "3.1.0",
  "info": {
    "title": "Vyrsea API",
    "version": "0.8.3",
    "description": "API del mismo origen. Passkeys y sesión HttpOnly. Este documento publica operaciones de lectura; no concede acceso a los datos del cliente."
  },
  "servers": [
    {
      "url": "https://vyrsea.com"
    }
  ],
  "externalDocs": {
    "description": "Ayuda y alcance actual de Vyrsea",
    "url": "https://vyrsea.com/ayuda"
  },
  "paths": {
    "/api/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Estado básico del proceso",
        "responses": {
          "200": {
            "description": "Conexión PostgreSQL cuando está configurada; no verifica Cron ni entregas"
          }
        }
      }
    },
    "/api/monitors/{id}/spain": {
      "get": {
        "operationId": "getSpanishISPEvidence",
        "summary": "IP resuelta desde cada operador, contraste con su propio listado e histórico por rangos con vínculos temporales, sin iniciar mediciones",
        "security": [
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "historyIP",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "IP vinculada al monitor del espacio actual, o all para todas."
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365,
              "default": 1
            },
            "description": "Limitado por la retención del plan."
          },
          {
            "name": "before",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Cursor nextBefore de la respuesta anterior."
          }
        ],
        "responses": {
          "200": {
            "description": "Muestras, IP, vínculos y hasta 400 cambios por IP/operador. Sin datos no implica desbloqueo."
          },
          "400": {
            "description": "IP no observada, cursor o periodo no permitido"
          },
          "401": {
            "description": "Sesión necesaria"
          },
          "404": {
            "description": "Monitor ajeno o inexistente"
          }
        }
      }
    },
    "/api/monitors/{id}/availability": {
      "get": {
        "operationId": "getMonitorAvailability",
        "summary": "Disponibilidad propia con 24 horas y periodo elegido, resolución horaria y retención del plan",
        "security": [
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365
            },
            "description": "O bien from y to: timestamps Unix de horas completas dentro de la retención."
          }
        ],
        "responses": {
          "200": {
            "description": "Agregados del periodo y de últimas 24 h"
          },
          "401": {
            "description": "Sesión necesaria"
          },
          "403": {
            "description": "Fuera de retención"
          },
          "404": {
            "description": "Monitor ajeno o inexistente"
          }
        }
      }
    },
    "/api/public/status/{id}": {
      "get": {
        "operationId": "getPublicServiceStatus",
        "summary": "Nombres, estados, disponibilidad, componentes agrupados y comunicados publicados expresamente",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Datos públicos, caché hasta 60 s"
          },
          "404": {
            "description": "Página no publicada o cuenta sin acceso"
          }
        }
      }
    },
    "/api/plans": {
      "get": {
        "operationId": "getPublicPlans",
        "summary": "Catálogo público; los planes no disponibles son propuestas",
        "responses": {
          "200": {
            "description": "Catálogo"
          }
        }
      }
    },
    "/api/dashboard": {
      "get": {
        "operationId": "getDashboard",
        "summary": "Dominios, cuotas, mantenimientos e histórico de la sesión aprobada; avanzado solo Pro / Agencia y Business. La lectura no renueva actividad.",
        "security": [
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365,
              "default": 1
            },
            "description": "No puede superar los días de histórico de la cuenta."
          },
          {
            "name": "view",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "advanced"
              ]
            },
            "description": "Exige un plan con plataforma avanzada."
          }
        ],
        "responses": {
          "200": {
            "description": "Datos del usuario; indica los proveedores realmente activos"
          },
          "401": {
            "description": "Sesión necesaria"
          },
          "403": {
            "description": "Cuenta pendiente o suspendida, histórico o plataforma fuera del plan"
          }
        }
      }
    },
    "/api/monitors/{id}/history": {
      "get": {
        "operationId": "getMonitorHistory",
        "summary": "Historial propio limitado por plan, bloques de 100",
        "security": [
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "result",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "failed",
                "ok"
              ],
              "default": "all"
            },
            "description": "Filtra antes de paginar."
          },
          {
            "name": "before",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "description": "Timestamp Unix exclusivo para consultar el bloque anterior."
          }
        ],
        "responses": {
          "200": {
            "description": "Hasta 100 comprobaciones dentro de la retención del plan"
          },
          "401": {
            "description": "Sesión necesaria"
          },
          "404": {
            "description": "No existe o no pertenece al usuario"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "session": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Host-pulso_session",
        "description": "Cookie HttpOnly de la sesión HTTPS existente. No expongas su valor a agentes ni a terceros."
      }
    }
  }
}
