# The OpenAPI description

The description is at `https://connectbyjbrh.com/developers/openapi.yaml`, written to OpenAPI 3.1.0 and generated by the documentation build. It describes six operations and nothing else. A route reaches it by being listed in the generator's allowlist, which is why opening an internal endpoint cannot publish it by accident.

- **Status:** Available
- **Audience:** developer
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/developers/openapi-description/

## What is in it

| `operationId` | Path | Purpose |
|---|---|---|
| `searchDocumentation` | `GET /api/public/docs/search` | Full-text search over every public page |
| `getDocumentationPage` | `GET /api/public/docs/page` | One page, with its Markdown body |
| `listDocumentation` | `GET /api/public/docs/list` | Walk the corpus, filtered |
| `getProductStatus` | `GET /api/public/docs/status` | Capability status for everything documented |
| `getHealth` | `GET /api/health` | A minimal liveness check |

One server is declared — the production host — and two tags group the operations: `documentation` and `service`. There are no security schemes, which is the machine-readable form of the statement that no credential exists. The `info` block carries a summary, a contact URL and a licence note pointing at the site terms, and its `version` is the product version at the time the file was generated.

> **Note** The health entry carries a warning worth repeating: `/api/health` is the only health path. There is no `/healthz`, `/health` or `/readyz`, and a deploy script that polls one of those will roll back a build that came up perfectly.

## The schemas

**`DocumentSummary`** — The shape every search result and listing item takes. `id`, `url`, `title`, `summary` and `status` are required; `markdown`, `description`, `audiences`, `channels`, `kind`, `updated` and `score` are optional, and `score` appears only on search results.
**`Document`** — `DocumentSummary` plus `body`, `body_format` (`markdown` or `unavailable`) and `related`, composed with `allOf` so a generated client shares one base type.
**`Error`** — The `{error: {code, message}}` envelope, referenced by the `BadRequest`, `NotFound` and `RateLimited` response components.

`status` is declared as an enum of the five capability words, so a generated client gets them as a type rather than as free strings. `id` is documented with the instruction never to parse it: it is a stable identifier, and a page's kind is a field of its own.

## The allowlist, and what stays closed

This description is an allowlist rather than an export. The application's own OpenAPI document, along with `/docs` and `/redoc`, is closed and stays closed; nothing about the internal API is published here, in any form, at any level of detail. A route appears in this file only because the generator was told to include it.

That ordering matters more than it sounds. If the public description were produced by filtering the application's schema, then every new internal endpoint would be one filter mistake away from being published. Building it from an explicit list inverts the failure: a mistake means a public route is missing from the document, which is visible and harmless, rather than a private route being described, which is neither.

> **Careful** Do not point a client generator at the application's own host paths hoping to find a richer schema. There is not one to find, and the routes behind a session are not a public contract — they change without notice because nothing outside the application depends on them.

## Generating a client from it

1. Fetch the file and pin the copy you generated from.
   - Result: You can compare `info.version` later to decide whether to regenerate.
2. Generate with `operationId` as the method name.
   - Result: The five names above are the stable part of the contract and read well in code.
3. Leave authentication unconfigured.
   - Result: No scheme is declared, and a generator that invents a bearer parameter is adding something the server never reads.
4. Keep the `Error` type and branch on `error.code`, with a default branch.
   - Result: Parameter-validation failures arrive in a different shape entirely — see [API error shapes](/developers/api-errors/).

The file is written to OpenAPI 3.1.0 rather than a later revision on purpose: tool support for 3.1 is universal, and nothing in an API this small needs a newer feature. A generator that only understands 3.0 may complain about the JSON Schema dialect; most now handle 3.1 directly.

## Questions

### Is the description generated or hand-written?

Generated by the documentation build from the same capability registry the pages come from, and overwritten by the next build. That is what stops it drifting from the behaviour it describes.

### Why is `/api/health` in a documentation API?

Because it is the one other path a caller legitimately needs, and because naming it here settles the recurring question of which health path exists. It returns `{ok: true}` and nothing more.

### Where are the write operations?

There are none. Every operation described is a `GET`, and no credential exists that would enable anything else — see [API authentication](/developers/api-authentication/).

## Related

- [The public API](https://connectbyjbrh.com/developers/public-api/)
- [The Arazzo workflows](https://connectbyjbrh.com/developers/arazzo-workflows/)
- [API versioning](https://connectbyjbrh.com/developers/api-versioning/)
- [API authentication](https://connectbyjbrh.com/developers/api-authentication/)
- [The machine-readable documentation](https://connectbyjbrh.com/developers/machine-manifests/)
- [OpenAPI](https://connectbyjbrh.com/docs/protocols/openapi/)

## What this page is based on

- `webapp/developers/openapi.yaml`
- `backend/app/public_developer_api.py` — the routes it describes
- OpenAPI 3.1.0 — https://spec.openapis.org/
