{
  "openapi": "3.1.0",
  "info": {
    "title": "Siteline API",
    "version": "1.0.0",
    "description": "AI agent readiness scanner for public websites. Evaluates Signal, Navigate, Absorb, and Perform (SNAP) across any URL. Rate limiting conforms to the Graceful Boundaries specification at Level 4.",
    "contact": {
      "name": "PAICE",
      "url": "https://paice.work/"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://siteline.to/terms/"
    }
  },
  "servers": [
    {
      "url": "https://siteline.to",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Full LLM context — everything an agent needs to use this API",
    "url": "https://siteline.to/llms-full.txt"
  },
  "paths": {
    "/api/scan": {
      "get": {
        "operationId": "scanUrl",
        "summary": "Scan a public URL for agent readiness",
        "description": "Evaluates a public website across the SNAP rubric and returns a grade (A-F), score (0-100), per-pillar breakdown, findings, and remediation tier. Results are cached for 24 hours per domain.",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "schema": { "type": "string" },
            "description": "Public URL or domain to scan.",
            "example": "https://example.com"
          },
          {
            "name": "debug",
            "in": "query",
            "required": false,
            "schema": { "type": "string", "enum": ["1", "true"] },
            "description": "Include fetch timing and debug details in the response."
          }
        ],
        "responses": {
          "200": {
            "description": "Scan result",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ScanResult" }
              }
            }
          },
          "400": {
            "description": "Invalid or missing URL",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (10 per IP per hour)",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/RateLimitError" }
              }
            }
          }
        }
      }
    },
    "/api/result": {
      "get": {
        "operationId": "getResult",
        "summary": "Look up a stored scan result",
        "description": "Retrieves a previously stored scan result by result ID or domain. Homepage result IDs follow domain-slug-YYYYMMDD; path/query-scoped result IDs follow domain-slug-path-scope-hash-YYYYMMDD.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": { "type": "string" },
            "description": "Result ID (e.g. example-com-20260322 or example-com-pricing-a1b2c3d4-20260322) or domain (e.g. example.com)."
          }
        ],
        "responses": {
          "200": {
            "description": "Stored scan result",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ScanResult" }
              }
            }
          },
          "404": {
            "description": "No result found"
          },
          "429": {
            "description": "Rate limit exceeded (60 per IP per minute)"
          }
        }
      }
    },
    "/api/limits": {
      "get": {
        "operationId": "getLimits",
        "summary": "Rate limit and policy discovery",
        "description": "Returns all enforced rate limits, SSRF protection policy, and endpoint links. Conforms to Graceful Boundaries Level 4."
      }
    },
    "/api/og": {
      "get": {
        "operationId": "getOgImage",
        "summary": "Generate OG grade card image",
        "description": "Generates a 1200x630 branded grade card image for social sharing.",
        "parameters": [
          {
            "name": "domain",
            "in": "query",
            "schema": { "type": "string" }
          },
          {
            "name": "grade",
            "in": "query",
            "schema": { "type": "string", "enum": ["A", "B", "C", "D", "F"] }
          },
          {
            "name": "score",
            "in": "query",
            "schema": { "type": "integer", "minimum": 0, "maximum": 100 }
          },
          {
            "name": "resultId",
            "in": "query",
            "schema": { "type": "string" },
            "description": "Alternative: auto-lookup from stored result."
          }
        ]
      }
    },
    "/api/email-capture": {
      "post": {
        "operationId": "captureEmail",
        "summary": "Submit email for scan report delivery",
        "description": "Captures an email address and optional scan result snapshot for report delivery."
      }
    }
  },
  "components": {
    "schemas": {
      "ScanResult": {
        "type": "object",
        "required": ["ok", "normalizedURL", "domain", "scannedAt", "grade", "score", "label"],
        "properties": {
          "ok": { "type": "boolean", "const": true },
          "normalizedURL": { "type": "string", "format": "uri" },
          "domain": { "type": "string" },
          "scannedAt": { "type": "string", "format": "date-time" },
          "resultId": { "type": "string", "description": "Stable identifier. Homepage scans use domain-slug-YYYYMMDD; path/query-scoped scans use domain-slug-path-scope-hash-YYYYMMDD." },
          "grade": { "type": "string", "enum": ["A", "B", "C", "D", "F"] },
          "score": { "type": "integer", "minimum": 0, "maximum": 100 },
          "label": {
            "type": "string",
            "enum": ["Agent-Usable", "Mostly Usable", "Needs Clearer Paths", "Hard for Agents", "Agent-Blocked or Unusable"]
          },
          "likelyFailureMode": { "type": "string" },
          "pillars": {
            "type": "object",
            "properties": {
              "access": { "type": "integer", "minimum": 0, "maximum": 100 },
              "navigability": { "type": "integer", "minimum": 0, "maximum": 100 },
              "readability": { "type": "integer", "minimum": 0, "maximum": 100 },
              "actionHandoff": { "type": "integer", "minimum": 0, "maximum": 100 }
            }
          },
          "findings": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": { "type": "string" },
                "status": { "type": "string", "enum": ["pass", "warn", "fail", "blocked", "not_applicable"] },
                "title": { "type": "string" },
                "reason": { "type": "string" },
                "impact": { "type": "string" },
                "remediation": { "type": ["string", "null"] },
                "group": { "type": "string", "enum": ["blockers", "coreIssues", "opportunities"] },
                "confidence": { "type": "string", "enum": ["high", "medium", "low"] }
              }
            }
          },
          "agenticEnablement": {
            "type": "object",
            "description": "Layer 2: qualitative assessment of 8 agentic purposes. Related resources are grouped by purpose (best score counts). Level caps the maximum grade.",
            "properties": {
              "level": { "type": "integer", "minimum": 0, "maximum": 4, "description": "0-1=cap D, 2=cap C, 3=cap B, 4=cap A" },
              "totalQuality": { "type": "integer", "description": "Sum of per-purpose quality scores (0/1/2 each, max 16)" },
              "maxQuality": { "type": "integer", "description": "Maximum possible quality score (16)" },
              "purposes": { "type": "object", "description": "Per-purpose quality scores (best resource per purpose)", "additionalProperties": { "type": "integer" } },
              "details": { "type": "object", "description": "Per-resource quality scores (individual breakdown)", "additionalProperties": { "type": "integer" } }
            }
          },
          "summary": {
            "type": "object",
            "properties": {
              "headline": { "type": "string" },
              "detail": { "type": "string" }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string" },
          "detail": { "type": "string" },
          "requestId": { "type": "string" }
        }
      },
      "RateLimitError": {
        "type": "object",
        "properties": {
          "error": { "type": "string" },
          "detail": { "type": "string" },
          "limit": { "type": "string" },
          "retryAfterSeconds": { "type": "integer" },
          "why": { "type": "string" },
          "humanUrl": { "type": "string", "format": "uri" },
          "requestId": { "type": "string" }
        }
      }
    }
  }
}
