API reference generation
Generate endpoint pages straight from OpenAPI specs, from a URL, this repo or another checkout.
On this page
Every page under API reference is generated from OpenAPI specs at build time. List a spec in docs.config.ts and the site builds, from that spec alone:
- a page per resource (OpenAPI tag) and a page per endpoint;
- sidebar entries with method badges;
- search results, Markdown copies,
llms.txtentries and MCP results; - a download of the spec at
/docs/openapi/<id>.json.
Add a spec
apiReference: {
tab: "API reference",
guides: { group: "Using the API", pages: ["api/introduction", "api/errors"] },
specs: [
{ id: "example", title: "Example API", source: "https://example.com/openapi.yaml" },
],
},The id becomes part of every URL: /docs/api/example/<resource>/<endpoint>. The title names the sidebar group; without it, the spec's info.title is used.
Where a spec can come from
source is read directly, with no copying.
| Source | Example | Notes |
|---|---|---|
| A URL | https://example.com/openapi.json | Fetched at build time. Requests to GitHub hosts send GITHUB_TOKEN when it is set, for private repositories |
| A file in this repo | openapi/example.json | Relative to the repo root |
| A file in another checkout | ~/<repo>/docs/swagger.yaml | Absolute paths and ~/ paths work |
Specs can be JSON or YAML, and OpenAPI 3.0, OpenAPI 3.1 or Swagger 2.0. Swagger 2.0 is converted on the way in: definitions, body and form parameters, security definitions, and host plus basePath. OpenAPI 3.1 type lists such as ["string", "null"] become nullable types.
Publish only public endpoints
Generated specs often include internal routes. Filter them out in the config instead of editing the spec:
{
id: "example",
source: "~/<repo>/docs/swagger.json",
include: ["/v1/*"],
exclude: ["*/internal/*", "DELETE *"],
},- A pattern matches the path (
/v1/*) or the method and path (DELETE *).*matches anything, including/. Matching ignores case. includekeeps only matching operations, andexcludedrops matching ones.- Operations marked
x-internal,x-hiddenorx-excludedin the spec are always dropped.
Fix the base URL
Specs generated on a developer machine often name localhost as their server. Set server to the public base URL; examples and resource pages then use it:
{ id: "example", source: "~/<repo>/docs/swagger.json", server: "https://api.example.com" }Keep a local copy
Add cache to keep a copy in this repo, and refresh it where the source is reachable:
{ id: "example", source: "~/<repo>/docs/swagger.json", cache: "openapi/example.json" }pnpm api:sync # every spec that has a cache
pnpm api:sync example # one spec, by idapi:sync saves the source as JSON, unfiltered. Filters apply when the site is built.
When a build cannot read a source, it uses the cache and prints a warning. With DOCS_OPENAPI_OFFLINE=1, it uses the cache first. If neither can be read, the build stops and names the spec.
When specs are read
| Mode | Behaviour |
|---|---|
pnpm dev | Read from the source and kept for 30 seconds, so a change shows up on refresh |
pnpm build | Read once, and saved to .next/openapi-snapshot/<id>.json |
| Running server | Only the MCP server (/docs/mcp) reads specs at request time. It uses the build's snapshot, then the source, then the cache |
What gets generated
- Endpoint URLs follow the path's shape:
POST /widgetsiscreate,GET /widgetsislist,GET /widgets/{id}isget, andPOST /widgets/{id}/archiveisarchive. Anything else uses the operation summary. - Endpoint pages show the method and path, the description, path, query, header and body parameters, what the endpoint returns, and its errors. Beside them are request examples in cURL, TypeScript and Python, and an example response for each status that returns a body.
- Example values come from the spec's own
examplefields. Where there are none, the page shows placeholders built from each field's type.
Download a spec
/docs/openapi/<id>.json serves the spec each API was built from: converted to OpenAPI 3, filtered, and with any server override applied. Resource pages and llms.txt link to it.