{
  "openapi": "3.1.0",
  "info": {
    "title": "Lexbase API — legislação portuguesa consolidada",
    "version": "1.0.0",
    "description": "Read-only HTTP API over Portuguese consolidated legislation. Every content response carries a verifiable citation (diploma + article) and its in-force status. The same corpus is exposed as an MCP server at https://api.lexbase.pt/mcp. Legal information — not legal advice.",
    "contact": { "name": "Lexbase", "url": "https://lexbase.pt" }
  },
  "servers": [{ "url": "https://api.lexbase.pt", "description": "Production" }],
  "security": [{ "bearerAuth": [] }],
  "paths": {
    "/health": {
      "get": {
        "summary": "Service health (unauthenticated)",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": { "type": "string" },
                    "index_size": { "type": "integer", "description": "number of fragments/blocos" },
                    "corpus_diplomas": { "type": "integer" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/search": {
      "get": {
        "summary": "Semantic + lexical search over consolidated legislation",
        "parameters": [
          { "name": "q", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Natural-language query in Portuguese." },
          { "name": "k", "in": "query", "schema": { "type": "integer", "default": 8 }, "description": "Number of results." },
          { "name": "tipo", "in": "query", "schema": { "type": "string" }, "description": "Filter by diploma type (Lei, Decreto-Lei, …)." },
          { "name": "tema_tag_id", "in": "query", "schema": { "type": "string" }, "description": "Filter by legal theme (see /themes)." },
          { "name": "in_force_only", "in": "query", "schema": { "type": "boolean", "default": true }, "description": "Restrict to wording currently in force." }
        ],
        "responses": {
          "200": {
            "description": "Ranked articles. Each hit carries a verifiable citation, the article text, and the matched snippet. total=0 with a note means an honest no-match.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchResponse" } } }
          },
          "401": { "description": "Missing/invalid key" },
          "429": { "description": "Quota or rate limit exceeded" }
        }
      }
    },
    "/diplomas": {
      "get": {
        "summary": "Index of every consolidated diploma (id + last-modified)",
        "parameters": [
          { "name": "offset", "in": "query", "schema": { "type": "integer", "default": 0 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 10000, "maximum": 50000 } }
        ],
        "responses": { "200": { "description": "Paginated diploma index", "content": { "application/json": { "schema": { "type": "object", "properties": { "total": { "type": "integer" }, "diplomas": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "lastmod": { "type": "string", "nullable": true } } } } } } } } } }
      }
    },
    "/diploma/{diploma_id}": {
      "get": {
        "summary": "Diploma metadata, article tree, or full consolidated text",
        "parameters": [
          { "name": "diploma_id", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "render", "in": "query", "schema": { "type": "string", "enum": ["meta", "tree", "full"], "default": "meta" } }
        ],
        "responses": { "200": { "description": "Diploma view" }, "404": { "description": "Not found" } }
      }
    },
    "/fragment/{fragment_id}": {
      "get": {
        "summary": "A single article by its fragment id, with citation and in-force dates",
        "parameters": [{ "name": "fragment_id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Article" }, "404": { "description": "Not found" } }
      }
    },
    "/fragment/{fragment_id}/amendments": {
      "get": {
        "summary": "Amendment history of an article",
        "parameters": [{ "name": "fragment_id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Amendments" }, "404": { "description": "Not found" } }
      }
    },
    "/themes": {
      "get": {
        "summary": "Legal themes, with diploma counts, usable as the search tema_tag_id filter",
        "responses": { "200": { "description": "Themes" } }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "A Lexbase API key (lx_… / leis_…) in the Authorization header. Create one at https://lexbase.pt/mcp."
      }
    },
    "schemas": {
      "SearchResponse": {
        "type": "object",
        "properties": {
          "query": { "type": "string" },
          "total": { "type": "integer" },
          "note": { "type": "string", "description": "Present when total=0 — why nothing matched." },
          "hits": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "fragment_id": { "type": "string" },
                "citation": { "type": "string", "description": "Verifiable citation: diploma + article." },
                "texto": { "type": "string" },
                "in_force": { "type": "boolean" },
                "url": { "type": "string", "description": "Permalink into lexbase.pt with the article highlighted." }
              }
            }
          }
        }
      }
    }
  }
}
