# The public API

Five GET endpoints under `https://connectbyjbrh.com/api/public/` search, fetch, list and describe the public documentation, plus one manifest reader. They need no credential, take no writes, and reach no workspace. `/api/health` is the only other path in the published description.

- **Status:** Available
- **Audience:** developer
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/developers/public-api/

## The endpoints

| Endpoint | Parameters | Returns |
|---|---|---|
| `GET /api/public/docs/search` | `q` (2–200 chars, required), `limit` 1–25 (default 10), `kind`, `status`, `channel` | `{query, count, results[]}` — ranked page summaries |
| `GET /api/public/docs/page` | `ref` (required, ≤300 chars), `body` (default `true`) | One page with its Markdown body and `related` list |
| `GET /api/public/docs/list` | `kind`, `status`, `channel`, `audience`, `limit` 1–1000 (default 100) | `{count, items[]}` in path order |
| `GET /api/public/docs/status` | — | Product, version, generated date, the five status words, every capability's status, corpus counts |
| `GET /api/public/docs/manifest/{name}` | `name` in the path | One generated manifest as JSON |
| `GET /api/health` | — | `{ok: true}` — the only health path there is |

```bash
curl -sS 'https://connectbyjbrh.com/api/public/docs/search?q=held+draft&limit=3'
curl -sS 'https://connectbyjbrh.com/api/public/docs/page?ref=/docs/email/held-drafts/'
curl -sS 'https://connectbyjbrh.com/api/public/docs/list?kind=workflow&limit=50'
curl -sS 'https://connectbyjbrh.com/api/public/docs/status'
```

## What a response carries

Every page summary has the same fields wherever it appears: `id`, `kind`, `url`, `markdown`, `title`, `description`, `summary`, `status`, `audiences`, `channels` and `updated`. Search results add a `score`; a fetched page adds `body`, `body_format` and `related`. The `summary` is the page's answer-first paragraph, which is written to stand alone — for many questions it is the whole answer and a fetch is unnecessary.

`status` is the field to branch on. `available` and `available_owner` describe something running; `foundation` means the model, service and tests exist while the usable capability is deliberately narrower; `not_yet` means designed and not built; `reference` means background with Connect's own use stated separately on the page.

Successful responses carry `Cache-Control: public, max-age=300` and `Access-Control-Allow-Origin: *`. The corpus changes on a deploy rather than per request, so caching for five minutes costs nothing in freshness and a browser client can call the API directly without a proxy.

## The boundary

This is not the application's API. The application's own OpenAPI document, `/docs` and `/redoc`, is closed and stays closed. A route reaches the published description only by being written there *and* listed in the generator's allowlist, so opening an internal endpoint cannot publish it by accident — which is the failure mode this arrangement exists to prevent.

Nothing here reaches a workspace. Every handler calls the same documentation module that serves the website's own pages, so there is no session to hold, no workspace scope to get wrong, and no write path to protect. The paths sit under `/api/public/` so the middleware rule that authentication applies to `/api` carries one visible exception rather than a scatter of special cases.

> **Careful** There is no public write endpoint. Nothing under `/api/public/` accepts `POST`, `PUT`, `PATCH` or `DELETE`, and no credential exists that would enable one. A client generated from the description will have read methods only, and that is the whole API.

## Using it well

- Search first, then fetch the winner. A search result carries a summary, which is enough to choose a page and often not enough to answer from.
- Prefer `docs/status` over inference. One call returns every capability's status word; guessing from prose is how an integration starts claiming something Connect does not do.
- Cache on `updated`. It is a date on every page and it moves only when the page is regenerated.
- Walk with `list`, not with repeated searches. Listing is ordered by path and stable between calls; search is ranked and its order depends on the query.
- Ask for `body=false` when you only need metadata; the body is the expensive part of a fetch.

## Questions

### Is there a sandbox or a test host?

The production host is the only one, and since every call is a read of public documentation there is nothing to damage by calling it. [Testing against Connect safely](/developers/sandbox/) covers what that means for a client's test suite.

### What happens if I ask for a page that does not exist?

HTTP `404` with `{"error": {"code": "not_found", "message": …}}`, and the message suggests searching first. The shapes are on [API error shapes](/developers/api-errors/).

### Can I use this API to build a competing documentation site?

The API returns published documentation and the site terms apply to it, as the description's licence field says. Nothing technical stops you reading it; attribution and terms are a separate question from access.

## Related

- [The OpenAPI description](https://connectbyjbrh.com/developers/openapi-description/)
- [API error shapes](https://connectbyjbrh.com/developers/api-errors/)
- [API rate limits](https://connectbyjbrh.com/developers/api-rate-limits/)
- [Searching the documentation programmatically](https://connectbyjbrh.com/developers/docs-search-api/)
- [The public data model](https://connectbyjbrh.com/developers/data-model-public/)
- [Developers and AI agents](https://connectbyjbrh.com/developers/)
- [OpenAPI](https://connectbyjbrh.com/docs/protocols/openapi/)

## What this page is based on

- `backend/app/public_developer_api.py`
- `backend/app/public_docs.py` — the search, fetch, listing and status functions
- `webapp/developers/openapi.yaml`
