# Every key, one page.

> The full ApiGlow config reference: every key of the inline JSON block, its default, and the per-spec override rules for multi-spec installations.
>
> Source: https://apiglow.dev/docs/configuration/index.md


The config lives in the host page, in a `<script id="api-doc-config" type="application/json">` block — or in the `window.API_DOC_CONFIG` global when you want comments around it. Only `openapi.url`, `spec` or `specs` is required; every other key has a default.

Keys are grouped below as the config groups them. Defaults are stated where one exists, as of v{{< app-version >}}.

The whole object in one copyable block, one comment per key, is on [Full reference](/docs/full-reference/) — and [the builder](/docs/builder/) writes it for you, station by station.

## `openapi`

| Key | What it does |
|---|---|
| `openapi.url` | URL of the OpenAPI 3.0.x–3.2.x schema, JSON or YAML. The hosting server must allow cross-origin reads ([CORS](/docs/cors/)). |
| `openapi.spec` | The schema itself — JS object or JSON string — carried by the page: no request, no CORS. Wins over `url`. |
| `openapi.specs` | Several schemas in one installation: `[{ id, title, url }]` entries, almost every key overridable per entry. Wins over `url`. See the [multi-spec guide](/docs/multi-spec/). |
| `openapi.default` | The spec shown on arrival in multi-spec (first entry otherwise). |
| `openapi.hide` | Patterns removing operations from nav, search and exports: `"tag:Internal"`, `"DELETE /admin/*"`, `"/admin/*"`, `"resetDatabase"` (operationId — the fallback id `{method}-{path-slug}` matches too). Documentation hiding, not security — the browser still downloads the full schema. See [OpenAPI support & schema control](/docs/schema-support/). |
| `openapi.overlays` | OpenAPI Overlay 1.1 documents (URL or inline object) applied to the schema at load, in declaration order. Failed actions are reported, never silent. Full semantics in the [schema guide](/docs/schema-support/). |
| `openapi.userOverlay` | A starting patch seeded into the reader's own [overlay editor](/docs/schema-support/#when-the-reader-is-the-one-who-is-stuck) (default `null`). Per spec it replaces the root's document rather than stacking, and re-seeding a different document discards the reader's local edits. |

## `theme`

| Key | Default | What it does |
|---|---|---|
| `theme.default` | `"system"` | Theme applied on first load; the reader's persisted choice then takes priority. `"system"` follows the OS `prefers-color-scheme` within the first light/dark pair of `available`. |
| `theme.available` | `["apiglow", "apiglow-dark"]` | Themes offered in the selector — the default is the app's signature pair. All 35 standard daisyUI themes are bundled, so listing one costs nothing. |
| `theme.custom` | — | Your own themes, generated at boot from daisyUI tokens — no build step. Root-only in multi-spec. See the [theming guide](/docs/theming/). |

## `language`

| Key | Default | What it does |
|---|---|---|
| `language.default` | `"browser"` | UI language on first load: `"browser"` follows the reader's `navigator.languages` within `available`; a language code pins it. The reader's persisted preference wins afterward. |
| `language.available` | — | Languages offered. English ships in the bundle; the others download on demand. |

## Environments

| Key | Default | What it does |
|---|---|---|
| `environments` | `[]` | Environments pre-filled on first load: `{ name, baseUrl, variables, defaultHeaders }`. A variable is `{ value, sensitive }`; sensitive ones are masked on display and redacted in history and exports. After first load, the reader's own storage is authoritative. |
| `environmentsLocked` | `false` | `true` removes every UI entry point to create or edit environments — only the selector remains. For controlled corporate deployments. |

## Content: `docsPages`, `feedback` and `scenarios`

| Key | Default | What it does |
|---|---|---|
| `docsPages` | `[]` | Prose pages woven into the nav: `{ slug, title, url }` — or `content` (the text itself) or `contentId` (id of a non-executable script element in the host page) instead of `url`. Add `kind: "changelog"` to render a page's `h2` headings as a release timeline; `nav: "bottom"` on a top-level entry sends it to the appendix zone below the API reference. Groups, external links, per-language maps, a `home: true` takeover and a JSON-manifest URL form are all covered in the [docs pages guide](/docs/docs-pages/). |
| `feedback.url` | `null` | Your endpoint for the "Was this page helpful?" row on docs pages, which POSTs `{ "page": "<slug>", "verdict": "up" \| "down" }` as JSON. No URL, no row — the app never sends anything anywhere on its own. Cross-origin, the JSON content type makes it a preflighted request. Overridable per spec. |
| `scenarios` | `[]` | Scenarios shipped with the docs: `{ id, title, url }` pointing at exported files; `pinned: true` features one on the home page. In multi-spec, declared only on `specs[]` entries. See [authoring scenarios](/docs/scenarios/). |

## `announcements`

New in v0.2.0: a strip above the header, for what the schema cannot say — a maintenance window, a deprecation date, a version that just shipped.

| Key | Default | What it does |
|---|---|---|
| `announcements` | `[]` | Either an array of entries, or the URL of a file holding `{ "announcements": [ … ] }` — the second form is the one your ops team edits on its own, with no redeploy of the host page. In multi-spec, root and spec entries accumulate; a named file replaces what the config declared. A file that fails to load is dropped silently, since a banner nobody receives is invisible either way. |
| `announcements[].text` | — | The message, as inline Markdown: a string, or one per language (`{ en: "…", fr: "…" }`). Sanitized like every other external content. |
| `announcements[].level` | `"info"` | `"info"`, `"success"`, `"warning"` or `"error"`. |
| `announcements[].dismissible` | `true` | `false` keeps the notice on screen whatever the reader has already waved away. |
| `announcements[].startsAt` / `.endsAt` | — | ISO instants. The window is read when the page renders, so a maintenance notice declared a week ahead publishes and retires itself — but an entry whose window opens while a tab is left open waits for the next load. A date that cannot be read widens the window instead of closing it. |
| `announcements[].id` | — | The key the reader's dismissal is remembered under. Without one, the declared text is fingerprinted: editing the message shows it again to everyone, and giving it an `id` buys the opposite — a typo fix that re-opens nothing. |

## `features`

| Key | Default | What it does |
|---|---|---|
| `features.scenarios` | `true` | `false` removes the scenarios feature entirely — nav, capture buttons, routes, search index. Stored local scenarios survive, intact, for when it is re-enabled. |
| `features.audit` | `true` | `false` removes the [schema audit](/docs/audit/) page: the settings block, the `#/audit` route, every computation. The [command line](/docs/audit-ci/) ignores it. |
| `features.onboarding` | `false` | `true` adds a generated "First call" page (`#/first-call`) at the top of the reference nav: the simplest GET the schema declares, walked through in the real try-it — language, credentials, Send. Absent when the schema declares no such read. See [Auth & onboarding](/docs/auth/). |
| `features.ci` | `true` | `false` removes the "Automate this scenario" panel — the [CI hand-off](/docs/scenarios/#hand-it-to-your-ci). The Arazzo export in the scenario menu stays. |

## `audit`

The audit's rule configuration, read by the `#/audit` page and by [`apiglow audit`](/docs/audit-ci/) alike, so the published grade and the CI one agree. A grade computed with it is labelled "custom rule set" wherever it appears. The [audit guide](/docs/audit/#configure-the-rules) has the precedence rules.

| Key | Default | What it does |
|---|---|---|
| `audit.rules` | `{}` | Rule id → `"error"`, `"warning"`, `"info"` or `"off"`, for the whole document. `apiglow audit --list-rules` prints every id. |
| `audit.overrides` | `[]` | The same settings under JSON pointer patterns: `{ paths, rules, reason }`. `*` matches within one segment, `**` any number of them; the last match wins. `reason` is free text for the next reader. |

A wrong entry is named in the browser console and left out; the command line refuses to run on it.

## `branding`

| Key | Default | What it does |
|---|---|---|
| `branding.productName` | — | Name shown in the header. |
| `branding.logoUrl` | `null` | URL of a logo shown in the header. |

## `tryIt`

| Key | Default | What it does |
|---|---|---|
| `tryIt.proxyUrl` | `null` | Optional CORS proxy template, `{{target}}` = encoded target URL. The app ships no proxy — you host it. |
| `tryIt.requestCredentials` | `"same-origin"` | Credentials mode of the try-it's fetches: `"omit"`, `"same-origin"` or `"include"`. `"include"` is required for cross-origin session-cookie auth, with server-side constraints detailed in [Try-it & CORS](/docs/cors/). |

## `history`

| Key | Default | What it does |
|---|---|---|
| `history.maxEntries` | `500` | Purge the request history beyond this count (whichever threshold hits first). |
| `history.maxAgeDays` | `30` | …or beyond this age. Root-only: retention is a browser storage cap, shared by all specs. |

## `oauth`

| Key | Default | What it does |
|---|---|---|
| `oauth` | `{}` | Per OAuth2 scheme (key = its name in `securitySchemes`): `{ clientId }` used by the in-browser flows, overridable by the `auth.X.clientId` environment variable. Never a client secret — the host page is public. |

## `seo`

| Key | Default | What it does |
|---|---|---|
| `seo.index` | `true` | `false` injects `<meta name="robots" content="noindex">` before first paint. Root-only: one page is served at one URL, and a crawler reads it without choosing a spec. It is a request to well-behaved crawlers, not a protection. Add an `X-Robots-Tag` header to cover non-HTML files. See [SEO & the bake CLI](/docs/seo/). |

One capability has no key at all. **Host-provided credentials** are a runtime-only API (`window.apidoc`), so a token cannot end up written in the config — see the [host credentials bridge](/docs/auth/#when-the-host-page-already-has-a-session). A reader's [local schema patch](/docs/schema-support/#when-the-reader-is-the-one-who-is-stuck) is nearly as private: `openapi.userOverlay` seeds its opening state, and there is no flag to disable it. It changes one browser's view of the document, and your `hide` rules still apply after it.

## Per-spec overrides (multi-spec)

In an `openapi.specs[]` installation, the root config is the fallback and nearly every key can be redeclared on an entry. Four rules decide the merge:

- **Settings objects** (`tryIt`, `branding`, `theme`, `language`, `features`, `oauth`) merge key by key and the spec wins. A key declared as `null` wins too, so a spec can disable the root proxy. `audit` merges its `rules` the same way and accumulates its `overrides`, root first.
- **Named lists** (`docsPages` by slug, `environments` by name) merge by identifier, and the spec wins.
- **`hide` and `overlays` accumulate.** Hiding cannot be un-hidden; overlays apply root first, then the entry's own.
- **`environmentsLocked`** is replaced when declared.

Four keys are exceptions. `history` is root-only: retention is one browser storage cap for all specs. `theme.custom` is root-only: themes are injected once at boot. `seo` is root-only: indexability describes the served page, which all specs share. And `scenarios` go the other way — declare them only on `specs[]` entries, since a scenario references operations of one specific spec. Each case is named in the console when a config gets it wrong.

