{
  "openapi": "3.1.0",
  "info": {
    "title": "Live Good Warrior Content API",
    "version": "1.0.0",
    "summary": "Read-only access to every published article and page on livegoodwarrior.com.",
    "description": "Live Good Warrior is a health and wellness publication for adults over 45, covering sleep, supplements, red light therapy, liver health, hair loss and nutrition.\n\nThis specification documents the read-only machine-readable surface of the site: the whole-site search index, the agent guide, the sitemap, and the markdown twin that exists for every page. There are no write operations, no authentication, and no rate limit beyond ordinary CDN fair use.\n\nThe site also runs two private form handlers under /api/ for its own opt-in and contact forms. They are deliberately NOT part of this specification. They are not a public interface and accept POST from the site's own pages only; the error shape they return is documented under components so that an unexpected caller can read the reply.\n\nContent is educational and is not medical advice. Attribute as Live Good Warrior, https://livegoodwarrior.com, author Anna.",
    "contact": {
      "name": "Anna",
      "email": "anna@livegoodwarrior.com",
      "url": "https://livegoodwarrior.com/contact-live-good-warrior/"
    },
    "license": {
      "name": "All rights reserved. Quotation with attribution and a link is welcome.",
      "url": "https://livegoodwarrior.com/terms-of-service/"
    }
  },
  "servers": [
    {
      "url": "https://livegoodwarrior.com",
      "description": "Production"
    }
  ],
  "tags": [
    { "name": "discovery", "description": "Files that describe what the site holds." },
    { "name": "content", "description": "The articles and pages themselves." }
  ],
  "paths": {
    "/llms.txt": {
      "get": {
        "operationId": "getAgentGuide",
        "tags": ["discovery"],
        "summary": "Agent guide",
        "description": "Plain-text index describing what the site covers, when an agent should reach for it, when it should not, and how to call it. Follows the llms.txt convention.",
        "responses": {
          "200": {
            "description": "The agent guide.",
            "content": {
              "text/plain": { "schema": { "type": "string" } }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApiSpec",
        "tags": ["discovery"],
        "summary": "This specification",
        "description": "Returns this OpenAPI document.",
        "responses": {
          "200": {
            "description": "The OpenAPI specification.",
            "content": {
              "application/json": { "schema": { "type": "object" } }
            }
          }
        }
      }
    },
    "/sitemap-index.xml": {
      "get": {
        "operationId": "getSitemapIndex",
        "tags": ["discovery"],
        "summary": "Sitemap index",
        "description": "XML sitemap index listing every indexable URL on the site. Use it to enumerate pages before fetching them.",
        "responses": {
          "200": {
            "description": "Sitemap index.",
            "content": {
              "application/xml": { "schema": { "type": "string" } }
            }
          }
        }
      }
    },
    "/search-index.json": {
      "get": {
        "operationId": "getSearchIndex",
        "tags": ["discovery"],
        "summary": "Whole-site text index",
        "description": "Every post and page in one JSON array: path, title, description and up to 4,000 characters of body text. Built at deploy time. Intended for retrieval, and for answering a question without fetching each page separately.",
        "responses": {
          "200": {
            "description": "One entry per published document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/IndexedDocument" }
                }
              }
            }
          }
        }
      }
    },
    "/{slug}/": {
      "get": {
        "operationId": "getPage",
        "tags": ["content"],
        "summary": "Fetch one article or page",
        "description": "Returns the page. Content is negotiated: send Accept: text/markdown for the markdown twin, or Accept: text/html for the rendered page. Responses carry Vary: Accept, Accept-Encoding.",
        "parameters": [
          { "$ref": "#/components/parameters/Slug" },
          {
            "name": "Accept",
            "in": "header",
            "required": false,
            "description": "Set to text/markdown for markdown, text/html for the rendered page.",
            "schema": {
              "type": "string",
              "enum": ["text/markdown", "text/html"],
              "default": "text/html"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The page, in the negotiated format.",
            "headers": {
              "Vary": {
                "description": "Always includes Accept, so caches keep the two variants apart.",
                "schema": { "type": "string" }
              }
            },
            "content": {
              "text/html": { "schema": { "type": "string" } },
              "text/markdown": { "schema": { "type": "string" } }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/{slug}/index.md": {
      "get": {
        "operationId": "getPageMarkdown",
        "tags": ["content"],
        "summary": "Fetch one page as markdown",
        "description": "The markdown twin of a page, without content negotiation. The same document getPage returns under Accept: text/markdown. Use this when a client cannot set request headers.",
        "parameters": [{ "$ref": "#/components/parameters/Slug" }],
        "responses": {
          "200": {
            "description": "The page as markdown.",
            "content": {
              "text/markdown": { "schema": { "type": "string" } }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/index.md": {
      "get": {
        "operationId": "getHomepageMarkdown",
        "tags": ["content"],
        "summary": "Homepage as markdown",
        "description": "The homepage in markdown: what the site covers, the topic list, the latest articles, and the discovery files.",
        "responses": {
          "200": {
            "description": "The homepage as markdown.",
            "content": {
              "text/markdown": { "schema": { "type": "string" } }
            }
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "Slug": {
        "name": "slug",
        "in": "path",
        "required": true,
        "description": "The page's URL slug, as listed in the sitemap or in the u field of the search index.",
        "schema": {
          "type": "string",
          "pattern": "^[a-z0-9-]+$",
          "examples": ["how-to-stop-snoring-naturally", "rapid-weight-loss-fatty-liver"]
        }
      }
    },
    "responses": {
      "NotFound": {
        "description": "No such page. The body is markdown when the request asked for markdown and HTML otherwise; both name the sitemap and the agent guide so a caller can recover.",
        "content": {
          "text/markdown": { "schema": { "type": "string" } },
          "text/html": { "schema": { "type": "string" } }
        }
      }
    },
    "schemas": {
      "IndexedDocument": {
        "type": "object",
        "description": "One published post or page. Field names are single letters to keep the whole-site index small.",
        "required": ["u", "t", "d", "b"],
        "properties": {
          "u": {
            "type": "string",
            "description": "Site-root-relative path, with trailing slash.",
            "examples": ["/how-to-stop-snoring-naturally/"]
          },
          "t": { "type": "string", "description": "Page title." },
          "d": { "type": "string", "description": "Short description or excerpt, up to 200 characters." },
          "b": {
            "type": "string",
            "description": "Body text, lowercased, up to 4,000 characters, truncated at a word boundary.",
            "maxLength": 4000
          }
        }
      },
      "Error": {
        "type": "object",
        "description": "The structured error returned by the private /api/ form handlers. Documented so an unexpected caller can read the reply; those endpoints are not part of this API.",
        "required": ["ok", "error"],
        "properties": {
          "ok": { "type": "boolean", "const": false },
          "error": {
            "type": "object",
            "required": ["code", "message", "hint"],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable code.",
                "examples": ["not_found", "method_not_allowed", "server_error"]
              },
              "message": { "type": "string", "description": "What went wrong, in one sentence." },
              "hint": { "type": "string", "description": "What to do about it." }
            }
          }
        }
      }
    }
  },
  "externalDocs": {
    "description": "Machine-readable access, explained",
    "url": "https://livegoodwarrior.com/docs/"
  }
}
