{
  "openapi": "3.1.0",
  "info": {
    "title": "docspack.dev",
    "version": "1",
    "description": "The machine-readable half of docspack.dev.\n\nEvery route here is a static file served from the edge: no credential, no rate limit, no state. They exist because an agent reading this site should not have to scrape HTML for the specification, the manual or the measurements behind the numbers on the landing page.\n\nThis document is itself served at `/openapi.json`, and it is what renders the page you are reading — the same source produces a reference a person reads and a digest an agent is answered from."
  },
  "servers": [
    {
      "url": "https://docspack.dev",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "agents",
      "description": "Routes written for a model rather than a browser."
    },
    {
      "name": "manual",
      "description": "The documentation, as its own source."
    },
    {
      "name": "measurements",
      "description": "The runs behind every number on the site."
    },
    {
      "name": "specification",
      "description": "The docs-package format itself."
    },
    {
      "name": "example",
      "description": "A fictional API, served in both renderings so the two can be compared."
    }
  ],
  "paths": {
    "/llms.txt": {
      "get": {
        "operationId": "getLlmsTxt",
        "summary": "The site's table of contents for an agent.",
        "description": "An [llms.txt](https://llmstxt.org) index: one line per document, each with a link and a one-line description. Start here, then fetch what the question needs.",
        "tags": [
          "agents"
        ],
        "responses": {
          "200": {
            "description": "The index.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/llms-full.txt": {
      "get": {
        "operationId": "getLlmsFullTxt",
        "summary": "Every document on the site, concatenated.",
        "description": "The whole corpus in one response. Useful for a one-off ingest; for a question about one thing, `/llms.txt` and one page cost far less.",
        "tags": [
          "agents"
        ],
        "responses": {
          "200": {
            "description": "The corpus.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/ai-rules.md": {
      "get": {
        "operationId": "getAiRules",
        "summary": "The block `docspack agent install` writes, as Markdown.",
        "tags": [
          "agents"
        ],
        "responses": {
          "200": {
            "description": "The rules.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/docs/{slug}.md": {
      "get": {
        "operationId": "getDocPage",
        "summary": "One page of the manual, as the Markdown it already is.",
        "description": "The same bytes the page at `/docs/{slug}` is rendered from, with no HTML shell. A `:::` presentation block is replaced by what it wraps, so the page a reader sees and the text an agent is given describe the same behaviour.",
        "tags": [
          "manual"
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "A page's slug, as listed in /llms.txt.",
            "schema": {
              "type": "string",
              "examples": [
                "searching"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The page.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No page with that slug."
          }
        }
      }
    },
    "/docs/search.json": {
      "get": {
        "operationId": "getSearchIndex",
        "summary": "The manual split into searchable sections.",
        "description": "The same sections `docspack build` would cut, so a section found here is the chunk a model would have been given. Ranking is deliberately not included: emitting a corpus and ranking one are different jobs.",
        "tags": [
          "manual"
        ],
        "responses": {
          "200": {
            "description": "The index.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchIndex"
                }
              }
            }
          }
        }
      }
    },
    "/schema/v1.json": {
      "get": {
        "operationId": "getManifestSchema",
        "summary": "The JSON Schema for a docs package's manifest.",
        "description": "The URL `docspack build` writes into every manifest as `$schema`, and the one a validator fetches. It resolves at exactly this path because the `$id` inside the document says so.",
        "tags": [
          "specification"
        ],
        "responses": {
          "200": {
            "description": "The schema, as a JSON Schema document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/spec.md": {
      "get": {
        "operationId": "getSpec",
        "summary": "The docs-package specification, as Markdown.",
        "description": "The chunk-id grammar, the path-escape rule and what every manifest field means.",
        "tags": [
          "specification"
        ],
        "responses": {
          "200": {
            "description": "The specification.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApi",
        "summary": "This document.",
        "description": "The OpenAPI 3.1 description of these routes, which is what the reference page and the per-operation digests are generated from.",
        "tags": [
          "specification"
        ],
        "responses": {
          "200": {
            "description": "The document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api.lapis": {
      "get": {
        "operationId": "getApiLapis",
        "summary": "This API in LAPIS notation, for a model.",
        "description": "The same document as `/openapi.json`, rendered in the notation docspack answers with. Around a tenth of the tokens, and the one artifact here that grows with the size of the API — for a question about a single endpoint, fetch that endpoint's digest instead.",
        "tags": [
          "agents"
        ],
        "responses": {
          "200": {
            "description": "The document.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/{operationId}.md": {
      "get": {
        "operationId": "getOperationDigest",
        "summary": "Everything needed to call one endpoint, and nothing else.",
        "description": "The base URL, the credential, the inputs with their types, the response, the failures, the types they reference, and a runnable `curl` call — for one operation. This is the densest form of the question \"how do I make this request\".",
        "tags": [
          "agents"
        ],
        "parameters": [
          {
            "name": "operationId",
            "in": "path",
            "required": true,
            "description": "An operation's id, as listed in /api.lapis.",
            "schema": {
              "type": "string",
              "examples": [
                "getDocPage"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The digest.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No operation with that id."
          }
        }
      }
    },
    "/api/example.json": {
      "get": {
        "operationId": "getExampleApi",
        "summary": "The worked example's OpenAPI document.",
        "description": "A fictional invoicing API — bearer auth, request bodies, a `DELETE`, an error shape — kept so the reference at `/api/example` can demonstrate what this site's own read-only API cannot. Point `docspack build --openapi` at this URL to see what it emits.",
        "tags": [
          "example"
        ],
        "responses": {
          "200": {
            "description": "The document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/example.lapis": {
      "get": {
        "operationId": "getExampleApiLapis",
        "summary": "The worked example in LAPIS.",
        "description": "The same document as `/api/example.json`, in the notation docspack answers with: every operation and every type in about a fifth of the tokens. The pair is here to be compared.",
        "tags": [
          "example"
        ],
        "responses": {
          "200": {
            "description": "The document as LAPIS.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/benchmarks.json": {
      "get": {
        "operationId": "getBenchmarks",
        "summary": "What a query costs, measured.",
        "description": "The object `bench/results.json` holds, served verbatim: every percentile and the machine it was taken on. A benchmark a reader cannot inspect is a claim.",
        "tags": [
          "measurements"
        ],
        "responses": {
          "200": {
            "description": "The run.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Benchmarks"
                }
              }
            }
          }
        }
      }
    },
    "/coverage.json": {
      "get": {
        "operationId": "getCoverage",
        "summary": "How much of a library its own documentation covers.",
        "description": "`bench/coverage.json`, including the sample of exported names each library documents nowhere. Produced by comparing a library's published documentation against the same library as installed.",
        "tags": [
          "measurements"
        ],
        "responses": {
          "200": {
            "description": "The run.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Coverage"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "SearchRecord": {
        "type": "object",
        "required": [
          "slug",
          "title",
          "heading",
          "anchor",
          "text"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "The page's slug, for the URL."
          },
          "title": {
            "type": "string",
            "description": "The page's title."
          },
          "heading": {
            "type": "string",
            "description": "The section's heading. Equal to the title for the text above the first `##`."
          },
          "anchor": {
            "type": "string",
            "description": "Fragment on the page. Empty for the lead section."
          },
          "text": {
            "type": "string",
            "description": "The section's prose, with Markdown syntax reduced to the words in it."
          }
        }
      },
      "SearchIndex": {
        "type": "object",
        "required": [
          "version",
          "records"
        ],
        "properties": {
          "version": {
            "type": "integer",
            "description": "Bumped when the record shape changes in a way a reader would notice."
          },
          "records": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SearchRecord"
            }
          }
        }
      },
      "Timing": {
        "type": "object",
        "description": "A measured distribution, in milliseconds.",
        "required": [
          "n",
          "p50",
          "p95",
          "min",
          "max"
        ],
        "properties": {
          "n": {
            "type": "integer",
            "description": "How many runs."
          },
          "p50": {
            "type": "number"
          },
          "p95": {
            "type": "number"
          },
          "min": {
            "type": "number"
          },
          "max": {
            "type": "number"
          }
        }
      },
      "Benchmarks": {
        "type": "object",
        "required": [
          "latency",
          "context"
        ],
        "properties": {
          "latency": {
            "type": "object",
            "description": "Cold-start timings, split so that Node's own startup is not counted as docspack's work.",
            "properties": {
              "coldAsk": {
                "$ref": "#/components/schemas/Timing"
              },
              "nodeBaseline": {
                "$ref": "#/components/schemas/Timing"
              }
            }
          },
          "context": {
            "type": "object",
            "description": "What an answer costs against the size of the corpus it came from.",
            "properties": {
              "corpusTokens": {
                "type": "integer"
              },
              "queries": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "query": {
                      "type": "string"
                    },
                    "tokens": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "network": {
            "type": "object",
            "description": "What fetching the same documentation over the web would have cost.",
            "properties": {
              "estimatedTokens": {
                "type": "integer"
              }
            }
          }
        }
      },
      "LibraryCoverage": {
        "type": "object",
        "required": [
          "library",
          "version",
          "coverage"
        ],
        "properties": {
          "library": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "coverage": {
            "type": "object",
            "properties": {
              "names": {
                "type": "integer",
                "description": "Every name an entry point in the `exports` map publishes."
              },
              "documented": {
                "type": "integer",
                "description": "How many of those the library's own documentation mentions anywhere."
              },
              "undocumented": {
                "type": "integer"
              },
              "onDisk": {
                "type": "integer",
                "description": "How many undocumented names the installed build declares."
              },
              "marked": {
                "type": "integer",
                "description": "The same count, dropping names the library marks internal by convention."
              }
            }
          }
        }
      },
      "Coverage": {
        "type": "object",
        "required": [
          "targets"
        ],
        "properties": {
          "targets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LibraryCoverage"
            }
          }
        }
      }
    }
  }
}
