{
  "openapi": "3.0.3",
  "info": {
    "title": "Prolectio public read API",
    "version": "1.0.0",
    "description": "Unauthenticated read access to public Prolectio Scores, public scan reports, and the AX Index distribution. Rate limit: 30 requests per 5 minutes per IP; exceeding it returns 429 with a Retry-After header. Reference: https://prolectio.com/docs/api",
    "contact": { "url": "https://prolectio.com/security", "email": "corrections@prolectio.com" }
  },
  "servers": [{ "url": "https://prolectio.com", "description": "Production" }],
  "paths": {
    "/api/v1/score": {
      "get": {
        "operationId": "getScore",
        "summary": "Public Prolectio Score for a domain",
        "parameters": [{ "name": "domain", "in": "query", "required": true, "schema": { "type": "string" }, "example": "prolectio.com" }],
        "responses": {
          "200": {
            "description": "The domain's public score",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Score" } } }
          },
          "404": { "description": "No public score found for this domain" },
          "429": { "description": "Rate limit exceeded; retry after the Retry-After header" }
        }
      }
    },
    "/api/v1/report": {
      "get": {
        "operationId": "getReport",
        "summary": "Full public scan report for a domain",
        "parameters": [{ "name": "domain", "in": "query", "required": true, "schema": { "type": "string" }, "example": "prolectio.com" }],
        "responses": {
          "200": {
            "description": "The same data the public /s/[slug] report page renders",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Report" } } }
          },
          "404": { "description": "No public report found for this domain" },
          "429": { "description": "Rate limit exceeded; retry after the Retry-After header" }
        }
      }
    },
    "/api/v1/index": {
      "get": {
        "operationId": "getIndex",
        "summary": "AX Index distribution and any published leaders",
        "responses": {
          "200": {
            "description": "Rubric version, scan date, row count, band distribution, and published leaders",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IndexSummary" } } }
          },
          "429": { "description": "Rate limit exceeded; retry after the Retry-After header" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {},
    "schemas": {
      "Band": { "type": "string", "enum": ["green", "amber", "red"] },
      "Score": {
        "type": "object",
        "required": ["domain", "score", "band", "scannedAt", "reportUrl"],
        "properties": {
          "domain": { "type": "string" },
          "score": { "type": "integer", "minimum": 0, "maximum": 100 },
          "band": { "$ref": "#/components/schemas/Band" },
          "scannedAt": { "type": "string", "format": "date-time" },
          "reportUrl": { "type": "string", "format": "uri" }
        }
      },
      "Report": {
        "type": "object",
        "required": ["domain", "slug", "score", "band"],
        "properties": {
          "domain": { "type": "string" },
          "slug": { "type": "string" },
          "score": { "type": "integer" },
          "band": { "$ref": "#/components/schemas/Band" },
          "categories": { "type": "object", "additionalProperties": { "type": "number" } },
          "report": { "type": "object", "description": "Full stored scan report (pages, checks, provenance)" }
        }
      },
      "IndexSummary": {
        "type": "object",
        "required": ["axRubricVersion", "scannedAt", "rowCount", "distribution"],
        "properties": {
          "axRubricVersion": { "type": "string" },
          "scannedAt": { "type": "string", "format": "date-time" },
          "rowCount": { "type": "integer" },
          "leaders": { "type": "array", "items": { "type": "object" } },
          "distribution": {
            "type": "object",
            "properties": {
              "total": { "type": "integer" },
              "green": { "type": "integer" },
              "amber": { "type": "integer" },
              "red": { "type": "integer" },
              "median": { "type": "integer" }
            }
          }
        }
      }
    }
  }
}
