Theme a documentation space
theme.docs is the part of a space's theme that only a Documentation space reads: how its navigation, header, page, blocks, code, API reference and page foot look, beyond the keys every space shares. It lives inside the same theme you already set with PUT or PATCH /orgs/{orgId}/spaces/{spaceId}/theme.
Setting
theme.docson a space of any other type is refused with a422. A site theme may carry it; it then reaches the site's documentation spaces.What a reader renders is
docsThemeOf(theme): the documentation defaults, under the site's theme, under the space's own. Every key you set wins.Unset on a documentation space means the documentation default — the
cleanlook — for every documentation space, including ones created before 2026-10-12 that never set these keys.look: "paper"brings back the previous look in one key.GET …/themealways answers with what you stored, never the resolved theme, so you can send it back whole without freezing the defaults into it.
Navigation
Sidebar, sections and phone nav
Four keys: sidebar, sections, mobileNav and homeAt. Every one is optional; leaving one unset renders the rail as it was before it existed. A colour is a hex or one of brand, ink, muted (which follow dark mode); …Dark keys are the dark-mode colour.
{
"docs": {
"sidebar": {
"groups": "collapsible",
"groupLabel": { "case": "none", "weight": 600, "size": 14, "color": "ink" },
"chevron": "end",
"active": { "style": "fill", "color": "brand", "background": "tint", "weight": 600 },
"width": 272,
"search": "auto",
"methodBadges": true
},
"mobileNav": { "style": "drawer", "label": "section", "bar": true },
"homeAt": "root"
}
}sidebar
Key | What it does |
|---|---|
|
|
|
|
|
|
| One row: |
| The current page's row: |
|
|
|
|
| A rule down each collection's list; a hairline between collections. |
| The rail's width, 200–360 px (default 240). Published as |
| The rail as its own full-height column, from 768px up. |
|
|
| Up to 8 quick links ( |
| Links, and a live status pill (the footer's |
| Every collection is its own scope (see a collection's |
|
|
| The rail search box's height, 28–48 px (unset: today's 32px plain box; the defaults set 36). Setting it dresses the box like the header's: a magnifier, the |
| A button folds the rail away on a wide screen; remembered per browser. |
| HTTP method pills on API reference pages. |
|
|
| The rail search box's key hint: |
|
|
|
|
| What a collection's heading in the rail does. Unset ( |
sections
Top-level sections that scope the rail, previous/next and search to a group of collections.
{
"sections": {
"placement": "sidebar",
"style": "card",
"items": [
{
"key": "guides",
"label": "Guides",
"icon": "book",
"collections": ["getting-started", "guides"]
},
{ "key": "api", "label": "API", "icon": "code", "collections": ["reference"] },
{ "key": "blog", "label": "Blog", "href": "/blog" }
]
}
}placement:sidebar(default) — the switcher at the top of the rail.header— the items as tabs in the header's sub-row.headerLinks— no switcher of its own: yourheaderLinks(orchrome.subnav.links) are the switcher. Give each link amatch—{ "collection": [ids] }with the ids of the section's collections, or apathPrefix— and it is marked current on that section's pages; the rail, previous/next and search scope exactly as with the other placements. Use this when the header already has one link per section, so the sections are not drawn twice.none— the sections scope the rail, previous/next and search, and no switcher is drawn anywhere.
style:tabs,card(default in the rail),selectordropdown.default: the itemkeyaselectordropdownswitcher names itself after on a page that is in no section (a composed front page). Unset, the first item — it never reads "Sections". Nothing is marked current either way.{ "sections": { "style": "dropdown", "default": "embers", "items": [ … ] } }An item names collections by slug. One with an
hrefand no collections is a plain link — another space of the site, say — and is never "current".keyis lowercase letters, digits and dashes.
mobileNav
Key | What it does |
|---|---|
|
|
|
|
|
|
| A sticky "≡ Section › Page" row under the header, flush against it. |
|
|
|
|
|
|
The drawer opens scrolled to the page you are reading — scrollToActive: false opens it at the top instead. Its head row stays put while the tree scrolls, its search and light/dark control share one row, and the rail's sidebar.footerLinks are listed at its foot — with sidebar.footerPin: true held at the drawer's bottom edge under the rail's rule while the tree scrolls (the same key as the wide rail's pin; there is no separate phone key). With sections.placement: "headerLinks" and the same links in chrome.subnav, the drawer lists those links once (the current one marked) instead of a second "Sections" card; with sections.style: "select" the drawer shows the select.
Key | Values | Unset |
|---|---|---|
| boolean |
|
{ "docs": { "mobileNav": { "style": "drawer", "sections": "top", "width": "full", "head": true } } }mobileNav.label: "trail" (scope: theme; no layer default — the defaults set section) brings back the full trail on a drawer opener without the sticky bar, and it works under every look:
{
"docs": {
"mobileNav": { "style": "drawer", "label": "trail" },
"sidebar": {
"footerLinks": [{ "label": "Contact support", "href": "/support" }],
"footerPin": true
}
}
}homeAt
root renders the space's home page at the space's own address instead of redirecting to …/{slug}. The page's slug address keeps working and names the root as canonical.
Switchers in the rail
The version, language and variant switchers are configured under Footer, versions and SEO; three keys there shape how they sit in the rail:
Key | Values | Unset |
|---|---|---|
|
|
|
|
|
|
|
| between the search and the tree |
| A glyph name, an image or an emoji, drawn 20px by the | none |
|
|
|
|
|
|
With placement: "rail" and position: "top" the switcher sits at the top of the page's right rail, above "On this page".
{
"docs": {
"variantSwitcher": {
"syncKey": "fw",
"style": "card",
"position": "top",
"options": [
{ "value": "next", "label": "Next.js", "icon": "code" },
{ "value": "remix", "label": "Remix", "icon": "book" }
]
},
"versionSwitcher": { "style": "select", "labelPlacement": "above" }
}
}Unless show is always, a variant select is drawn only on a page that has tabs (or a code group) with its syncKey — on any other page it would switch nothing. The switchers' select takes components.input.radius.
header.splitRule
With header.layout: "rail-split", splitRule: false drops the rule that closes off the brand cell (unset: drawn). { "docs": { "header": { "layout": "rail-split", "splitRule": false } } }
A collection's own rail settings (collapsed, icon, linkHub, hubCards, navScope, backStyle) live on the collection — see Authoring reference → Collections. backStyle shapes an isolated scope's head: link (unset) is "‹ All docs" then the collection's name; title is one centred "‹ Functions" row back to the scope above it (the section, when there is one) — PATCH /orgs/{orgId}/spaces/{spaceId}/collections/{collectionId} with { "nav": { "navScope": "isolated", "backStyle": "title" } }. Page keys (icon, navTitle, badge, navKind, navFont) are page metadata.
Theme components can fill two rail outlets: docs-sidebar-start and docs-sidebar-end.
hub — a collection's landing page
theme.docs.hub shapes every collection's landing page (…/c/{slug}). Every key is optional; unset is the designed default.
Key | What it does |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Mix freely: { "docs": { "hub": { "layout": "list", "updated": false }, "sidebar": { "groupLink": "toggle" } } } is a quiet list with fold-only headings; { "docs": { "hub": { "columns": 3, "descriptions": false } } } is a dense grid of titles. Every part carries a data-hub-* attribute (data-hub-entry, data-hub-entry-meta, data-hub-group, …) for custom CSS.
Header and search
Every rail link (sidebar.anchors[], sidebar.footerLinks[]), header link, button and block takes audience (all / signedIn / signedOut), resolved before first paint — a "Sign up" button is for signed-out readers only by default. See "Who sees what" in the theming guide for the whole list, the account menu (chrome.accountMenu) and the reader's /me page.
Key | Values |
|---|---|
|
|
| ≤ 40 chars — the launcher's words. Default "Ask AI", translated |
| boolean — ⌘I / Ctrl+I opens the panel, and the launcher shows the hint |
| boolean — an "Ask AI about …" row at the top of the search modal at any placement |
|
|
|
|
The launchers only show on a page where Ask AI is on, so a header shared with a blog keeps them off the blog. With rail-split, pair chrome.searchPlacement: "start" and chrome.search: "icon" so the search lands inside the brand cell, and leave layout.frame unset so the header row and the docs row line up.
The search modal itself is a chrome key: chrome.searchStyle.open: "modal" (see Header and footer).
The header's own chrome keys a documentation header most often wants (2026-10-12; the full table is in the theming reference):
Key | Values |
|---|---|
|
|
|
|
|
|
| the pill's width from 768px; it now gives way (down to 160px) before the buttons or "Log in" are crowded out |
|
|
|
|
| hex — the link's own colour and outline (a grey pill instead of the brand); |
|
|
|
|
|
|
|
|
|
|
|
|
| the end group's slots in order: |
|
|
|
|
|
|
|
|
|
|
|
|
| also |
| up to 4 items at the end of the section row, dropdowns allowed |
|
|
|
|
{
"chrome": {
"brandText": "auto",
"search": "pill",
"searchPlacement": "center",
"searchStyle": { "width": 420, "kbd": "mod-k", "open": "modal" },
"header": { "linksAlign": "after-search" },
"headerLinks": [{ "label": "Ask AI", "href": "/ask", "kbd": "mod-i", "phone": "bar" }]
},
"announcement": { "text": "v5 is out", "placement": "below-header", "span": "content" }
}Header fixes that need no key: below 420px at most one filled button stays in the phone row (the first one pinned there; the rest are in the menu); no header button or sign-in label wraps onto two lines; a centred search is a search glyph on one phone row (never a second row for the menu); the phone bar never shrinks the brand or a brand-placed version pill — the search folds to a glyph first, then buttons move into the menu; and under sidebar-first the rail's column reaches the bottom of the window on a short page.
{
"chrome": {
"search": "pill",
"searchPlacement": "start",
"searchStyle": { "label": "", "kbd": "mod-k", "kbdStyle": "plain" },
"header": {
"linksAlign": "after-search",
"paint": "brand",
"order": ["links", "search", "ctas"]
},
"logoPhone": "mark",
"subnav": { "endLinks": [{ "label": "Help", "href": "/help" }] }
},
"announcement": {
"text": "v5 is out",
"lead": "New",
"placement": "below-header",
"span": "content",
"phone": "lead"
}
}Search hits from the public search (GET /public/orgs/{orgId}/spaces/{spaceId}/search) carry collection ({id, name}, or null for a top-level page) and headings (up to three {text, anchor, level} that match — link to url#anchor). The search takes collections=<id>,<id> to keep hits inside those collections.
A page's metadata.searchExclude: true leaves it out of the reader search and the site search; it is still published, linked and in the sitemap (use noindex for search engines). Ask AI and the console search still find it.
Page
How the page head, the article column and the right-hand rail read. Every key is optional; unset draws the page as it was.
Key | Values | What it does |
|---|---|---|
|
|
|
|
| The space's default article width. A page's own |
| boolean | | The 2px rule under the header. |
|
| The standfirst under the title — |
|
| A trail above the title: the collection (unless |
|
| A small line above the title: the collection's name, or the trail |
|
| How the kicker looks. Unset: 12px, semibold, uppercase, the eyebrow colour |
|
| Where "Updated …" and the contributors sit. Unset = |
|
| "Mar 4, 2026", "3 days ago", or the date with the time |
| boolean | The contributors beside the date. Unset = shown |
| boolean | | A hairline under the page head. |
| 0–48 | The gap between the title and the standfirst, in px. Unset = about 4px |
| boolean |
|
|
| Where the actions bar goes. |
| text, ≤ 40 | A small mono heading over the actions where they sit in the rail ("AI tools") |
|
|
|
| 1–8 of | Which targets, in order. Unset = copy, markdown, ChatGPT, Claude, MCP. |
| one of the items | The one shown first (alone, in |
|
| Under 640px, |
|
| Which heading levels the on-this-page rail lists. Unset = h2–h3. A page's |
| boolean | Also list stepper step titles |
| text, or | The rail's heading; |
|
| The label's look |
| 11–18 | The entries' size in px |
| #hex / #hex / | The current entry: a bar (default), coloured text only, or a soft fill |
| up to 4 of | What stacks under the rail, in this order. The rule between them and the contents list is drawn only when the list is (a page with too few headings has none) |
|
|
|
|
| Under 1280px, where the rail is hidden: nothing (default), a collapsible list, or a "Jump to a section" select — after the whole page head (title, standfirst or lede, the meta row and byline), 16px below it. |
|
| An inset surface around the article (and the rail, with |
|
| The lifted aside column (a |
onThisPage: "always" now means it: one heading is enough for a rail, and a page with no heading at all drops the empty column.
Page metadata
Set with PATCH …/pages/{pageId}/metadata.
Key | Values | What it does |
|---|---|---|
| boolean | Hides the title on screen; it stays the page's |
| ≤ 300 chars | The standfirst, when it should differ from |
| boolean | No "Updated …" on this page |
| boolean | Keeps |
|
|
|
|
|
|
|
| The splash hero. |
|
| This page's rail depth |
| boolean |
|
|
| Centres the page head |
|
| The hero's alignment below 768px, where a split hero stacks. Unset = follows |
|
| This page's own slice of the theme — see Per-page theme below |
Per-page theme (metadata.themePatch)
Scope: page. One page can wear a different block look or page head than the rest of the space — a landing whose cards are tiles while every guide keeps the outlined card, or one long reference page with a deeper contents rail. The patch takes any theme.components.* key and the docs page keys pageHead, toc, articleWidth, pageActions, frame, panel, feedback, pager, api.titleSize and blocks.prompt.icon, with exactly the shapes those keys have on the space theme. It is merged over the space theme for that page only (its CSS is scoped to [data-docs-page="<page id>"], so a neighbour never inherits it after a client-side navigation). Unknown keys are refused with a 422 naming them; the patch is replaced whole on each write and null clears it. Unset = the page follows the space.
PATCH /orgs/{orgId}/spaces/{spaceId}/pages/{pageId}/metadata
{ "themePatch": { "components": { "card": { "border": "tile" } },
"docs": { "toc": { "levels": { "min": 2, "max": 4 } }, "pageHead": { "rule": "column" } } } }Where a value comes from, highest first: block attribute (a cards block's own border) → page themePatch → space theme → the look's patch → the built-in default.
{
"docs": {
"articleAlign": "start",
"topRule": false,
"pageActions": {
"placement": "title-end",
"style": "split",
"primary": "copy",
"phone": "icon"
},
"pageHead": { "updated": "footer", "breadcrumb": { "separator": "chevron" } },
"toc": { "labelCase": "none", "labelSize": 13, "steps": true, "phone": "disclosure" }
}
}Stable hooks for customCss: [data-docs-crumbs], [data-docs-kicker], [data-docs-title-row], [data-docs-head-rule], [data-docs-top-rule], [data-docs-rail] (toc | aside | pane), [data-docs-rail-extras], [data-docs-aside], [data-docs-hero], [data-docs-panel], [data-docs-title-float] (a hidden title's actions), [data-docs-rail-actions-label], [data-docs-phone-bar], [data-docs-header-head] (the crumb and actions in a rail-split header), .page-actions-updated.
{
"docs": {
"pageHead": {
"titleGap": 16,
"icon": false,
"rule": "content",
"updated": "actions",
"breadcrumb": { "show": true, "placement": "header" }
},
"pageActions": { "placement": "header", "style": "split", "phone": "bar", "label": "AI tools" },
"toc": { "extras": ["feedback", "actions"], "extrasPlacement": "before" },
"panel": { "phone": true, "radius": 12, "inset": { "top": 0, "right": 8, "bottom": 8 } }
}
}Round 4 page keys
Every key optional; unset = today's page. Scope theme means theme.docs.* (also accepted per page inside metadata.themePatch.docs); page means metadata.*.
Key | Scope | Shape | Default | Example |
|---|---|---|---|---|
| theme / page |
|
|
|
| theme / page |
|
|
|
| theme / page | see | — |
|
| theme / page | see | — |
|
| theme / page |
|
|
|
| theme / page |
|
|
|
| theme / page | boolean / #hex / #hex | none / the hairline colour |
|
| theme / page |
|
|
|
layout.guides (scope theme — the hairline column guides behind every reader page: columns, width, color, style, outer, gutter) gained two keys that matter on a docs page: layout.guides.gutter.inset (0–96 px, unset 0) is the space between each gutter band and the text — on a docs page the bands hug the article, so this is how far from the prose they sit; layout.guides.placement (content | page-edge, unset content) pins the END band to the page's far edge. Example: { "layout": { "guides": { "columns": 1, "gutter": { "pattern": "stripes", "inset": 48 }, "placement": "page-edge" } } }.
What every page now does, with no key (fixes, not options):
No breadcrumb on the home page. The trail there was the page's own name.
pageHead.breadcrumb.home: truebrings it back. The home is also the page the space root sends a reader to when nothing names one (the first page), and a step that repeats the next ("Payments › Payments") is dropped.The phone contents list follows the whole page head (
toc.phone) — the title, the standfirst or a large opening paragraph, the meta row — with a 16px gap, not the title alone (it read as the page's first paragraph).toc.phonePlacementis the way back to "under the title".No background guide hairlines on a phone. Below 768px
layout.guidesdraws neither the gutter bands nor the 1px rules.With the phone bar drawn, a hidden title's floating actions row takes no height and the gap from the bar to the title is 16px.
A
hideTitlepage keeps its title-row actions, at the article's top end, and the contents list skips the heading that stands in for the title (a heading markeddata-hero-heading, a heading inside[data-docs-hero], or the heading that leads the body of ahideTitlepage).Tables never crush. A cell in a table's scrolling frame keeps its words whole (columns at least 6ch, header cells on one line); a wide table scrolls inside its frame instead of breaking "Required" letter by letter.
Code is set in the system code face, not Courier:
--font-monodefaults toui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospaceon the reader, andmonoFontFamilystill wins.Four cards go four across (from a 720px track; two by two below it) instead of three and an orphan — when the cards block sets no
columnsand is not theroworhorizontallook.
Round 5 page keys
Same rules as round 4: every key optional, unset = today's page; scope theme is theme.docs.*, also accepted per page in metadata.themePatch.docs. "Layer" says whether the DOCS default layer sets it.
Key | Scope | Shape | Default | Layer | Example |
|---|---|---|---|---|---|
| theme / page | a new value of | — | no |
|
| theme / page | #hex | the line colour | no |
|
| theme / page |
|
| no |
|
| theme / page | #hex |
| no |
|
| theme / page | px 11–16 |
| no |
|
| theme / page |
|
| no |
|
| theme / page |
|
|
|
|
What every page now does, with no key (round 5 fixes):
The phone text row never starts a line with a divider. With
pageActions.phone: "text"below 640px, "Last updated" (pageHead.updated: "actions") takes its own line, and the actions are ONE nowrap row of the first two items; the rest open from the row's menu glyph. The count is made on the server, so the row never reflows.
Cards, tables, stepper and callouts on the page
These are theme.components.* keys, so they apply to every space type. All optional; unset draws today's block.
Key | Values | What it does |
|---|---|---|
| #hex | Fills every card |
|
|
|
|
|
|
|
|
|
|
| A collapsed table's "Show more": the outline pill (unset) or a solid brand button, at the start or centred, and |
| up to 6 #hex | Inline code colour per column, first column first — a class column in one ink, a value column in another |
| 16–48 | The marker's diameter in px. Unset = 25.6 |
|
|
|
| 0–24 | The gap under that callout style's title, in px (also published as |
{
"components": {
"card": {
"background": "#f6f7f9",
"backgroundDark": "#16181d",
"iconAlign": "start",
"border": "tile"
},
"table": {
"more": { "style": "solid", "align": "center", "fade": true },
"codeInk": ["#0284c7", "#7c3aed"]
},
"stepper": { "markerSize": 24, "markerAlign": "title", "titleSize": 24 },
"hint": { "variants": { "info": { "titleGap": 4 }, "warning": { "titleGap": 4 } } }
}
}Blocks
Package D adds no theme.docs keys: the docs blocks' looks are theme.components.* (table, stepper, card, kbd, tabs, hint.variants, expandable.*Dark) and apply to every space type — see theme-components.md › "Docs blocks (wave D)".
One key sets a block's look for the whole space (round 3):
Key | Values | Default | What it does |
|---|---|---|---|
|
|
| The look a |
|
|
| Round 4: |
|
| neither | Round 4: that action is drawn filled in the brand. |
|
|
| Round 4: the copy control as the glyph alone. |
| 1–6 | 1 under | Round 4: the "Open in …" links a |
| 0–6 | all (today). The DOCS layer sets | Round 5: the "Open in …" links any prompt keeps in its row from 641px; the rest fold into a More menu drawn on the server (a native disclosure: Enter/Space open it, Escape closes it). Example: |
| a glyph name, or a short glyph | none | Round 5: drawn before the label of every prompt that names no |
|
|
| Round 5: |
| #hex | today's surface | Round 5: the prompt's fill, light and dark. A prompt's own attrs win. |
|
|
| Round 5: a folded prompt ( |
| 10–20 (px) | 11.2px (0.7rem); 14px on a | Round 6: the label's size. Example: |
| 100–900 | 600 | Round 6: the label's weight. |
| #hex | the muted grey (ink on a | Round 6: the label's colour, light and dark. |
| boolean | drawn | Round 6: |
|
| inline: Copy beside the label, "Open in …" at the end; stacked: from the start | Round 6: where Copy and "Open in …" sit in their row, as a group. Card rows only (a one-row |
{ "docs": { "blocks": { "prompt": { "variant": "pill" } } } }The blocks gained these attributes in round 3, each optional and each drawn only when set (the full table, with values, is in Authoring › Round 3 block attributes):
tabs:asCodeGroup(unset: a group of nothing but code blocks draws as a code group;falseopts out) andvariant: "card".prompt:variant: "pill",align,textStyle: "prose",collapse, and{icon, label}logos intargets.heading:icon,iconStyle(inline/tile),lineHeight(0.9–2). On ahideTitlepage the first heading is markeddata-hero-heading, and the rail skips it.gridItem:textAlign,icon,iconStyle; cells stretch to their row.section:pattern(dots/grid),patternColor,patternFade(radial/bottom).
Their hooks for elementStyles / custom CSS: .blk-icon[data-icon-style], .forumulate-prompt[data-variant="pill"], .prompt-logo, .prompt-more, .tabs[data-variant="card"], .doc-section[data-pattern], [data-hero-heading].
Code
Code has no theme.docs keys: everything about a code block lives in components.code / components.codeDark, which every space type reads — the copy button (copy), blocks and inline code painted separately (block, inline), the frame look, the language label, wrapping, terminal bars and the line colours. See "Code blocks and inline code separately" in the theming reference.
Key | Values | What it does |
|---|---|---|
|
| The code group's tab labels (and its dropdown picker): |
{ "components": { "code": { "tabFont": "ui" } } }Round 5 (R5-005, a fix): a code group's tabs already name each sample, so a showLanguage: true that comes from the THEME (the DOCS default layer sets it) no longer draws a language chip inside a code group — a standalone block keeps its chip. A group (or a block in it) that sets showLanguage: true itself still gets one, and the theme value "always" draws it everywhere, groups included: { "components": { "code": { "showLanguage": "always" } } }. The DOCS layer also sets components.code.tabFont: "ui", so a group's tab labels read in the interface face.
The language chip never covers code (a fix): a block whose first line runs under the chip reserves the chip's width on that line, and a terminal block carries the chip in its bar.
What a documentation space gets when those keys are unset is its DOCS default: an icon copy button, a pale bordered block (#fafafa / #e5e7eb, radius 10; #16181d / #2a2e35 in dark), a grey inline chip, the language label shown, and the github code theme when you named none. Every key you set wins.
API reference
Owner: package F. All under theme.docs.api; every key unset draws today's reference. (The DOCS defaults — split, landing, list — arrive with the defaults flip, not here.)
Key | Values | Unset | What it does |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
| Where the SDK download panel mounts. |
| up to 10 of | the first seven | Which sample languages, in this order. |
| one of the above | — | The tab shown first, until the reader picks one (their pick is remembered). |
|
|
| A select instead of a tab strip. |
|
|
| The language strip's look. |
| boolean |
| A numbered gutter on every sample — the code block's own (numbers are never copied). |
|
|
|
|
| boolean |
| Open the console on load. |
| boolean |
| Start parameters at the spec's examples. |
|
|
|
|
| boolean |
| An "OpenAPI spec" download link in each operation's head row ( |
| boolean |
|
|
| 12–18 (px) | today's sizes | The size of descriptions in parameters, fields, the body and responses. The DOCS default layer sets |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Below 768px: |
| boolean |
|
|
|
|
| Round 5: the address below 768px. |
| a new value of | — | Round 5: the address as bare mono text — no chip, no border, no fill. With |
| px 20–48, or | the theme's h1 | Round 5: the page title's size on an OPERATION page (one carrying an API operation block) — guide pages are never touched. |
|
|
| Round 5: |
| #hex | a 4% ink wash | Round 5: the panel's surface, light and dark. |
| #hex | the reader ink | Round 5: the response body's text on the panel. The code blocks' ink is often meant for a dark block, so the panel no longer borrows it. |
|
|
| Round 5: |
All of these are scope theme (theme.docs.api); a block's own layout and params attrs still win over the theme's.
The sample bar. Every operation's code samples sit under ONE header bar (~36px), like a code group: the language tabs at the code header's 13px (in components.code.tabFont's face, via --code-tab-font), and at the bar's end the copy control — plus an Ask-AI button when the space has an Ask-AI panel. The copy control follows components.code.copy, the same key the code blocks read: icon (and unset) is a glyph, text the words "Copy"/"Copied", cell a full-height cell divided from the tabs, none (or components.code.actions without "copy") no button. Example:
{ "docs": { "api": { "examples": { "tabStyle": "pill", "lineNumbers": true } } } }Independently of these keys, every reference now: breaks the URL chip only at slashes, does not repeat the page title as a summary under the H1, renders descriptions as markdown, keeps the parameter table's Type/Required columns readable, and paints the SDK panel in the brand colour. Round 5 adds (fixes, no key): on a phone the operation head is ONE row — the method inline before the URL, the URL one line that scrolls in its own track with an end fade, never wrapped mid-path; in the split layout the parameter table fits its column (types wrap, "Required" never clips); and inside urlStyle: "pill" the path has no second inline-code fill — the pill is one flat surface. Round 4 adds: the samples are syntax-highlighted with the same highlighter and palette as a guide's code blocks (components.code.syntax / syntaxTheme, light and dark; curl is coloured as shell); the sample bar's language select, copy and ask controls take the code block's ink (components.code.block.ink), not the page's, so they read on a dark code bar over a light page; and inside a phone panel (panel.phone) the operation drops its own frame — no box in a box.
Mix and match: page and API reference keys are building blocks
Nothing on the page is tied to a look. A look (theme.docs.look) is only a PATCH of the individual keys documented above — it writes some of them for you, and every one of them works under every look, and under none. Each key has one scope: theme (theme.docs.*, theme.components.*, theme.layout.*), page (metadata.*, including metadata.themePatch), or block (a block's own attributes). The nearest scope wins: block → page → theme → look → default. Three combinations that are not any showcase replica, each a complete body:
A quiet reference with a loud landing. Stripe-style parameters and a path pill on the clean look; the space's home page alone gets tiles and no rail border. PATCH …/theme:
{
"docs": {
"look": "clean",
"pageHead": { "rule": "column", "breadcrumb": { "show": true, "phone": "parent" } },
"toc": { "border": true, "phone": "disclosure", "phonePlacement": "after-title" },
"api": {
"paramName": { "chip": false },
"textSize": 14,
"bodyHeading": "plain",
"url": "path",
"urlStyle": "pill",
"phone": { "samples": "first", "frame": false }
}
}
}then the home page's own metadata:
PATCH /orgs/{orgId}/spaces/{spaceId}/pages/{homeId}/metadata
{ "themePatch": { "components": { "card": { "border": "tile" } },
"docs": { "toc": { "border": false } } } }A paper handbook with a kicker and a phone that stays out of the way. The default (paper) look, the section as an eyebrow instead of a trail, the page actions as text links on phones and the contents control gone there:
{
"docs": {
"pageHead": {
"breadcrumb": { "show": true, "style": "kicker" },
"kickerStyle": { "font": "mono", "size": 11 },
"updated": "actions"
},
"pageActions": { "style": "split", "phone": "text" },
"toc": { "phone": "select", "phonePlacement": "hide", "extrasOnAside": "hide" }
}
}A design-system reference with ruled guides and a branded API card. Guides 48px off the text with the end band at the page edge, a content-wide head rule (the rail starts under it), the phone bar with an icon-only contents button, and the API card from components.apiRef:
{
"layout": {
"guides": {
"columns": 1,
"gutter": { "pattern": "stripes", "inset": 48 },
"placement": "page-edge"
}
},
"components": { "apiRef": { "methodStyle": "soft", "card": true } },
"docs": {
"pageHead": { "rule": "content", "breadcrumb": { "show": true, "phone": "collapse" } },
"pageActions": { "phone": "bar", "phoneToc": "icon" },
"api": { "layout": "split", "examples": { "picker": "dropdown" } }
}
}A changelog-grade API with a quiet head and a panel. Round 5's keys on the muted look: the head rule bleeding to the sidebar in a faint ink, the section as a sentence-case grey kicker, rail-less landings, text-only method and URL with a 26px operation title, responses as a panel, and every prompt folded to two assistants with a pill fold:
{
"components": { "apiRef": { "methodStyle": "text" } },
"docs": {
"look": "muted",
"pageHead": {
"rule": "bleed",
"ruleColor": "#ececec",
"ruleColorDark": "#26262b",
"breadcrumb": {
"show": true,
"style": "kicker",
"kickerCase": "none",
"kickerSize": 13,
"kickerColor": "#71717a"
}
},
"toc": { "onHero": "hide" },
"pageActions": { "phone": "bar", "phoneIcon": "primary" },
"api": {
"urlStyle": "plain",
"titleSize": 26,
"phone": { "url": "path" },
"responses": { "style": "panel", "header": "none" }
},
"blocks": {
"prompt": {
"openInDesktop": 2,
"collapseStyle": "pill",
"labelCase": "none",
"icon": "sparkles"
}
},
"feedback": {
"style": "labelled",
"third": true,
"labels": { "yes": "Yes, thanks", "no": "Not really" }
}
}
}Footer, versions and SEO
Every key is optional; unset renders what the reader rendered before the key existed, except where the documentation defaults say otherwise (see Look and defaults).
feedback
"Was this page helpful?"
{ "docs": { "feedback": { "style": "pill", "scale": 4, "align": "start", "placement": "rail" } } }Key | Values | Unset |
|---|---|---|
|
| shown |
|
|
|
|
|
|
|
| none |
|
| "Yes" / "No" / "I have feedback", translated |
|
|
|
|
|
|
| Up to 80 characters | "Was this page helpful?", translated |
| When the comment box opens: |
|
metadata.hideFeedback: true removes it from one page. The comments readers write are at GET …/docs/feedback and under Analytics in the console.
editLink, issueLink and suggestEdits
{
"docs": {
"editLink": { "urlTemplate": "https://github.com/acme/docs/edit/main/docs/{path}" },
"issueLink": {
"label": "Report a problem",
"urlTemplate": "https://github.com/acme/docs/issues/new?title={title}&body={url}"
},
"suggestEdits": { "show": true }
}
}urlTemplate is an absolute https URL. Its placeholders are filled and URL-encoded: {url} (the page's address), {title}, {slug} and {path} — the repository file a repository sync recorded in metadata.sourcePath, else <slug>.md (which is the file name our own export writes). For a GitHub sync the edit template is https://github.com/<repo>/edit/<branch>/<folder>/{path}. metadata.editUrl overrides the template for one page, and false hides the link there. label replaces "Edit this page" / "Report an issue".
suggestEdits.show offers "Suggest an edit" to signed-in members only; it opens the space's change requests in the console, where the usual eligibility check applies. Readers without an account cannot suggest edits.
pager
{ "style": "links" | "cards", "show": false } — previous / next at the page foot. cards draws each as a bordered box. Per page, metadata.hidePager and metadata.pager (prev / next as a page id or false, and label) override the tree's order.
versionSwitcher and localeSwitcher
{ "docs": { "versionSwitcher": { "placement": "header", "style": "select", "label": "Release" } } }Key | Values | Unset |
|---|---|---|
|
|
|
|
|
|
| Up to 40 characters, or | "Version" / "Language", translated |
A header placement falls back to the sidebar when the header has no slot for it. A version's badge and banner are set on the version itself (PATCH …/docs/versions/{key}, see the authoring reference). A version's address is /<key>/<page>, for example /v2/install. The older ?version=<key> form still works.
variantSwitcher
One control that switches every tabs block with the same syncKey — "show content for npm / pnpm / yarn" across the whole site:
{
"docs": {
"variantSwitcher": {
"syncKey": "pm",
"label": "Package manager",
"options": [
{ "value": "npm", "label": "npm" },
{ "value": "pnpm", "label": "pnpm" }
],
"default": "npm",
"placement": "rail"
}
}
}Pair it with tabs whose syncKey is pm and whose tab titles are the option labels (:::tabs{syncKey="pm"}). The choice is remembered across pages and written to ?tabs=pm:<label>. placement is sidebar (unset), rail or header. Showing whole blocks per variant is not a feature: use tabs.
localeFallback
"404" (unset) or "source". With source, a language address (/docs/es/<page>, or ?locale=es in query style) for a page that has no translation in that declared language serves the original under a "not translated yet" notice. Its canonical is the original. It is not noindex, because a canonical already says it is a copy. It is never in hreflang or the sitemap. See Language addresses.
Elsewhere
You want | Where it lives |
|---|---|
Site-wide meta tags |
|
Scripts on documentation pages | Theming, advanced → Which consent tier: scripts run only on a verified custom domain. |
Title pattern, site name, hidden pages in search |
|
Page keywords |
|
Look and defaults
What changed on 2026-10-12, and the way back
Every documentation space whose owner had not set these keys now reads with the clean look and the defaults below. Nothing was written to your space: the defaults are resolved when a reader loads the page, under every key you set, so anything you chose still wins.
To keep the reader exactly as it was:
{ "docs": { "look": "paper" } }paper restores everything except the PDF tool: the space-wide PDF is now off by default on documentation spaces because it is a type declaration, not a theme key. Turn it back on with settings.readerTools.pdf: true.
The defaults
Area | Default (unset) | Key that changes it |
|---|---|---|
Paper and ink | neutral white / near-black, dark |
|
Type | UI sans, 16px body on 1.6, H1 36px 600 −0.02em, H2/H3 600 |
|
Header | always on: logo, search pill centred on the header with a ⌘K chip, ONE theme button that cycles system → light → dark |
|
Brand words | beside a square mark; screen-reader only beside a wordmark (no "Acme" image + "Acme" text) |
|
Brand repeat | no brand at the top of the sidebar (a lone space; inside a site the site decides) |
|
Article | start-aligned with the title |
|
Sidebar groups | collapsible, sentence case, 600, 14px, ink; chevron at the end |
|
Active row | brand text, 600, soft brand tint |
|
Sidebar | 272px; its search box hides when the header has one (36px tall when shown); API method badges; a long label clamps to two lines |
|
Phone | a drawer, labelled with the section, opening on the section list |
|
Page actions | one split "Copy page ▾" at the title's end; an icon on a phone |
|
Updated | in the page footer |
|
Breadcrumb | shown (never on the space's home page) |
|
Title → lede | 16px |
|
Prompt block | the compact look |
|
Stripe under the header | none |
|
On this page | sentence-case 13px label; stepper steps indexed; a disclosure on phones |
|
Code | copy icon, language chip, neutral block and inline surfaces, the |
|
Links | underlined when your brand is under 3:1 against the ink (so a link is findable without colour) |
|
Tables | hairline rows, compact, no zebra |
|
Lists | a muted dash |
|
Tabs, steps, keys | scrolling tab rows, outlined step markers on a line, keycaps |
|
Pager | cards |
|
Feedback | a pill |
|
API reference | two-pane at ≥1280px, the SDK panel on the landing page only, parameters as a list |
|
Ask AI | a floating launcher, plus an "Ask AI" row in search |
|
A lone logo in dark mode | drawn inverted — a lone |
|
PDF of the space | off |
|
Some of these keys belong to features shipping in the same wave; a default for a key your reader does not draw yet does nothing until it does.
look
{ "docs": { "look": "muted" } }
| What it is |
|---|---|
| Neutral white paper, a UI sans, hairline tables, a quiet sidebar, the documentation defaults below |
|
|
|
|
|
|
|
|
| The documentation defaults OFF: the warm paper and every reader default as they were before 2026-10 |
Every look other than clean is a small patch over clean, and every key you set yourself wins over any look, field by field.
The clean look's tokens
Emitted at zero specificity, so any palette slot you set always wins:
Token | Light | Dark |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The tokens follow colorScheme (and colorSchemeStrict) exactly as the reader does, and a section painted light/dark takes that scheme's set.
Type in the clean look: the face is the platform UI sans, published as --docs-ui-font; running text at 16px on a 1.6 leading; H2 and H3 at 600; the page title 36px (30px on a phone), weight 600, tracking −0.02em. Your fonts, typography and mobile.density keys still win.
Inline code sits on a 6% ink wash with the hairline border rather than the brand's, reading --reader-code-inline-bg / -border first, so components.code still wins.
CSS variables for documentation
Variable | Written by | Default |
|---|---|---|
|
| 15rem; 272px under |
| the article's max width | the page width |
| the header's rendered height | — |
| the clean look | the platform UI sans |
See also CSS variables worth targeting.
Icons
Anywhere an icon takes a NAME — a card, a button, a callout, a nav entry — it accepts our own glyphs (book, code, message, calendar, chart, puzzle, lock, shield, star, bell, search, sparkle, link, cog, clock, heart, image, trophy, webhook, check-circle, warning, eye, bookmark, hash, upload, pencil, quote, user, …) and the extended set below, drawn from Lucide (ISC). Our glyph wins where a name is in both. Anything else that looks like a word is refused (422), so a misspelt name never prints as text; an emoji or a short symbol is still drawn as text.
Extended names: activity, alarm-clock, alert-circle, alert-triangle, anchor, archive, arrow-down, arrow-left, arrow-right, arrow-right-left, arrow-up, arrow-up-right, at-sign, atom, award, badge-check, banknote, bar-chart, battery, binary, blocks, bluetooth, book-marked, book-open, bot, box, braces, brain, bug, calendar-check, calendar-days, camera, check, chevron-down, chevron-left, chevron-right, chevron-up, circle, circle-check, circle-dot, circle-x, clipboard-list, cloud, cloud-download, cloud-upload, code-2, coffee, compass, component, container, copy, corner-down-right, cpu, credit-card, database, dollar-sign, download, edit, external-link, eye-off, file, file-code, file-json, file-plus, file-text, files, filter, fingerprint, flag, flask-conical, folder, folder-open, function-square, gauge, gift, git-branch, git-commit, git-fork, git-merge, git-pull-request, github, gitlab, globe, graduation-cap, grid, grip-vertical, hard-drive, heading, headphones, help-circle, history, home, hourglass, inbox, info, key, key-round, languages, layers, layout, layout-grid, library, lightbulb, line-chart, link-2, list, list-checks, list-ordered, lock-keyhole, lock-open, log-in, log-out, mail, map, map-pin, maximize-2, megaphone, menu, message-circle, message-square, mic, milestone, minimize-2, minus, monitor, moon, more-horizontal, more-vertical, move, music, navigation, network, newspaper, notebook-pen, octagon-alert, package, palette, panel-left, paperclip, pause, pie-chart-2, play, plug, plus, power, printer, quote-2, receipt, redo-2, refresh-cw, regex, repeat, rocket, rotate-ccw, scan-face, scan-search, scissors, search-code, send, server, settings-2, share-2, shield-alert, shield-check, shopping-cart, sliders, smartphone, sparkles, square, square-terminal, sun, table, tablet, tag, target, terminal, test-tube, text-cursor, thumbs-down, thumbs-up, ticket-check, timer, trash, trending-up, type, undo-2, user-check, user-plus, users, variable, video, wallet, wand, wifi, workflow, wrench, x, zap, zoom-in, zoom-out.
The top rule (changed)
The 2px rule under the header used to be a two-part stripe: ink for the first 62% of the width, then your brand. Under a header painted near the ink colour the ink part vanished, and what was left looked like a progress bar stuck at 40%. So the rule is now one solid line in your brand colour wherever it is drawn: with topRule unset under look: "paper", and with topRule: true.
You want | Set |
|---|---|
The solid brand line |
|
The old split stripe |
|
No rule |
|
The rule now also follows you onto the space root, /c/ hubs and tag pages (see One frame on every docs surface), so it no longer comes and goes as a reader moves from a page to its collection.
Looks are patches: what each one sets
A look writes a handful of ordinary keys under your own, and nothing else. Every key it writes is documented above and works under any look. clean is the documentation defaults (see The defaults); each other look is clean plus:
| Keys it adds over |
|---|---|
|
|
|
|
|
|
|
|
| nothing: the documentation defaults are off, and every unset key renders as it did before 2026-10 |
Layering is theme → space → page: a site theme's docs, then the space's own theme, then a page's metadata.themePatch (or its per-page theme.docs, below). Within each, a key you set beats the look. Three combinations that are not any template:
The muted canvas with the bold header, keeping the clean sidebar row:
{ "docs": { "look": "muted" }, "chrome": { "header": { "paint": "brand" } } }The panel look's inset article on a white page, the rail inside the panel, and the split stripe back:
{
"docs": {
"look": "clean",
"panel": { "radius": 16, "includeRail": true, "shadow": "sm" },
"topRule": "split"
},
"palette": { "paper": "white" }
}dark with the bold sidebar row only, and the front door framed with the tree:
{
"docs": {
"look": "dark",
"sidebar": { "active": { "style": "solid", "bar": false } },
"frame": { "front": "tree" }
}
}One frame on every docs surface
The space root, every /c/<slug> collection hub and every /tags/<tag> page now wear the same frame as an article: the article alignment, the inset panel (and panel.phone), the top rule, frame.tree: "hide", and the panel look. Before, they were drawn flat, so a "Panel" look gave inset articles beside a flat hub. The search box, Ask AI, the version, language and variant switchers (variantSwitcher.show: "always") and the rail outlets are there too.
Key | Values | Unset | What it does |
|---|---|---|---|
|
|
| The page tree beside the content, on articles, hubs, tag pages and a framed front door |
|
| the page as it was | The on-this-page rail's default; |
|
| what the stored front page says: framed when its sidebar is the docs tree ( | The space's front door (its landing or composed front page). |
frame.front exists because a space on the generated landing (never opened in the front-page builder) had no way to show its tree on its own home. Unset keeps every existing front door exactly as it is, so set { "docs": { "frame": { "front": "tree" } } } to frame yours.
Reader addresses a documentation space has
Address | What it is |
|---|---|
| The front door: the home page ( |
| A page. |
| A collection's hub — |
| Every page carrying |
| The page as Markdown, in the address's language |
| The whole space as one printable document ( |
| RSS of recently changed pages, linked by slug. |
| Every address above in a published language. The root and hubs name themselves canonical per language, with |
Public JSON for the same: GET …/tags/{tag}, GET …/pages/{pageId}.md and GET …/updates.xml.
Newer keys
Every key below is optional and unset renders what the reader drew before it existed. All are theme.docs.* unless the scope says otherwise.
Header, search and Ask AI
Key | Values | Unset | What it does |
|---|---|---|---|
| up to 6 questions, 120 characters each | none | One-click starter questions in the empty Ask AI panel (and in the Ask AI launcher widget) |
|
| none | "Ask a person instead" under an answer, opening |
| ≤ 80 characters |
| The docs search's words, in the rail and in the header on this space |
| boolean | off | A row of section chips with counts in the search dialog that narrows the hits |
|
| our own search | Search with your Algolia DocSearch index instead (see What you need to set up) |
| boolean | the header stays | The header slides away while the reader scrolls down and returns on the first scroll up |
|
| none | A repository button; |
| boolean | off | Readers keep the pages they opened for offline reading. Never a signed-in, shared-link or network-limited page; turning it off retires installed copies |
Page head, actions and navigation
Key | Values | Unset | What it does |
|---|---|---|---|
|
| none | "6 min read", counted from the page body |
|
| none | "Maintained by …" from the page's |
| (existing) | — | Now honoured in the actions row too ( |
| up to 4 | none | Your own actions after the built-in ones. |
|
|
| How a page's |
| 0–6 | the branch on the current path opens | Tree levels at or above this start open (collections are level 1); deeper ones start collapsed. |
|
| not shown | "Related pages". |
|
| only a version's own banner (unreleased, unmaintained) |
|
| boolean | none | A chip with the version's name beside the title |
| boolean | no memory | A version the reader picked is where the root and version-less addresses open next time |
| a new placeholder | — | The reader's language (empty on the original), for repositories that keep one file per language |
| boolean | none | A "wrap long lines" button on every code block |
API reference
Key | Values | Unset | What it does |
|---|---|---|---|
|
|
|
|
|
| no credential | What the samples show for the key or token ( |
|
| none | Download links for the space's own collections beside each operation, and a "Run in Postman" button for your published collection link (a postman.com address) |
|
|
| How AsyncAPI channel pages name their operations, whether the example message shows, and a protocol chip (WS, MQTT, AMQP…) |
GraphQL schemas import into the same reference: upload a .graphql SDL (or an introspection result), optionally with the endpoint readers' "Try it" calls, and each operation becomes a page with GraphQL, cURL, TypeScript and Python samples. Two blocks go with the reference: Rate limit (rateLimit) and Error codes (errorCodes, each row anchored at #error-<code>); see the authoring reference.
Page metadata
Set with PATCH …/pages/{pageId}/metadata; null clears.
Key | Values | What it does |
|---|---|---|
|
| The release stage of what the page documents, drawn per |
| a member's id | Who keeps the page true: named in the byline ( |
| up to 12 slugs or ids | This page's own related pages; they lead the |
| one class name | A CSS class on the page's tree row, for |
| up to 8 | Written as |
| boolean |
|
Labels in every language (translations)
Every label you type into theme.docs (the contents heading, the feedback question and buttons, Ask AI's name and starter questions, switcher captions, the search placeholder, the version banner's wording…) used to have one value for every language. theme.docs.translations gives each language its own:
{
"docs": {
"toc": { "label": "In this guide" },
"ask": { "label": "Ask the docs", "suggestions": ["How do I install it?"] },
"translations": {
"es": {
"toc": { "label": "En esta guía" },
"ask": { "label": "Pregunta a la documentación", "suggestions": ["¿Cómo lo instalo?"] },
"labels": { "Getting started": "Primeros pasos" }
}
}
}
}Fixed labels are named by their key path. List items (section tabs, rail anchors and footer links, variant options, custom page actions) are matched by their original words in labels, so reordering a list never scrambles the translation. A missing language or key reads in the original, and a label you turned off (false) stays off. The .md twin, print view and updates.xml use the same words for the address's language. See Translated labels.
Per-item overrides
A single documentation page can carry its own theme in two ways:
metadata.themePatch— the page keys listed under Per-page theme, scoped to that page.the page's own customization (
PATCH …/pages/{pageId}/metadatawiththeme, alongsidecustomCss,shareImageUrl,jsonLd…). On a documentation space it accepts the token keys every piece takes (colours, fonts,components,typography,layout,contentWidth,motion) and thedocsnamespace. Any other type's namespace (post,forum, …) is refused with a422naming it, because nothing on a docs page would draw it.
The page's contentWidth and layout.widths.text reach the article column. See Theme your site → Per-page customization.
Outlets
Theme components can fill these slots on a documentation space (Theme components):
Outlet | Where | Context |
|---|---|---|
| Above and after the article |
|
| Top of the left rail under the brand; foot of the rail | — |
| Top of the right rail (above "On this page"); foot of the right rail |
|
| A |
|
The page and sidebar outlets now also render on the space root, hubs and tag pages, so a sponsor wall in docs-sidebar-end is on every docs surface.
In the studio
Look → Docs reader: every setting holds every
theme.docskey, split into folders: Navigation; Header, search and Ask AI; Page head; Contents rail; Page foot; API reference; Blocks; Collection pages; Versions and languages; Frame and panel (the look,frame,panel, widths andtopRule); Labels per language; and More. Double-clicking a part of the page opens its keys.Look → Page tools switches
settings.readerTools(below), and beside it Ask page owners to review after (days) setssettings.docsReview.A collection's settings hold its rail controls: starts collapsed, its own scope and the back link's style (
nav.collapsed,navScope,backStyle).A page's settings card holds Page owner.
Settings holds the SEO panel (with Generated share card and Extra meta tags), Network access, and Embed, with a choice of widget (search box, Ask AI box, or the Ask AI corner button).
Housekeeping → Change requests: each request has a Preview link, a stable address of the space with that request's edits that works until it is merged or closed (
POST …/preview/change-requests/{crId}, Preview links).
Behaviour settings
Setting | Default on a documentation space | What it does |
|---|---|---|
| on | The search box in the rail |
| off | The PDF / print link and |
| off | A separate "Copy page" tool (the actions bar already copies) |
| on | "Open in ChatGPT / Claude" |
| on | "Connect with MCP" |
| off | 7–730. A page with an owner that goes unedited this long sends its owner one review reminder per interval (checked daily) |
| everyone | Limit the space to some networks — see Limit a space to some networks |
| none | Up to 20 |
The settings.* keys are written with PATCH /orgs/{orgId}/spaces/{spaceId} and { "settings": { … } }, validated strictly:
curl -X PATCH "https://forumulate.com/api/orgs/$ORG/spaces/$SPACE" \
-H "Authorization: Bearer $FORUMULATE_TOKEN" -H "Content-Type: application/json" \
-d '{ "settings": { "readerTools": { "pdf": true }, "docsReview": { "afterDays": 90 } } }'Embeds, previews and analytics
Ask AI launcher.
/api/embed/ask.jsputs a corner Ask AI button in your own product, with yourask.suggestions, answers with their sources, and yourask.handoff. It takesdata-org,data-space,data-position(bottom-rightorbottom-left),data-label,data-accent,data-themeanddata-locale;window.Forumulate.askhasopen(),close()andask(question). See Embeds.Local preview.
forumulate dev <folder> --space <spaceId>serves a folder of Markdown on your machine, reloading as you save, rendered by our own renderer (POST …/docs/render, which saves nothing).--site <your docs address>borrows your published look.Analytics and chat vendors. Besides the existing ones, the analytics settings take Mixpanel, Amplitude, Heap, Hotjar, Microsoft Clarity, LogRocket, Koala, Pirsch, FullStory, Clearbit and Front Chat, each behind the consent banner like the rest. See Analytics.
Importers. Mintlify, ReadMe and Docusaurus projects import with their navigation (alongside GitBook, MkDocs, Notion, Confluence and the help-desk importers); see Importers.
Live code. Runnable HTML/CSS/JS and React demos are their own guide: Live code playgrounds.
What you need to set up
These features stay off until you give them your own account's details. Until then readers see exactly what they saw before.
Feature | What you need | Where you enter it | Until then |
|---|---|---|---|
Algolia DocSearch | Your Algolia Application ID, the search-only API key (never an admin key), and the index name |
| Our own search |
Run in Postman | A published Postman collection link from your Postman workspace |
| No button (the Postman/Insomnia/Bruno downloads need nothing) |
Analytics and chat vendors | Each vendor's public site or project ID | Your space's or organization's analytics settings | No script is loaded |
Live code playgrounds | Nothing from you: a sandbox domain on Forumulate's side | — | The block's fallback picture |
The Algolia key is public by design (Algolia issues it for browsers), and its host is added to that space's allowed connections only.
In the API
Every operation below is also reachable from the other surfaces without a dedicated command. forumulate api <Operation> calls it from the CLI, the JavaScript SDK has a typed method for each one (the last column names it; the Python and Go clients are generated from the same spec), and an agent reaches the organization-scoped ones through the MCP call_api tool. Where a CLI command or MCP tool exists too, the last column names it. Operation names link to the API reference.
Public search
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| Get the search in a space (public reader) |
|