Configuration reference: Site config

Embed
Preview of the card
<a href="https://forumulate.com/reference/config-site-config"><img src="https://forumulate.com/reference/_api/public/orgs/046a9bc0-d530-43ae-9bae-af7945008cda/spaces/8042f004-e3d9-445f-b7a4-b6c1f86836e6/pages/d2bc5950-1192-400e-a18e-01882a8b24a0/social-card.svg?v=3e7ae33eec8b" alt="Configuration reference: Site config" width="600" height="315"></a>

A site’s config: header, footer, tabs, theme, page mounts, search.

Where it is used: PATCH /orgs/{orgId}/sites/{siteId} { "config": … }, and site.config in forumulate.yaml.

JSON Schema: https://forumulate.com/schemas/2026-12-01/site-config.json — point your editor at it with "$schema": "https://forumulate.com/schemas/2026-12-01/site-config.json" (JSON) or # yaml-language-server: $schema=https://forumulate.com/schemas/2026-12-01/site-config.json (YAML), and check a whole folder with forumulate validate <dir>.

This page is generated from the schema the API validates with; it cannot describe a key the API does not take.

Casing

  • Every key in a JSON or YAML configuration document (site config, theme, forumulate.yaml, a category) is camelCase, exactly: primaryColor, footerColumns, headerLinks. primary_color and PrimaryColor are unknown keys, refused with the camelCase name in the hint. The one exception is two top-level blocks of a version-2 site manifest, which are snake_case: reader_auth (a list of sign-in configurations) and status_monitors; readerAuth is a different key there, the one-word shorthand readerAuth: atlas.

  • Front matter is the exception: a key is read in any casing (seo_title, seo-title, SeoTitle all mean seoTitle), and the names other tools use (description, sidebar_label, sidebar_position, image) are read as ours.

  • Enum values are lower-case ("wide", "system"), except space types and visibilities, which are upper-case ("DOCS", "PUBLIC").

  • Colours are #rrggbb (or #rgb); lengths that are numbers are CSS pixels.

Keys

Top-level keys and the keys one level inside them. [] marks “each item of a list”.

Key

Type

Required

What it is

accountMenu

object

What the signed-in reader's avatar menu holds: the entries and their order (built-ins and your own links), the name beside the avatar, the bell.

accountMenu.bell

boolean

accountMenu.items

"activity" | "profile" | "bookmarks" | "notifications" | "preferences" | "account" | "signOut" | "divider" | object[]

accountMenu.showAvatar

boolean

accountMenu.showName

boolean

ai

object

enabled: false turns Ask and semantic search off on every member that has not said otherwise.

ai.enabled

boolean

yes

align

"split" | "left" | "center"

The header row's arrangement: split (default — brand left, links right), left, or center (brand centred, links either side).

analytics

object

Analytics tags for every member (a space's analytics-tags body): per vendor, a member's own id, else the site's, else the organization's. inheritOrg: false drops the organization's; customHead is one block, a member's replacing the site's.

analytics.amplitude

string | ""

analytics.clarity

string | ""

analytics.clearbit

string | ""

analytics.customHead

string

analytics.fathom

string | ""

analytics.frontChat

string | ""

analytics.fullstory

string | ""

analytics.ga4

string | ""

analytics.gtm

string | ""

analytics.heap

string | ""

analytics.hotjar

string | ""

analytics.inheritOrg

boolean

analytics.intercom

string | ""

analytics.koala

string | ""

analytics.logrocket

string | ""

analytics.lucida

string | ""

analytics.matomo

string | ""

analytics.matomoHost

string | ""

analytics.metaPixel

string | ""

analytics.mixpanel

string | ""

analytics.pirsch

string | ""

analytics.plausible

string | ""

analytics.plausibleHost

string | ""

analytics.posthog

string | ""

analytics.posthogHost

string | ""

analytics.provider

"seolyticize"

analytics.segment

string | ""

analytics.seolyticize

string | ""

analytics.simpleAnalytics

string | ""

analytics.siteKey

string | ""

analytics.umami

string | ""

analytics.umamiHost

string | ""

analytics.watchtower

string | ""

analytics.watchtowerHost

string | ""

analytics.watchtowerScriptHost

string | ""

brandText

"auto" | "show" | "hide" | object

The brand's words beside the header logo: auto (default), show or hide, or the object form with source and phone.

changelogFeed

object

The site's /changelog.json and /changelog.xml (JSON Feed 1.1 plus structured fields): whether they are served, which changelog space feeds them, their title and description, the release limit, the categories exposed, what marks a release breaking and the link style. A key left out falls back to the feeding space's settings.changelogFeed, then the default.

changelogFeed.breakingTags

string[]

Tags (or type values) that mark a release breaking, besides its own breaking field. Default ["breaking","breaking-change"]; [] leaves only the field.

changelogFeed.categories

"added" | "changed" | "fixed" | "deprecated" | "removed" | "security"[]

Only releases of these categories are exposed (Keep a Changelog's six; a release's category is its type field or a tag that names one). Unset: every release, including those of no category.

changelogFeed.description

string

The document's description. Unset: the space's SEO description, else a generated line.

changelogFeed.enabled

boolean

false turns the document off (404). Default true.

changelogFeed.limit

integer

Releases in one response — also the cap on ?limit=. Default 50, at most 500; page on with ?cursor=.

changelogFeed.linkStyle

"release" | "changelog"

Where an item's url points: release (the release's own page, default) or changelog (the changelog's page at the release's anchor).

changelogFeed.spaceId

string

Site only: the changelog space that feeds the site's root document. Unset: the first public changelog space of the site, in the site's order. Must be a member of the site.

changelogFeed.title

string

The document's title. Unset: the space's name.

comments

object

Reader comments (enabled, moderated) for every member that has not saved its own.

comments.enabled

boolean

Whether readers may comment at all. Left alone when omitted.

comments.moderated

boolean

Whether a comment waits for approval before it shows. Left alone when omitted.

copyright

string

The copyright or legal line at the very bottom of the footer, up to 200 characters.

cta

object

The call-to-action button at the end of the header row: { label, href } plus its own colours and size. ctas (up to three) replaces it when set.

cta.audience

"all" | "signedIn" | "signedOut"

cta.background

string

cta.border

string

cta.borderDark

string

cta.fontSize

number

cta.height

integer

cta.href

string

yes

cta.icon

string

An icon name from GET /icons?for=header (see the HeaderIconName enum: our glyphs, the extended set and the brand marks), or an image URL (https://…, /api/assets/…, or a data: PNG/JPEG/WebP/GIF — not SVG). Any other bare word is refused.

cta.ink

string

cta.inkDark

string

cta.label

string

yes

cta.phone

"menu" | "bar"

cta.showOnPhone

boolean

cta.size

"sm" | "md" | "lg"

ctaStyle

"solid" | "pill" | "outline" | "link" | "subtle"

The header button's shape: solid (default), pill, outline or link.

ctas

object[]

Up to three header buttons, in order. When set it replaces cta and secondaryCta.

ctas[].action

"signIn" | "signUp"

ctas[].audience

"all" | "signedIn" | "signedOut"

ctas[].background

string

ctas[].border

string

ctas[].borderDark

string

ctas[].fontSize

number

ctas[].height

integer

ctas[].href

string

yes

ctas[].icon

string

An icon name from GET /icons?for=header (see the HeaderIconName enum: our glyphs, the extended set and the brand marks), or an image URL (https://…, /api/assets/…, or a data: PNG/JPEG/WebP/GIF — not SVG). Any other bare word is refused.

ctas[].ink

string

ctas[].inkDark

string

ctas[].label

string

yes

ctas[].phone

"menu" | "bar"

ctas[].showOnPhone

boolean

ctas[].size

"sm" | "md" | "lg"

ctas[].style

"solid" | "pill" | "outline" | "link" | "subtle"

ctas[].underline

boolean

desktopNav

"inline" | "menu"

NAVIGATION on a wide screen: inline (default — headerLinks in the bar) or menu (behind a menu button, as on a phone). The phone twin is mobileNav.

discovery

object

aiIngestion and listed for every member that has not said: member → site → organization.

discovery.aiIngestion

boolean

discovery.listed

boolean

List this blog in the public blog directory (/blogs). Default true.

footer

object

The footer's own lockup (logo, logoMark), where "Cookie settings" sits, your own footer HTML/CSS and a newsletter row.

footer.art

object

footer.background

object

footer.border

boolean

footer.bottomAlign

"frame" | "columns"

footer.bottomRule

boolean

footer.bottomSize

number

footer.brandPlacement

"side" | "top" | "bottom" | "none"

footer.brandWidth

number | string

footer.breadcrumb

boolean

footer.breadcrumbPlacement

"top" | "bottom"

footer.columnRule

boolean

footer.columnWidth

number

footer.columns

integer | "auto"

footer.contentWidth

string

footer.cookieLinkPlacement

"bottom" | "brand" | "columns"

footer.css

string

footer.font

"heading" | "body" | "mono"

footer.headingFont

"heading" | "body" | "mono"

footer.headingGap

number

footer.headingInk

string

footer.headingInkDark

string

footer.headingSize

number

footer.headingStyle

"label" | "plain"

footer.headingWeight

integer

footer.hideOn

"docs"[]

footer.html

string

footer.htmlLinkInk

string

footer.htmlLinkInkDark

string

footer.htmlLinkUnderline

boolean

footer.htmlPlacement

"above" | "below" | "replace"

footer.htmlSize

number

footer.layout

"stack" | "rows"

footer.linkGap

number

footer.linkInk

string

footer.linkInkDark

string

footer.linkSeparator

"none" | "pipe" | "dot" | "slash"

footer.linkSize

number

footer.linkWeight

integer

footer.logo

string | "" | false

footer.logoMark

string | "" | false

footer.newsletter

object

footer.padding

object

footer.phone

"stack" | "accordion"

footer.scheme

"light" | "dark"

footer.showBrandName

boolean

footer.socialSize

integer

footer.socialStyle

"plain" | "circle" | "square"

footer.status

object

footer.width

string

footerColumns

object[]

A multi-column footer: up to 12 × { heading, links, stack }, up to 20 links each (the link field is label).

footerColumns[].audience

"all" | "signedIn" | "signedOut"

footerColumns[].heading

string

yes

footerColumns[].links

object[]

yes

footerColumns[].stack

boolean

footerDescription

string

A short blurb under the brand name in the footer, up to 400 characters.

footerLinks

object[]

A flat row of footer links, up to 20 × { label, href }. footerColumns is the multi-column footer.

footerLinks[].audience

"all" | "signedIn" | "signedOut"

footerLinks[].href

string

yes

footerLinks[].label

string

yes

header

object

How the header bar looks and lays out its links: overlay, height, border, shadow, blur, logo, linksAlign (where headerLinks sit), maxLinks (how many before "More").

header.accentTop

object

header.activeMatch

"auto" | "explicit"

header.activeStyle

"underline" | "text" | "pill"

header.background

object

header.blur

integer

header.border

"line" | "none" | "strong" | object

header.borderColor

string

header.borderSpan

"bar" | "group"

header.brandPrefix

object

header.brandSize

number | object

header.controlsAlign

"inline" | "end"

header.ctaDivider

boolean

header.floating

object

header.group

object

header.groupOnScroll

"full"

header.height

integer | object

header.iconSize

number | object

header.linksAlign

"start" | "end" | "center" | "after-search"

header.logoHeight

integer | object

header.logoHref

string

header.maxLinks

integer

header.menuButton

object

header.navCase

"none" | "uppercase" | "lowercase" | "capitalize"

header.navFont

"heading" | "body" | "mono"

header.navGap

integer

header.navInk

string

header.navInkDark

string

header.navSeparator

string

header.navSize

number

header.navTracking

number

header.navWeight

integer

header.opacity

number

header.order

"search" | "links" | "actions" | "themeToggle" | "signIn" | "ctas" | "menu"[]

header.overlay

boolean

header.overlayInk

string

header.paint

"page" | "brand"

header.phoneLayout

"row" | "stacked"

header.scrollTitle

boolean | "site"

header.scrollTitleFont

"heading" | "body" | "mono"

header.sectionCrumb

boolean

header.sectionCrumbSeparator

string

header.shadow

"none" | "sm" | "md" | "lg"

headerLinks

object[]

NAVIGATION — the header's own items, up to 12: { label, href }, or an item that opens a dropdown (children) or a mega-menu panel (panel). In a site they REPLACE the automatic space tabs unless headerSpaceIds is set; how dropdowns open is navMenu, the phone/desktop behaviour is mobileNav/desktopNav.

headerLinks[].audience

"all" | "signedIn" | "signedOut"

headerLinks[].border

string

headerLinks[].borderDark

string

headerLinks[].children

object[]

headerLinks[].divider

boolean

headerLinks[].githubRepo

string

headerLinks[].highlight

boolean

headerLinks[].href

string

headerLinks[].icon

string

An icon name from GET /icons?for=header (see the HeaderIconName enum: our glyphs, the extended set and the brand marks), or an image URL (https://…, /api/assets/…, or a data: PNG/JPEG/WebP/GIF — not SVG). Any other bare word is refused.

headerLinks[].iconDark

string

An icon name from GET /icons?for=header (see the HeaderIconName enum: our glyphs, the extended set and the brand marks), or an image URL (https://…, /api/assets/…, or a data: PNG/JPEG/WebP/GIF — not SVG). Any other bare word is refused.

headerLinks[].iconOnly

boolean

headerLinks[].ink

string

headerLinks[].inkDark

string

headerLinks[].kbd

"mod-k" | "slash" | "mod-i"

headerLinks[].label

string

yes

headerLinks[].match

object

headerLinks[].panel

object

headerLinks[].phone

"menu" | "bar"

headerLinks[].placement

"bar" | "menu" | "both"

headerLinks[].style

"link" | "button" | "outline"

headerSpaceIds

string[]

The member spaces drawn as tabs in the header bar, in this order (the first five; the rest fold behind "More"). Unset: every member space — unless the site has headerLinks, which then replace the automatic tabs, so a site with its own links shows ONE navigation. []: no tabs. List ids to show tabs beside your own links. A space left out stays reachable by its address, the footer and the sitemap.

height

"compact" | "standard" | "tall"

The header bar's height: compact, standard (default) or tall.

legalGate

object

What a required legal document withholds (scope, anonymous) on every member, per field: a member's override → the site's → the organization's legal_gate.* setting.

legalGate.anonymous

"require_identity" | "cookie"

legalGate.scope

"none" | "post" | "read" | "signin"

llms

object

The site's llms.txt, llms-full.txt and llms-guides*.txt: title, summary, intro, include/exclude, section order, an Optional section and the full-text switch.

llms.enabled

boolean

false withdraws every llms file of the site (404). Default true.

llms.exclude

object

When anything is listed, ONLY what it names is in the files.

llms.full

object

The full-text files.

llms.grouping

"space" | "flat"

space (default): a ## per space. flat: no space headings; each space’s own sections stand at the top level.

llms.include

object

When anything is listed, ONLY what it names is in the files.

llms.intro

string

Markdown placed after the summary and before the first section: how to use the site, what to prefer.

llms.optional

object

llmstxt.org’s “Optional” section: links a model may skip when short of context.

llms.sections

object[]

Order (and optional heading / description) of the spaces; spaces not listed follow in site order.

llms.summary

string

The blockquote under the title (default: the site’s tagline).

llms.title

string

The # title (default: the site’s name).

logoPhone

"full" | "mark"

mark: draw logoMark instead of the wordmark below 640px wide. Unset: the full lockup at every width.

mobileNav

"menu" | "bar"

NAVIGATION on a phone: menu (default — headerLinks behind a hamburger) or bar (inline in the header row). The wide-screen twin is desktopNav.

navMenu

object

NAVIGATION — how the dropdowns and mega menus of headerLinks open (hover or click) and look. Items themselves live in headerLinks.

navMenu.animation

"none" | "fade" | "slide" | "scale"

navMenu.background

string

navMenu.border

string

navMenu.closeDelay

integer

navMenu.ink

string

navMenu.openDelay

integer

navMenu.radius

number

navMenu.shadow

"none" | "sm" | "md" | "lg"

navMenu.trigger

"hover" | "click"

navMenu.width

integer

pageMounts

object[]

Single pages published at a path of the site: { path: "/pricing", spaceId, page: "<slug>" }, or path: "/" to make one page the site root instead of the generated index. The space must be a member, the page must exist, and a path may not shadow a member space’s own segment. The page’s address inside its space redirects (308) to the mounted path, which is its canonical.

pageMounts[].page

string

yes

The page, by its slug in that space.

pageMounts[].path

string

yes

Where the page answers on the site: / for the root, or one segment (/pricing).

pageMounts[].spaceId

string

yes

The member space the page belongs to.

readerIdentity

object

How far one reader identity carries (scope), per field: member → site → organization.

readerIdentity.scope

"space" | "org"

readerThemes

object[]

The reader's selectable themes for every member without a list of its own.

readerThemes[].name

string

yes

readerThemes[].theme

object

yes

robots

object

The site's robots.txt: extra rules, disallowed paths, a policy per AI crawler, sitemaps and a raw block. Generated private-path rules are always kept.

robots.aiCrawlerPolicy

object

Per AI crawler: allow | disallow | default. Wins over aiCrawlers. Known: GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-User, Claude-SearchBot, anthropic-ai, Google-Extended, PerplexityBot, Perplexity-User, Applebot-Extended, Bytespider, CCBot, Meta-ExternalAgent, cohere-ai, Amazonbot, DuckAssistBot, YouBot.

robots.aiCrawlers

"allow" | "disallow"

The default for every AI crawler this platform knows. disallow refuses all of them; allow leaves them to the spaces’ AI-ingestion settings. Unset = the spaces’ settings decide.

robots.disallowPaths

string[]

Paths no crawler may fetch, added to the everyone (*) group: ["/drafts/", "/*.pdf$"].

robots.extraRules

object[]

Your own groups: { userAgent, allow[], disallow[], crawlDelay }. Joined to the generated group of the same crawler.

robots.extraSitemaps

string[]

More Sitemap: URLs (http or https), after the generated one.

robots.raw

string

Anything else, appended last and verbatim: robots.txt directives and # comments only, at most 200 lines / 8000 characters, starting with a User-agent line.

robots.sitemaps

"auto" | "off"

auto (default) names the site’s sitemap; off writes no generated Sitemap: line.

search

"none" | "icon" | "pill" | object

The header search control, as on a space's chrome.search: none | icon | pill. (An object here is the site search settings, written that way on 2026-10-24: it is accepted and moved to searchSettings.)

searchHideOn

"desktop" | "phone"[]

Hide the header search at one width: ["desktop"] or ["phone"].

searchPlacement

"start" | "center" | "end" | "before-links"

Where the header search sits on a wide screen: start, center, end (default) or before-links.

searchSettings

object

Site search: typoTolerance, answer (off | byok | provider), cacheSeconds, types (the kinds searched) and excludeSpaceIds. Each unset key falls back to the org setting (search.typo_tolerance, search.site_answer, search.site_cache_seconds).

searchSettings.answer

"off" | "byok" | "provider"

A grounded answer above the hits: off, byok (the org’s own AI key) or provider (the org’s connected answer provider). Unset: the org setting search.site_answer (default off).

searchSettings.cacheSeconds

integer

How long a shared cache may keep an anonymous answer (s-maxage). 0 = never. Unset: the org setting search.site_cache_seconds (default 60).

searchSettings.excludeSpaceIds

string[]

Member spaces left out of the site search (still published, still in the nav).

searchSettings.types

"page" | "reference" | "post" | "topic" | "release" | "idea" | "legal" | "incident" | "event"[]

The kinds this site searches at all. Unset: every kind.

searchSettings.typoTolerance

boolean

Correct one typo per word of four letters or more when a search finds nothing (payuot → payout). Unset: the org setting search.typo_tolerance (default on).

searchStyle

object

How the header search looks: the pill's words (also the placeholder), its fill and ink, size, and glyph: false to drop the magnifier.

searchStyle.background

string

searchStyle.backgroundDark

string

searchStyle.glyph

boolean

searchStyle.iconFrame

"none" | "circle" | "square"

searchStyle.ink

string

searchStyle.inkDark

string

searchStyle.kbd

"mod-k" | "slash"

searchStyle.kbdStyle

"caps" | "plain"

searchStyle.label

string

searchStyle.labelPhone

string

searchStyle.open

"box" | "modal"

searchStyle.shrink

boolean

searchStyle.size

"sm" | "md"

searchStyle.width

integer

secondaryCta

object

A second header button beside cta, with its own style. ctas replaces both when set.

secondaryCta.audience

"all" | "signedIn" | "signedOut"

secondaryCta.background

string

secondaryCta.border

string

secondaryCta.borderDark

string

secondaryCta.fontSize

number

secondaryCta.height

integer

secondaryCta.href

string

yes

secondaryCta.ink

string

secondaryCta.inkDark

string

secondaryCta.label

string

yes

secondaryCta.phone

"menu" | "bar"

secondaryCta.showOnPhone

boolean

secondaryCta.size

"sm" | "md" | "lg"

secondaryCta.style

"outline" | "link" | "solid" | "subtle"

secondaryCta.underline

boolean

seo

object

The site's SEO, the body of a space's PUT …/seo. description and seoTitle describe the site's own front page; every other key (verification tokens, socialImageUrl, twitterSite, siteName, titleTemplate, metatags, socialCard, contentLanguage, noindex, …) is also each member's default — a member’s stated value wins, and noindex from either level deindexes.

seo.contentLanguage

string | ""

seo.description

any | string | ""

seo.generatedTitles

any | "descriptive" | "short" | ""

How a generated page is titled when nothing is written for it: descriptive (“Posts about API — Blog”) or short (“API”, as before). An empty string follows the organization’s discovery.generated_titles, then descriptive.

seo.indexArchives

boolean

seo.indexHidden

boolean

seo.indexMemberDirectory

boolean

seo.linkSubpages

boolean

seo.metatags

object[]

seo.noindex

boolean

seo.ogTitleSiteSuffix

boolean | null

Whether og:title carries the site suffix the <title> has. Unset (null) follows the organization’s discovery.og_title_site_suffix, then false: a share card already names the site in og:site_name.

seo.pages

map

Per generated page: blog.tag, blog.tags, blog.archive, blog.archiveMonth, blog.author, blog.subscribe, community.about, community.badges, community.categories, community.leaderboard, status.uptime, status.history, status.maintenance, status.component. Each takes seoTitle and seoDescription.

seo.schemaType

any | "WebPage" | "AboutPage" | "ContactPage" | "ProfilePage" | "SoftwareApplication" | "Product" | ""

seo.seoTitle

any | string | ""

The <title> of the space’s index in the original language, without the site suffix. Other languages: translations.<code>.seoTitle on PATCH …/spaces/{spaceId}.

seo.siteName

any | string | ""

seo.socialCard

object

seo.socialImageUrl

any | string | ""

seo.titleTemplate

any | string | ""

seo.twitterSite

any | string | ""

seo.verification

object

showSignIn

boolean

false takes the reader "Sign in" control out of the header (in-page sign-in prompts stay). Default true.

sidebarBrand

"show" | "hide"

The brand lockup at the top of the docs reader's left rail: show (default) or hide.

sidebarToggle

boolean | object

A menu button at the start of the header that opens the page's sidebar drawer, on pages that have one; { placement: "end" } puts it at the far end.

signInStyle

object

How the reader "Sign in" control looks: look (link default, solid, outline), icon, label, order against the buttons, phone placement.

signInStyle.icon

string

An icon name from GET /icons?for=header (see the HeaderIconName enum: our glyphs, the extended set and the brand marks), or an image URL (https://…, /api/assets/…, or a data: PNG/JPEG/WebP/GIF — not SVG). Any other bare word is refused.

signInStyle.label

string

signInStyle.look

"link" | "solid" | "outline"

signInStyle.order

"before-ctas" | "after-ctas" | "before-controls"

signInStyle.phone

"menu" | "bar"

socialLinks

object[]

Social profiles badged with icons in the footer, up to 10 × { platform, href }.

socialLinks[].href

string

yes

socialLinks[].platform

"x" | "github" | "linkedin" | "youtube" | "discord" | "mastodon" | "instagram" | "facebook" | "bluesky" | "website" | "behance" | "devto" | "dribbble" | "gitlab" | "hackernews" | "matrix" | "medium" | "patreon" | "pinterest" | "producthunt" | "reddit" | "rss" | "slack" | "spotify" | "stackoverflow" | "substack" | "telegram" | "threads" | "tiktok" | "twitch" | "vimeo" | "whatsapp"

yes

sticky

boolean

true: the header stays at the top while the reader scrolls. Default false.

subnav

object

NAVIGATION — a section row BELOW the header row: your own links (and endLinks), and where a docs space with sections.placement: "header" draws its section tabs. The header's main items are headerLinks; the bar above it is topBar.

subnav.active

object

subnav.background

string

subnav.backgroundDark

string

subnav.border

"line" | "none"

subnav.endLinks

object[]

subnav.ink

string

subnav.inkDark

string

subnav.justify

"start" | "between"

subnav.links

object[]

subnav.navSize

number

subnav.navWeight

integer

subnav.phone

"wrap" | "scroll" | "menu" | "hide"

tabs

object[]

NAVIGATION — named GROUPS of member spaces, up to 20 × { title, slug, spaceIds }: the landing page and its sections band group spaces by tab, and spaces in no tab fall under "More". Not the header links: those are headerSpaceIds (which spaces get a tab in the bar) and headerLinks (your own items).

tabs[].slug

string

yes

tabs[].spaceIds

string[]

yes

tabs[].title

string

yes

tagline

string

A line under the site name on its generated landing page, up to 200 characters.

theme

object

The site-wide look (a full theme object): every member space wears it over its own stored theme while it is in the site.

theme.about

object

theme.accentColor

string

theme.ads

object

theme.announcement

object | false

theme.apiOrigins

string[]

theme.blog

object

theme.bodyFontFamily

string

theme.brandName

string | false

theme.careers

object

theme.changelog

object

theme.chrome

object

Header and footer for a STANDALONE space. A space inside a site wears the SITE’s chrome (the top level of the site config) and this is stored but not rendered there.

theme.colorScheme

"system" | "light" | "dark"

theme.colorSchemeStrict

boolean

theme.compare

object

theme.componentInstalls

object[]

theme.components

object

theme.contact

object

theme.contentWidth

"standard" | "wide" | "full"

theme.courses

object

theme.cssSnippets

object[]

theme.custom

object

theme.customCss

string

theme.customFontFamily

string

theme.customFontUrl

any | string | ""

theme.customJs

string

theme.docs

object

theme.elementStyles

object[]

theme.events

object

theme.faq

object

theme.faviconUrl

any | string | ""

theme.font

"sans" | "serif" | "mono"

theme.fontOrigins

string[]

theme.fontOriginsRemoved

string[]

theme.fonts

object

theme.footnotes

object

theme.forum

object

theme.frameOrigins

string[]

theme.headingAnchors

"hover" | "always" | "none"

theme.headingFontFamily

string

theme.i18n

object

theme.imgOrigins

string[]

theme.layout

object

theme.legal

object

theme.logoDarkMode

"none" | "invert" | "plate"

theme.logoIncludesName

boolean

theme.logoMark

any | string | ""

theme.logoUrl

any | string | ""

theme.logoUrlDark

any | string | ""

theme.logoUrlLight

any | string | ""

theme.mediaOrigins

string[]

theme.mobile

object

theme.monoFontFamily

string

theme.motion

object

theme.newsroom

object

theme.notFound

object

theme.onThisPage

"auto" | "always" | "never"

theme.palette

object

theme.paletteDark

object

theme.post

object

theme.primaryColor

string

theme.primaryColorDark

string

theme.roadmap

object

theme.schemaLogoUrl

any | string | ""

theme.scriptOrigins

string[]

theme.settings

map

theme.settingsSchema

object[]

theme.showLanguageSwitcher

boolean

theme.status

object

theme.stringOverrides

map

theme.titleBand

object

theme.typography

object

theme.whiteLabel

boolean

Hide Forumulate branding in the public readers and emails. Inside a site, the SITE theme’s whiteLabel (config.theme.whiteLabel) decides for every space in it.

themeToggle

object

The light/dark control: show, style, position in the header row, colours.

themeToggle.activeColor

string

themeToggle.border

boolean

themeToggle.footerAlign

"end" | "last-column"

themeToggle.hideOn

"desktop" | "phone"[]

themeToggle.modes

"system" | "light" | "dark"[]

themeToggle.phone

"menu" | "row"

themeToggle.position

"end" | "start" | "menu" | "footer" | "sidebar"

themeToggle.radius

number

themeToggle.show

boolean

themeToggle.style

"mono" | "icon" | "switch" | "cycle" | "select"

topBar

object

NAVIGATION — a thin utility bar ABOVE the header row (an announcement, a few links, contact details). The header's main items are headerLinks; the row below it is subnav.

topBar.align

"start" | "end" | "center" | "between"

topBar.background

string

topBar.button

object

topBar.case

"none" | "uppercase" | "lowercase" | "capitalize"

topBar.font

"heading" | "body" | "mono"

topBar.height

integer

topBar.ink

string

topBar.links

object[]

yes

topBar.phone

"wrap" | "scroll" | "hide" | "icons" | "row"

topBar.sticky

boolean

topBar.style

"bar" | "tab"

topBar.tracking

number

translations

map

The chrome in other languages: { "es": { labels: { "Pricing": "Precios" }, footerDescription, copyright } } — labels maps the original words of any header item, footer link or button.

Common mistakes

Each of these is answered with a did-you-mean by the API’s 422 and by forumulate validate.

Where

You wrote

We read

Why

footerColumns[]

title

heading

A footer column is a heading over links, as in the rendered HTML.

footerColumns[]

items

links

A column holds links, each {label, href}.

footerLinks[]

title

label

Every link in the chrome is {label, href}.

headerLinks[]

title

label

Every link in the chrome is {label, href}.

headerLinks[]

url

href

Links take href, as in HTML.

footerLinks[]

url

href

Links take href, as in HTML.

socialLinks[]

url

href

Links take href, as in HTML.

socialLinks[]

network

platform

A social link names its platform (github, x, linkedin, …).

cta

text

label

A button is {label, href}.

cta

url

href

A button is {label, href}.

top level

primary

primaryColor

Colours are named <role>Color.

top level

accent

accentColor

Colours are named <role>Color.

top level

brandColor

primaryColor

The brand colour is the theme’s primaryColor.

top level

font

fontFamily

The body face is fontFamily; headings take headingFontFamily.

typography

headingAnchors

headingAnchors

It is a top-level theme key, not part of typography.

top level

sidebar_position

order

Front matter: order positions a page; sidebar_position is read as it.

top level

favicon

faviconUrl

Image keys end in Url.

top level

logo

logoUrl

Image keys end in Url.

The Forumulate teamUpdated