---
title: API reference generation
description: Generate endpoint pages straight from OpenAPI specs, from a URL,
  this repo or another checkout.
url: https://hexelstudio.com/docs/reference/api-reference
---

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.txt` entries and MCP results;
- a download of the spec at `/docs/openapi/<id>.json`.

## Add a spec

```ts docs.config.ts
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.

<Warning>
  A `~/` path exists only on your machine. A build on Vercel, in CI or in Docker cannot read it: give the spec a `cache` and commit that file.
</Warning>

## Publish only public endpoints

Generated specs often include internal routes. Filter them out in the config instead of editing the spec:

```ts
{
  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.
- `include` keeps only matching operations, and `exclude` drops matching ones.
- Operations marked `x-internal`, `x-hidden` or `x-excluded` in 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:

```ts
{ 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:

```ts
{ id: "example", source: "~/<repo>/docs/swagger.json", cache: "openapi/example.json" }
```

```bash
pnpm api:sync           # every spec that has a cache
pnpm api:sync example   # one spec, by id
```

`api: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 /widgets` is `create`, `GET /widgets` is `list`, `GET /widgets/{id}` is `get`, and `POST /widgets/{id}/archive` is `archive`. 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 `example` fields. 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.
