---
title: OpenAPI specs
description: Point Reed at a spec and get a generated section.
---

Declare specs by name, then reference a name from `navigation`.

```json
{
  "api": { "specs": { "ingestion": "openapi/ingestion.yaml" } },
  "navigation": {
    "tabs": [
      {
        "label": "Ingestion API",
        "groups": [
          {
            "label": "Ingestion API",
            "pages": [{ "label": "Ingestion API", "api": true, "spec": "ingestion" }]
          }
        ]
      }
    ]
  }
}
```

Reed synthesizes an endpoint page per operation. No MDX file exists for them, and you do
not write one.

## Spec names

`^[A-Za-z0-9_-]+$`, and unique **case-insensitively**. Each name becomes an
`openapi/<name><ext>` output path, and two names differing only in case would collide
unpredictably on a case-insensitive filesystem — which is the default on macOS and
Windows. The schema rejects the collision rather than letting the result depend on which
machine ran the build.

## One tab per spec

An API entry must be the only page of its group, and that group the only group of its
tab. You cannot mix generated endpoint pages alongside hand-written pages in one group.

This is a real constraint on your information architecture: an "API" tab holding both a
hand-written authentication guide and the generated endpoints is not expressible. Put the
guide in a different tab and link across.

`spec` may be omitted only when `api.specs` has exactly one entry.

## Multiple specs

```json
{
  "api": {
    "specs": {
      "ingestion": "openapi/ingestion.yaml",
      "management": "openapi/management.yaml"
    }
  }
}
```

Each gets its own `ApiRef`, and therefore its own tab. A project with two APIs has two
tabs; that is the shape, not a workaround.

## Your spec file is never modified

Code snippets are injected as `x-codeSamples` into a **derived copy** of the document
inside the build. The file on disk is untouched, and any `x-codeSamples` you wrote
yourself always win over generated ones.
