{
  "openapi": "3.1.0",
  "info": {
    "title": "Interfaces públicas de aimoova.com para agentes",
    "version": "1.1.0",
    "description": "API pública de contenido, MCP, markdown y salud. Todas las interfaces agénticas son públicas y de solo lectura; no hay endpoints de escritura, claves, portal SaaS ni acceso al CMS interno.",
    "contact": {
      "name": "Aimoova",
      "url": "https://www.aimoova.com/contacto",
      "email": "info@aimoova.com"
    },
    "x-deprecation-policy": {
      "minimumNoticeDays": 180,
      "description": "Aimoova avisará con un mínimo de 180 días antes de retirar una versión estable. Esta política no anuncia una retirada."
    }
  },
  "servers": [
    {
      "url": "https://www.aimoova.com"
    }
  ],
  "tags": [
    {
      "name": "Public Content API",
      "description": "Contenido ya publicado, sin datos internos de Payload."
    },
    {
      "name": "MCP",
      "description": "Model Context Protocol de solo lectura."
    },
    {
      "name": "Markdown",
      "description": "Representación markdown de las páginas públicas."
    },
    {
      "name": "Operations",
      "description": "Estado del servicio."
    }
  ],
  "paths": {
    "/api/v1": {
      "get": {
        "tags": [
          "Public Content API"
        ],
        "operationId": "describePublicContentApi",
        "summary": "Describe la API pública de contenido v1",
        "description": "Sin autenticación. Coste: 1 unidad de trabajo.",
        "x-work-unit-cost": 1,
        "responses": {
          "200": {
            "description": "Contrato raíz exacto.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiRoot"
                }
              }
            }
          },
          "400": {
            "description": "La raíz recibió parámetros o un cuerpo no permitidos.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "invalidRequest": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "Invalid request"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Cuota ponderada agotada.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "rateLimited": {
                    "value": {
                      "error": {
                        "code": "rate_limited",
                        "message": "Rate limit exceeded"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Error interno estable.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "internalError": {
                    "value": {
                      "error": {
                        "code": "internal_error",
                        "message": "Internal error"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/pages": {
      "get": {
        "tags": [
          "Public Content API"
        ],
        "operationId": "listPublicPages",
        "summary": "Lista rutas publicadas con cursor",
        "description": "Une los dos sitemaps de Aimoova de forma atómica. Limit por defecto 25, mínimo 1 y máximo 50. Coste: 6 unidades de trabajo.",
        "x-work-unit-cost": 6,
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/PageKind"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 25
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Cursor base64url opaco. Máximo 512 bytes codificado y 256 bytes decodificado.",
            "schema": {
              "type": "string",
              "maxLength": 512
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Página estable del snapshot.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageList"
                }
              }
            }
          },
          "400": {
            "description": "Petición o cursor inválido.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "invalidRequest": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "Invalid request"
                      }
                    }
                  },
                  "invalidCursor": {
                    "value": {
                      "error": {
                        "code": "invalid_cursor",
                        "message": "Invalid cursor"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "El snapshot del cursor emitido ya ha expirado.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "cursorExpired": {
                    "value": {
                      "error": {
                        "code": "cursor_expired",
                        "message": "Cursor expired"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Cuota ponderada agotada.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "rateLimited": {
                    "value": {
                      "error": {
                        "code": "rate_limited",
                        "message": "Rate limit exceeded"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Error interno estable.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "internalError": {
                    "value": {
                      "error": {
                        "code": "internal_error",
                        "message": "Internal error"
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Capacidad ocupada o catálogo temporalmente no disponible.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "busy": {
                    "value": {
                      "error": {
                        "code": "busy",
                        "message": "Service busy"
                      }
                    }
                  },
                  "catalogUnavailable": {
                    "value": {
                      "error": {
                        "code": "catalog_unavailable",
                        "message": "Catalog unavailable"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/pages/content": {
      "get": {
        "tags": [
          "Public Content API"
        ],
        "operationId": "getPublicPageContent",
        "summary": "Lee una ruta catalogada como markdown",
        "description": "Solo acepta una ruta canónica que exista en el catálogo vigente. No acepta URLs, queries, traversal ni zonas reservadas. Coste: 4 unidades de trabajo.",
        "x-work-unit-cost": 4,
        "parameters": [
          {
            "name": "path",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^/(?:$|[^?#]*)$",
              "example": "/precios"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Representación del HTML público, nunca un documento de Payload.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageContent"
                }
              }
            }
          },
          "400": {
            "description": "Petición o ruta inválida.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "invalidRequest": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "Invalid request"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "La ruta catalogada no existe.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "notFound": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Not found"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Cuota ponderada agotada.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "rateLimited": {
                    "value": {
                      "error": {
                        "code": "rate_limited",
                        "message": "Rate limit exceeded"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Error interno estable.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "internalError": {
                    "value": {
                      "error": {
                        "code": "internal_error",
                        "message": "Internal error"
                      }
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "Fallo del contenido público de origen o contenido superior a 512 KiB.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "upstreamError": {
                    "value": {
                      "error": {
                        "code": "upstream_error",
                        "message": "Upstream error"
                      }
                    }
                  },
                  "contentTooLarge": {
                    "value": {
                      "error": {
                        "code": "content_too_large",
                        "message": "Content too large"
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Capacidad ocupada o catálogo temporalmente no disponible.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "busy": {
                    "value": {
                      "error": {
                        "code": "busy",
                        "message": "Service busy"
                      }
                    }
                  },
                  "catalogUnavailable": {
                    "value": {
                      "error": {
                        "code": "catalog_unavailable",
                        "message": "Catalog unavailable"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "tags": [
          "MCP"
        ],
        "operationId": "mcp",
        "summary": "Servidor MCP Streamable HTTP",
        "description": "JSON-RPC 2.0, protocolo 2025-06-18. Máximo 16 mensajes por lote, 20 unidades por petición, body máximo 65.536 bytes y deadline absoluto de 5 segundos. La cuota compartida es 60 unidades por cliente cada 300 segundos.",
        "x-max-body-bytes": 65536,
        "x-max-request-work-units": 20,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/JsonRpcMessage"
                  },
                  {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/JsonRpcMessage"
                    },
                    "minItems": 1,
                    "maxItems": 16
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Objeto JSON-RPC o array para un lote.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "MCP-Protocol-Version": {
                "$ref": "#/components/headers/McpProtocolVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/JsonRpcResponse"
                    },
                    {
                      "type": "array",
                      "minItems": 1,
                      "maxItems": 16,
                      "items": {
                        "$ref": "#/components/schemas/JsonRpcResponse"
                      }
                    }
                  ]
                }
              }
            }
          },
          "202": {
            "description": "Lote formado solo por notificaciones; sin cuerpo.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "MCP-Protocol-Version": {
                "$ref": "#/components/headers/McpProtocolVersion"
              }
            }
          },
          "400": {
            "description": "JSON-RPC inválido, lote vacío/excesivo, protocolo no soportado o coste superior a 20.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcError"
                }
              }
            }
          },
          "405": {
            "description": "GET/SSE no está disponible."
          },
          "408": {
            "description": "Deadline absoluto del body agotado.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcError"
                }
              }
            }
          },
          "413": {
            "description": "Body superior a 65.536 bytes.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcError"
                }
              }
            }
          },
          "429": {
            "description": "Cuota ponderada agotada; ninguna operación del lote se ejecuta.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcError"
                }
              }
            }
          },
          "500": {
            "description": "Error interno JSON-RPC.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcError"
                }
              }
            }
          },
          "503": {
            "description": "Los dos permisos de trabajo costoso están ocupados.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcError"
                }
              }
            }
          }
        }
      }
    },
    "/healthz": {
      "get": {
        "tags": [
          "Operations"
        ],
        "operationId": "health",
        "summary": "Estado del servicio",
        "responses": {
          "200": {
            "description": "Servicio listo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthStatus"
                }
              }
            }
          },
          "503": {
            "description": "Servicio no disponible o dependencia no preparada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthStatus"
                }
              }
            }
          }
        }
      }
    },
    "/": {
      "get": {
        "tags": [
          "Markdown"
        ],
        "operationId": "readHomepageMarkdown",
        "summary": "Portada en HTML o markdown",
        "description": "Usa Accept: text/markdown, ?mode=agent o /index.md. Markdown cuesta 4 unidades.",
        "parameters": [
          {
            "name": "Accept",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "example": "text/markdown"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "HTML normal o markdown.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              },
              "text/markdown": {
                "schema": {
                  "type": "string",
                  "maxLength": 524288
                }
              }
            }
          },
          "429": {
            "description": "Cuota ponderada agotada.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "rateLimited": {
                    "value": {
                      "error": {
                        "code": "rate_limited",
                        "message": "Rate limit exceeded"
                      }
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "Fallo del contenido público de origen o contenido superior a 512 KiB.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "upstreamError": {
                    "value": {
                      "error": {
                        "code": "upstream_error",
                        "message": "Upstream error"
                      }
                    }
                  },
                  "contentTooLarge": {
                    "value": {
                      "error": {
                        "code": "content_too_large",
                        "message": "Content too large"
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Capacidad ocupada o catálogo temporalmente no disponible.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "busy": {
                    "value": {
                      "error": {
                        "code": "busy",
                        "message": "Service busy"
                      }
                    }
                  },
                  "catalogUnavailable": {
                    "value": {
                      "error": {
                        "code": "catalog_unavailable",
                        "message": "Catalog unavailable"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/{ruta}": {
      "get": {
        "tags": [
          "Markdown"
        ],
        "operationId": "readPageMarkdown",
        "summary": "Página pública en HTML o markdown",
        "description": "Accept: text/markdown, sufijo .md o mode=agent seleccionan markdown. Solo mode y parámetros de tracking se descartan; cualquier query funcional conserva HTML.",
        "parameters": [
          {
            "name": "ruta",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "precios"
          },
          {
            "name": "Accept",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "example": "text/markdown"
            }
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "const": "agent"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "HTML normal o markdown público.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              },
              "text/markdown": {
                "schema": {
                  "type": "string",
                  "maxLength": 524288
                }
              }
            }
          },
          "404": {
            "description": "La página no existe."
          },
          "429": {
            "description": "Cuota ponderada agotada.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "rateLimited": {
                    "value": {
                      "error": {
                        "code": "rate_limited",
                        "message": "Rate limit exceeded"
                      }
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "Fallo del contenido público de origen o contenido superior a 512 KiB.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "upstreamError": {
                    "value": {
                      "error": {
                        "code": "upstream_error",
                        "message": "Upstream error"
                      }
                    }
                  },
                  "contentTooLarge": {
                    "value": {
                      "error": {
                        "code": "content_too_large",
                        "message": "Content too large"
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Capacidad ocupada o catálogo temporalmente no disponible.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "examples": {
                  "busy": {
                    "value": {
                      "error": {
                        "code": "busy",
                        "message": "Service busy"
                      }
                    }
                  },
                  "catalogUnavailable": {
                    "value": {
                      "error": {
                        "code": "catalog_unavailable",
                        "message": "Catalog unavailable"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "headers": {
      "RateLimitPolicy": {
        "description": "Política fija de cuota ponderada.",
        "schema": {
          "type": "string",
          "const": "\"agent\";q=60;w=300"
        }
      },
      "RateLimit": {
        "description": "Unidades restantes y segundos hasta reset.",
        "schema": {
          "type": "string",
          "pattern": "^\"agent\";r=\\d+;t=\\d+$"
        }
      },
      "RateLimitLimit": {
        "description": "Límite de unidades por ventana.",
        "schema": {
          "type": "integer",
          "const": 60
        }
      },
      "RateLimitRemaining": {
        "description": "Unidades enteras restantes tras el cobro.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "maximum": 60
        }
      },
      "RateLimitReset": {
        "description": "Segundos enteros hasta reset, redondeados hacia arriba.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300
        }
      },
      "RetryAfter": {
        "description": "Segundos recomendados antes de reintentar.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "McpProtocolVersion": {
        "description": "Versión MCP soportada.",
        "schema": {
          "type": "string",
          "const": "2025-06-18"
        }
      }
    },
    "schemas": {
      "PageKind": {
        "type": "string",
        "enum": [
          "page",
          "blog",
          "article",
          "category",
          "author"
        ]
      },
      "ApiRoot": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "version",
          "readOnly",
          "authentication",
          "documentation",
          "openapi",
          "resources",
          "rateLimit",
          "deprecationPolicy"
        ],
        "properties": {
          "name": {
            "type": "string",
            "const": "Aimoova Public Content API"
          },
          "version": {
            "type": "string",
            "const": "v1"
          },
          "readOnly": {
            "type": "boolean",
            "const": true
          },
          "authentication": {
            "type": "string",
            "const": "none"
          },
          "documentation": {
            "type": "string",
            "format": "uri",
            "const": "https://www.aimoova.com/desarrolladores"
          },
          "openapi": {
            "type": "string",
            "format": "uri",
            "const": "https://www.aimoova.com/.well-known/openapi.json"
          },
          "resources": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "pages",
              "content"
            ],
            "properties": {
              "pages": {
                "type": "string",
                "const": "https://www.aimoova.com/api/v1/pages"
              },
              "content": {
                "type": "string",
                "const": "https://www.aimoova.com/api/v1/pages/content{?path}"
              }
            }
          },
          "rateLimit": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "unit",
              "limit",
              "windowSeconds"
            ],
            "properties": {
              "unit": {
                "type": "string",
                "const": "work-unit"
              },
              "limit": {
                "type": "integer",
                "const": 60
              },
              "windowSeconds": {
                "type": "integer",
                "const": 300
              }
            }
          },
          "deprecationPolicy": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "minimumNoticeDays"
            ],
            "properties": {
              "minimumNoticeDays": {
                "type": "integer",
                "const": 180
              }
            }
          }
        }
      },
      "PageSummary": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "path",
          "url",
          "contentUrl",
          "kind",
          "lastModified"
        ],
        "properties": {
          "path": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "contentUrl": {
            "type": "string",
            "format": "uri"
          },
          "kind": {
            "$ref": "#/components/schemas/PageKind"
          },
          "lastModified": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "PageList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "page",
          "links"
        ],
        "properties": {
          "data": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/PageSummary"
            }
          },
          "page": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "snapshot",
              "limit",
              "hasMore",
              "nextCursor"
            ],
            "properties": {
              "snapshot": {
                "type": "string"
              },
              "limit": {
                "type": "integer",
                "minimum": 1,
                "maximum": 50
              },
              "hasMore": {
                "type": "boolean"
              },
              "nextCursor": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 512
              }
            }
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "self",
              "next"
            ],
            "properties": {
              "self": {
                "type": "string",
                "format": "uri"
              },
              "next": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uri"
              }
            }
          }
        }
      },
      "PageContent": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "links"
        ],
        "properties": {
          "data": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "path",
              "url",
              "kind",
              "lastModified",
              "title",
              "description",
              "image",
              "content"
            ],
            "properties": {
              "path": {
                "type": "string"
              },
              "url": {
                "type": "string",
                "format": "uri"
              },
              "kind": {
                "$ref": "#/components/schemas/PageKind"
              },
              "lastModified": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "title": {
                "type": "string"
              },
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "image": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uri"
              },
              "content": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "mediaType",
                  "text",
                  "estimatedTokens"
                ],
                "properties": {
                  "mediaType": {
                    "type": "string",
                    "const": "text/markdown"
                  },
                  "text": {
                    "type": "string",
                    "maxLength": 524288
                  },
                  "estimatedTokens": {
                    "type": "integer",
                    "minimum": 1
                  }
                }
              }
            }
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "collection",
              "canonical"
            ],
            "properties": {
              "collection": {
                "type": "string",
                "format": "uri"
              },
              "canonical": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "ApiError": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "invalid_request",
                  "invalid_cursor",
                  "not_found",
                  "cursor_expired",
                  "rate_limited",
                  "upstream_error",
                  "content_too_large",
                  "busy",
                  "catalog_unavailable",
                  "internal_error"
                ]
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "JsonRpcMessage": {
        "type": "object",
        "additionalProperties": true,
        "required": [
          "jsonrpc",
          "method"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "number",
              "null"
            ]
          },
          "method": {
            "type": "string",
            "enum": [
              "initialize",
              "notifications/initialized",
              "ping",
              "tools/list",
              "tools/call"
            ]
          },
          "params": {
            "type": "object"
          }
        }
      },
      "JsonRpcResponse": {
        "oneOf": [
          {
            "type": "object",
            "required": [
              "jsonrpc",
              "id",
              "result"
            ],
            "properties": {
              "jsonrpc": {
                "type": "string",
                "const": "2.0"
              },
              "id": {
                "type": [
                  "string",
                  "number",
                  "null"
                ]
              },
              "result": {}
            }
          },
          {
            "$ref": "#/components/schemas/JsonRpcError"
          }
        ]
      },
      "JsonRpcError": {
        "type": "object",
        "required": [
          "jsonrpc",
          "id",
          "error"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "number",
              "null"
            ]
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "HealthStatus": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "status",
          "checks"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "error"
            ]
          },
          "checks": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "name",
                "ok"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "ok": {
                  "type": "boolean"
                },
                "detail": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  }
}
