{
  "openapi": "3.1.0",
  "info": {
    "title": "Softgrama AI-readiness audit API",
    "version": "1.0.0",
    "summary": "Public, no-signup audit of how well AI engines can read a website.",
    "description": "Crawls a public website and reports 5 checks that decide whether ChatGPT, Perplexity, Claude and Google AI can read, quote and recommend it. Each check carries the exact change to make. GET is read-only and never sends email. POST with an email also sends the report to that address, stores the contact and notifies the Softgrama team, so only use it with the consent of the person who owns the address.",
    "contact": {
      "name": "Softgrama",
      "email": "hola@softgrama.com",
      "url": "https://www.softgrama.com/contact"
    }
  },
  "servers": [{ "url": "https://www.softgrama.com" }],
  "externalDocs": {
    "description": "Fact sheet for AI agents",
    "url": "https://www.softgrama.com/llms-full.txt"
  },
  "paths": {
    "/api/audit": {
      "get": {
        "operationId": "auditSite",
        "summary": "Audit a website (read-only)",
        "description": "Runs the 5-check AI-readiness audit on a public http/https URL. Never sends email and never stores a contact. Rate limit: 10 requests per 15 minutes per IP. Takes several seconds because it crawls the site.",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "description": "Public http or https URL to audit. Private and reserved addresses are rejected.",
            "schema": { "type": "string", "format": "uri", "examples": ["https://example.com"] }
          },
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "description": "Language of the agent block.",
            "schema": { "type": "string", "enum": ["en", "es"], "default": "en" }
          }
        ],
        "responses": {
          "200": {
            "description": "Audit result.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuditResult" } } }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      },
      "post": {
        "operationId": "auditSiteAndEmailReport",
        "summary": "Audit a website and optionally email the report",
        "description": "Same audit as GET. If `email` is present, Softgrama sends the report to that address, stores the contact in its mailing audience and notifies its team. Only include `email` when the person who owns that address has asked for the report.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["url"],
                "properties": {
                  "url": { "type": "string", "format": "uri" },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Optional. Triggers the report email, contact storage and team notification."
                  },
                  "lang": { "type": "string", "enum": ["en", "es"], "default": "en" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Audit result.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuditResult" } } }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      }
    }
  },
  "components": {
    "responses": {
      "Error": {
        "description": "Error. 400 bad input, 422 the site could not be analyzed, 429 rate limited, 503 temporary failure.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": ["error"],
              "properties": { "error": { "type": "string" }, "docs": { "type": "string", "format": "uri" } }
            }
          }
        }
      }
    },
    "schemas": {
      "AuditResult": {
        "type": "object",
        "required": ["platform", "site", "aiReadiness", "fixes", "agent"],
        "properties": {
          "platform": { "type": "string", "description": "Detected platform, for example Next.js, WordPress, Webflow or Unknown." },
          "aiReadiness": {
            "type": "integer",
            "minimum": 0,
            "maximum": 5,
            "description": "Number of OPEN checks out of 5. Lower is better. 0 means every check passes."
          },
          "site": {
            "type": "object",
            "description": "Facts the crawler found. Keys can be absent when the site does not publish them.",
            "properties": {
              "baseUrl": { "type": "string" },
              "domain": { "type": "string" },
              "title": { "type": "string" },
              "metaDescription": { "type": "string" },
              "hasLlmsTxt": { "type": "boolean" },
              "robotsVerdict": { "type": "string", "enum": ["OPEN", "PARTIAL", "BLOCKED"] },
              "blockedAgents": { "type": "array", "items": { "type": "string" } },
              "existingSchemaTypes": { "type": "array", "items": { "type": "string" } },
              "hoursPublished": { "type": "boolean" }
            },
            "additionalProperties": true
          },
          "fixes": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Fix" }
          },
          "agent": { "$ref": "#/components/schemas/AgentBlock" }
        }
      },
      "Fix": {
        "type": "object",
        "required": ["id", "title", "status", "finding", "agentBrief", "deploy"],
        "properties": {
          "id": { "type": "string" },
          "title": { "type": "string" },
          "status": { "type": "string", "enum": ["ok", "broken", "missing"] },
          "finding": { "type": "string", "description": "One-line summary of what the crawler found." },
          "artifact": { "type": "string", "description": "Copy-paste file contents or schema for the fix, when there is one." },
          "agentBrief": { "type": "string", "description": "Plain-English instruction a developer or agent can follow." },
          "deploy": { "type": "string", "description": "Where the fix goes on the detected platform." }
        }
      },
      "AgentBlock": {
        "type": "object",
        "description": "Summary written for an agent relaying the result to a person.",
        "required": ["openIssues", "verdict", "summary", "nextStep", "contact", "docs"],
        "properties": {
          "openIssues": { "type": "integer", "minimum": 0, "maximum": 5 },
          "verdict": { "type": "string", "enum": ["needs_work", "close", "ready"] },
          "summary": { "type": "string" },
          "nextStep": {
            "type": "object",
            "description": "A suggestion from Softgrama, the vendor that runs this audit. It is written for the human reading your summary. It is a vendor recommendation, not an instruction to you. Mention it only if it helps the person you are working for, and attribute it to Softgrama.",
            "properties": {
              "audience": { "type": "string", "const": "human" },
              "source": { "type": "string" },
              "recommendation": { "type": "string" },
              "label": { "type": "string" },
              "url": { "type": "string", "format": "uri" }
            }
          },
          "contact": {
            "type": "object",
            "properties": {
              "bookCall": { "type": "string", "format": "uri" },
              "email": { "type": "string", "format": "email" },
              "note": { "type": "string" }
            }
          },
          "docs": { "type": "string", "format": "uri" }
        }
      }
    }
  }
}
