---
title: "API"
description: "One page per exported symbol, at a guessable URL, so every type name in the documentation is a link rather than a dead end."
url: "https://opsinjs.pensievelabs.org/reference/api"
source: "https://opsinjs.pensievelabs.org/reference/api.md"
section: "Reference"
kind: "reference"
reviewed: "2026-09-02"
reviewer: "engineering"
aliases: ["api index", "symbol pages", "exported symbols"]
---

> Elements written as `<PascalCase … />` below are opsinjs documentation
> components. Their attributes are the content: the values they render are
> generated from `tokens/*.json` and `registry/catalogue.ts` and are
> published separately at https://opsinjs.pensievelabs.org/r/index.json and under the Reference
> section.
> Nothing is missing from this page. The data simply does not live in
> the prose.

<PageTemplate kind="reference" />

Every `type` and `interface` this site's `lib/` exports has a page here,
addressed by its exact exported identifier:

```text
opsinjs.pensievelabs.org/docs/reference/api/ClinicalStatus
opsinjs.pensievelabs.org/docs/reference/api/HealthCategory
opsinjs.pensievelabs.org/docs/reference/api/ReferenceRange
```

The identifier is used verbatim, with the same case, no kebab-casing and no
pluralisation. That is the whole design: an inline `<ApiLink>` in prose, a link
from a props table, and a program guessing a URL from a type name it read in an
error message all arrive at the same page without a lookup table.

## How this is generated [#how-this-is-generated]

**Source:** the `export` declarations under `apps/www/lib/`, which are this
documentation site's own TypeScript rather than a published package. No `@opsinjs` package has ever
been released and, under
[decision 0002](../../project/decisions/0002-shadcn-registry-distribution.mdx),
none is going to be: opsinjs is distributed by copying source into your
repository. So read these pages as the definitions behind the site and the
copied components, never as the API of a module you can install.
&#x2A;*Script:** `scripts/build-reference.mts`, which reads declaration lines
textually rather than through `fumadocs-typescript`, because a reference
generator has to run before anything in the repository compiles.
&#x2A;*Command:** `pnpm run generate`. This index page is hand-written; every page
beneath it is generated, and `assert-ia.mts` fails the build if an `<ApiLink>`
anywhere in the corpus names a symbol with no page.

### Why per-symbol pages and not one long page [#why-per-symbol-pages-and-not-one-long-page]

A single API document is the format every generated documentation tool reaches
for and it fails in the same three ways each time. Anchors churn, so a link that
worked last release lands halfway down the wrong section. The page grows past the
point where a browser's find-in-page is useful. And a program that fetches it
gets forty thousand tokens of unrelated types in order to answer one question
about one union. A page per symbol costs one file each and fixes all three, and
the `.md` twin of a symbol page is small enough to paste into a prompt whole.

Only types and interfaces earn one. Exported functions and constants are listed
in [Types](../generated/types.mdx) with the file they are declared in, which is
what you actually want for a function; a page apiece for every `STATUS_META`
would bury the symbols an `<ApiLink>` has to resolve to.

### What a symbol page contains [#what-a-symbol-page-contains]

Four things, and no more than a line scanner can honestly produce: the symbol's
name, its doc comment in full, a table giving its kind and the file it is
declared in, and its whole declaration, copied from the source rather than
reconstructed. An interface shows its members and a union shows its cases,
because for a type declared across twenty lines the opening line is the one part
of it the page title has already told you. Field comments are stripped from that
block, because a paragraph inside a signature hides the shape the block is there
to show; the prose is in the source, at the path the table names.

Improve a page by improving its doc comment. The first sentence of it is also
the page's `description`, which is the search snippet and the card subtitle, so
the generator writes that line rather than preserving it and a page's summary
cannot drift from the symbol's.

For the health types the page is only ever half the answer. The declaration says
there are four status levels; only
[Clinical status semantics](../../health/clinical-status-semantics.mdx) says what
`attention` obliges you to do, and that is the half a type reference cannot
carry. The two pages here that keep hand-written prose above their generated
block, below, both do that linking by hand; for the rest, follow the symbol back
to the doctrine pillar yourself.

Two things a symbol page still does not contain, and both are the part such a
page is usually judged on. Defaults are not listed, because a default lives in
the function that applies the type rather than in the type. Nor is the set of
components and functions that consume the symbol, which is the part a type
reference usually omits and the part you usually need. Both need a type checker
rather than a textual scan, and until the generator grows one, the declaration
and its file are what these pages promise.

<Todo owner="engineering">
  List defaults and consumers on the per-symbol pages.
  It needs 

  `build-reference.mts`

   to read the declarations through a TypeScript
  program instead of a line scan. Members and unions are expanded already, by
  capturing the declaration to its closing brace or its last case.
</Todo>

### What exists today [#what-exists-today]

A page for every `type` and `interface` under `lib/`, each one written by
`pnpm run generate` and replaced wholesale on the next run. That covers the
colour model, the status and category vocabulary, the catalogue, preset and
registry shapes, and the error types. The full list, with the functions and constants that do not get
a page, is [Types](../generated/types.mdx).

Two of them predate the generator and are still half hand-written above their
generated block:

* [ClinicalStatus](./ClinicalStatus.mdx), the four clinical status levels
* [HealthCategory](./HealthCategory.mdx), the subject-identity categories

Their prose was written as a specification, before the types were declared, and
it is still marked as one. Where that prose and the generated block below it
disagree, the generated block is what the code says and the prose is what was
intended. No other symbol page here has two halves like that; every other one is
generated end to end.
