{
  "openapi": "3.0.3",
  "info": {
    "title": "saimshafique.com Agent & Developer API",
    "description": "Public, unauthenticated, read-only machine interfaces for saimshafique.com — the portfolio of Saim Shafique (frontend & full-stack developer, Lahore, Pakistan). Use these endpoints to fetch agent-oriented page content as markdown or JSON, check service status, and recover from broken links with structured JSON errors. Human-facing pages live at https://saimshafique.com/ and the developer guide at https://saimshafique.com/developers.\n\n## Versioning & stability\n\nThis API is version **1**, addressable both ways:\n\n- **URL path versioning (preferred):** `https://saimshafique.com/api/v1/status`, `https://saimshafique.com/api/v1/markdown`\n- **Unversioned aliases:** `/api/status`, `/api/markdown` — these always track the latest stable version.\n\nEvery response carries an `X-API-Version` header (currently `1`). Breaking changes will ship only under a new path version (`/api/v2/...`); the previous version keeps working during a deprecation window. Deprecations are signalled with a standard `Sunset` header on affected responses (HTTP-date of removal, set to a placeholder spec URL until the first deprecation is scheduled) and announced in the developer portal at https://saimshafique.com/developers before anything is switched off.\n\n## Rate limits\n\nA courtesy limit of **600 requests per 60-second window** per client per serving instance applies to API endpoints. Responses carry the RFC-draft `RateLimit-Policy`, `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset` headers so agents can self-throttle in real time. When the limit is exceeded, the endpoint returns HTTP 429 with a structured JSON error and a `Retry-After` header giving the exact number of seconds to wait.",
    "version": "1.0.0",
    "contact": {
      "name": "Saim Shafique",
      "email": "contact@saimshafique.com",
      "url": "https://saimshafique.com/contact"
    },
    "license": {
      "name": "CC-BY-4.0",
      "url": "https://creativecommons.org/licenses/by/4.0/"
    }
  },
  "servers": [
    {
      "url": "https://saimshafique.com/api/v1",
      "description": "Production — version 1 (preferred, path-versioned)"
    },
    {
      "url": "https://saimshafique.com",
      "description": "Production — unversioned aliases (track latest stable)"
    }
  ],
  "tags": [
    {
      "name": "content",
      "description": "Agent-readable page content (markdown mirrors of every public page)."
    },
    {
      "name": "meta",
      "description": "Service discovery and health."
    }
  ],
  "paths": {
    "/status": {
      "get": {
        "tags": ["meta"],
        "summary": "Service status and resource discovery",
        "description": "Returns service health plus canonical URLs for every machine-readable resource on the domain (llms.txt guide, OpenAPI spec, sitemap, markdown negotiation endpoint). Call this first when discovering the site programmatically. Available at /api/v1/status (preferred) and /api/status (alias).",
        "operationId": "getServiceStatus",
        "responses": {
          "200": {
            "description": "Service is healthy. Includes RateLimit-* headers for self-throttling.",
            "headers": {
              "X-API-Version": { "description": "API version serving the request (currently `1`).", "schema": { "type": "string" } },
              "RateLimit-Policy": { "description": "Rate limit policy, e.g. `600;w=60`.", "schema": { "type": "string" } },
              "RateLimit-Limit": { "description": "Requests allowed per window.", "schema": { "type": "string" } },
              "RateLimit-Remaining": { "description": "Requests remaining in the current window.", "schema": { "type": "string" } },
              "RateLimit-Reset": { "description": "Seconds until the window resets.", "schema": { "type": "string" } }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/StatusResponse" }
              }
            }
          },
          "429": {
            "description": "Courtesy rate limit exceeded. Wait `Retry-After` seconds, then retry.",
            "headers": {
              "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer" } }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          }
        }
      }
    },
    "/markdown": {
      "get": {
        "tags": ["content"],
        "summary": "Fetch any page as markdown or JSON",
        "description": "Returns the content of a public page. By default the markdown mirror is returned as `text/markdown`; add `format=json` to receive the same content as a structured JSON object (fields `url`, `format`, `content`). Equivalent to requesting a page with an `Accept: text/markdown` header. Mirrors exist for the homepage (/index.md), all four project case studies (/projects/{skillcheck,inksolver,bimo,chronos}.md), and the about, contact, privacy, and developers pages. Available at /api/v1/markdown (preferred) and /api/markdown (alias).",
        "operationId": "getPageContent",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "description": "Site-relative path of the page whose content should be returned, e.g. `/projects/skillcheck.md`. A bare directory such as `/about` resolves to its index mirror.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 300,
              "examples": {
                "home": { "value": "/index.md" },
                "project": { "value": "/projects/skillcheck.md" },
                "about": { "value": "/about" }
              }
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "Response representation. `markdown` (default) returns the raw markdown source as text/markdown; `json` returns a structured object with `url`, `format`, and `content` fields.",
            "schema": {
              "type": "string",
              "enum": ["markdown", "json"],
              "default": "markdown"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page content. Responses carry `Vary: Accept, Accept-Encoding` (markdown mode) plus RateLimit-* and X-API-Version headers.",
            "headers": {
              "X-API-Version": { "description": "API version serving the request (currently `1`).", "schema": { "type": "string" } },
              "Vary": { "description": "`Accept, Accept-Encoding` in markdown mode.", "schema": { "type": "string" } },
              "RateLimit-Remaining": { "description": "Requests remaining in the current window.", "schema": { "type": "string" } }
            },
            "content": {
              "text/markdown": {
                "schema": { "type": "string", "description": "Raw markdown source of the page." }
              },
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MarkdownPage" }
              }
            }
          },
          "404": {
            "description": "No mirror exists for that path. The error body lists recovery URLs.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          },
          "400": {
            "description": "Missing or malformed `url` query parameter.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          },
          "429": {
            "description": "Courtesy rate limit exceeded. Wait `Retry-After` seconds, then retry.",
            "headers": {
              "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer" } }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "StatusResponse": {
        "type": "object",
        "required": ["service", "status", "owner", "resources"],
        "properties": {
          "service": {
            "type": "string",
            "enum": ["saimshafique.com"],
            "description": "Domain this API serves."
          },
          "status": {
            "type": "string",
            "enum": ["ok"],
            "description": "Health indicator; always `ok` while this response is served."
          },
          "owner": {
            "type": "object",
            "required": ["name", "role", "location", "email"],
            "properties": {
              "name": { "type": "string", "example": "Saim Shafique" },
              "alternateName": { "type": "string", "example": "SX4IM" },
              "role": {
                "type": "string",
                "example": "Frontend & Full-Stack Developer"
              },
              "location": { "type": "string", "example": "Lahore, Pakistan" },
              "email": { "type": "string", "format": "email", "example": "contact@saimshafique.com" }
            }
          },
          "resources": {
            "type": "object",
            "description": "Canonical URLs for every machine-readable resource on this domain.",
            "required": [
              "agentGuide",
              "developerPortal",
              "openapiSpec",
              "sitemap",
              "markdownNegotiation",
              "apiCatalog"
            ],
            "properties": {
              "agentGuide": {
                "type": "string",
                "format": "uri",
                "example": "https://saimshafique.com/llms.txt"
              },
              "developerPortal": {
                "type": "string",
                "format": "uri",
                "example": "https://saimshafique.com/developers"
              },
              "openapiSpec": {
                "type": "string",
                "format": "uri",
                "example": "https://saimshafique.com/openapi.json"
              },
              "sitemap": {
                "type": "string",
                "format": "uri",
                "example": "https://saimshafique.com/sitemap.xml"
              },
              "markdownNegotiation": {
                "type": "string",
                "format": "uri",
                "description": "Send this URL an `Accept: text/markdown` header to receive markdown instead of HTML.",
                "example": "https://saimshafique.com/"
              },
              "apiCatalog": {
                "type": "string",
                "format": "uri",
                "description": "RFC 9727 well-known API catalog describing this API.",
                "example": "https://saimshafique.com/.well-known/api-catalog"
              }
            }
          }
        }
      },
      "MarkdownPage": {
        "type": "object",
        "required": ["url", "format", "content"],
        "properties": {
          "url": {
            "type": "string",
            "description": "Normalized site-relative path that was resolved.",
            "example": "/projects/skillcheck.md"
          },
          "format": {
            "type": "string",
            "enum": ["text/markdown"],
            "description": "Media type of `content`."
          },
          "content": {
            "type": "string",
            "description": "Full markdown source of the page, starting with an H1 title."
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "description": "Structured error returned by all API endpoints, including 429 rate-limit responses. HTML error pages are never served under /api/*.",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message", "status", "resolution"],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable error code.",
                "enum": ["not_found", "bad_request", "rate_limited", "internal_error"]
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation of what went wrong."
              },
              "status": {
                "type": "integer",
                "description": "HTTP status code, repeated in the body.",
                "minimum": 400,
                "maximum": 599
              },
              "resolution": {
                "type": "array",
                "items": { "type": "string" },
                "description": "Concrete next-step hints an agent can follow to recover."
              }
            }
          }
        }
      }
    }
  }
}
