Octri

octri.json

octri.json is your docs site's configuration as one JSON file. Appearance, navigation, the home page, redirects, top bar buttons, social links, SEO, page feedback, the Try it playground, custom code and custom components: everything a setting can describe is here, in one place you can read top to bottom and change in a few keystrokes.

You open it from the Files panel in API Studio, under Config.

The file is a view, not a copy

Octri does not store octri.json. It is built from your project's current settings each time you open it, and saving writes your changes back to those same settings. Change the theme in Docs settings and the file shows it the next time you open it; change the file and Docs settings shows it. The two can never drift apart.

Editing the file

Click octri.json under Config in the Files panel. It opens in the code editor in the middle of API Studio.

The file starts with a $schema line:

json
{
  "$schema": "https://app.octri.dev/schemas/octri.json"
}

The editor reads that schema as you type. It suggests every key that fits where your cursor is, lists the allowed values for each setting, shows what a setting does when you hover it, and underlines a mistake before you save. The schema is public, so any editor that understands JSON Schema, such as VS Code, gives you the same help if you edit a copy on your own machine. Bring that copy back with ⋯ → Import into octri.json on the Config group: its text becomes an unsaved edit in the editor, so you can read it over before you save.

Save with the Save button or Ctrl+S (Cmd+S on a Mac). Revert throws away unsaved edits.

When a save reaches readers

The label above the editor reads Draft and live, because the file holds settings that follow two different rules.

Saved as a draft, live when you PublishLive as soon as you save
navigation.tabsappearance
navigation.homenavigation.buttons
navigation.redirectssocials
seo
feedback
playground
customCode
components

Each setting follows the same rule it follows everywhere else in the studio. Your sidebar, home page and redirects go out together in the next release, alongside your page drafts. A colour change or a new social link is on the docs site the moment you save.

Most of the file applies on save

Only navigation, the home page and redirects wait for Publish. Changing appearance, seo or playground changes your live docs straight away, so check those changes in the preview first.

How a save is read

Saving compares the file you saved with the file as it was when you opened it, and writes only the difference.

  • Only what you changed is written. A setting you did not touch is never sent. If someone changes the logo in Docs settings while you edit the sidebar here, your save keeps their logo.
  • Every setting is optional. Leaving one out means its default.
  • Deleting a setting resets it to its default. Remove "layout": "floating" and the layout goes back to full.
  • Deleting a whole section leaves it alone. If appearance, navigation or socials is missing entirely, nothing in it changes. Deleting navigation by accident cannot empty your docs site.
  • Keys are strict. A misspelt key is an error, not a setting that silently does nothing.
  • Nothing is written if anything is wrong. A refused save lists every problem with its place in the file, for example navigation.tabs[1].items[0].page, and you fix them and save again.
Lists inside navigation are replaced whole

Inside navigation, tabs, buttons and redirects are lists, and a save writes the list exactly as the file has it. Deleting the buttons or redirects key removes every button or every rule, and deleting home leaves the site with no home page. To keep them, leave the keys in place and edit the entries.

If the same settings change somewhere else while you have the file open, a notice offers Load latest. Saving anyway still writes only the parts you changed.

The shape of the file

Every section at the top level is optional.

json
{
  "$schema": "https://app.octri.dev/schemas/octri.json",
  "appearance": { },
  "navigation": { },
  "socials": { },
  "seo": { },
  "feedback": { },
  "playground": { },
  "customCode": { },
  "components": { }
}
SectionWhat it holds
appearanceColours, logo, favicon, fonts, text sizes, layout, sidebar, top bar, code themes, the On this page rail, the repository link
navigationTabs, sidebars, the home page, redirects and top bar buttons
socialsSocial icons and your own links under each page
seoTitles, descriptions, link previews and indexing
feedbackThe "Was this helpful?" question
playgroundThe Try it panel on endpoint pages
customCodeWhether your custom CSS and HTML load
componentsDescriptions and props of your custom components

customCode and components appear on the Growth plan and above. On a lower plan they are left out of the file, and adding them is refused with "Custom code is on the Growth plan and above."

appearance

How the docs site looks. Every setting here applies on save, exactly like the Appearance panel in Docs settings.

json
"appearance": {
  "colors": { "primary": "#FF6A00" },
  "colorScheme": "dark",
  "themeToggle": false,
  "layout": "floating",
  "contentWidth": "default",
  "corners": "default",
  "logo": {
    "light": "https://example.com/logo.svg",
    "dark": "https://example.com/logo-dark.svg",
    "link": "https://example.com",
    "show": true
  },
  "favicon": "https://example.com/favicon.png",
  "fonts": { "body": "inter", "heading": "body", "code": "jetbrains-mono" },
  "textSize": { "sidebar": "large", "content": "large", "code": "smaller" },
  "sidebar": { "style": "default", "width": "wide", "resizable": true, "lines": false, "icons": false },
  "navbar": { "layout": "default", "search": "centered", "surface": "solid" },
  "codeTheme": { "light": "octri", "dark": "octri" },
  "tableOfContents": { "endpoints": true, "pages": true },
  "repository": { "url": "https://github.com/acme/api", "stars": true }
}

Colour, scheme and layout

SettingValuesDefaultWhat it does
colors.primary#rrggbb#ff6a00The brand colour: links, active sidebar rows, primary buttons
colorSchemesystem, light, darksystemThe scheme a reader starts in. system follows their operating system
themeToggletrue, falsetrueShows the light and dark switch in the top bar
layoutfull, contained, framed, floatingfullHow the page is framed (see below)
contentWidthnarrow, default, widedefaultWidth of the reading column
cornerssharp, default, roundeddefaultCorner rounding across the site. rounded also turns buttons and badges into pills

The four layouts:

  • full: the sidebar and the On this page rail sit at the screen edges.
  • contained: the whole page is one centred block.
  • framed: the content sits on a raised panel.
  • floating: the sidebar is a card held off the edges of the screen.

Logo and favicon

SettingDefaultWhat it does
logo.lightemptyLogo image URL for the light scheme, and for both schemes when logo.dark is empty
logo.darkemptyLogo image URL for the dark scheme. Empty uses the light logo
logo.linkemptyWhere clicking the logo goes: a path or a full URL. Empty means the docs home
logo.showtruefalse shows the project name instead of the logo, and keeps the URLs above for later
faviconemptyFavicon image URL

Image URLs are up to 500 characters. Upload images in Docs settings first, then copy their URLs here.

Fonts

SettingValuesDefault
fonts.bodymanrope, inter, plus-jakarta-sans, dm-sans, system, googlemanrope
fonts.headingbody, manrope, inter, plus-jakarta-sans, dm-sans, googlebody
fonts.codejetbrains-mono, fira-code, ibm-plex-mono, system, googlejetbrains-mono
fonts.google.bodyAny Google Fonts family name, e.g. "Roboto"empty
fonts.google.headingA Google Fonts family nameempty
fonts.google.codeA Google Fonts family nameempty

heading: "body" uses the body face for headings. Set any slot to google and it uses the family named in the matching fonts.google field:

json
"fonts": {
  "body": "google",
  "code": "google",
  "google": { "body": "Source Sans 3", "code": "Source Code Pro" }
}

Text size

textSize.sidebar, textSize.content and textSize.code each take smaller, large or larger. The defaults are large for the sidebar and content, and smaller for code, so code examples sit a step below the page text.

SettingValuesDefaultWhat it does
sidebar.styledefault, bordered, minimaldefaultHow sidebar rows are drawn
sidebar.widthcompact, default, widedefaultThe sidebar's starting width
sidebar.resizabletrue, falsetrueLets readers drag the sidebar wider or narrower
sidebar.linestrue, falsefalseDraws a line down each folder's children
sidebar.iconstrue, falsetrueShows folder, page and link icons

Top bar

SettingValuesDefaultWhat it does
navbar.layoutdefault, stacked, centereddefaultdefault is one row. stacked puts the tabs on a second row. centered puts the tabs in the middle
navbar.searchsidebar, centered, buttonsidebarSearch at the top of the sidebar, centred in the top bar, or as a button
navbar.surfacesolid, frosted, transparent, coloredfrostedWhat the top bar is made of
navbar.color.light#rrggbb or emptyemptyThe top bar fill in the light scheme. Empty uses the primary colour
navbar.color.dark#rrggbb or emptyemptyThe top bar fill in the dark scheme. Empty uses the primary colour

navbar.color is only read while surface is colored.

Code, rail and repository

SettingValuesDefaultWhat it does
codeTheme.lightoctri, github, monokai, nord, draculaoctriCode highlighting in the light scheme
codeTheme.darkSame as aboveoctriCode highlighting in the dark scheme
tableOfContents.endpointstrue, falsefalseThe On this page rail on endpoint pages
tableOfContents.pagestrue, falsetrueThe On this page rail on written pages
repository.urlA repository URL, or emptyemptyYour source repository, linked from the top bar
repository.starstrue, falsefalseShows the repository's star count beside the link

Your custom stylesheet is not in octri.json. It is its own file, styles/custom.css, in the Files panel.

Your docs site's structure: the tabs along the top bar, the sidebar under each one, the home page, redirects and the top bar buttons.

json
"navigation": {
  "tabs": [
    {
      "label": "Guides",
      "type": "content",
      "path": "guides",
      "items": [
        {
          "folder": "Getting started",
          "items": ["guides/getting-started/quickstart", "guides/getting-started/authentication"]
        }
      ]
    },
    { "label": "API Reference", "type": "reference", "path": "api-reference" },
    { "label": "Changelog", "type": "changelog" },
    { "label": "Status", "type": "link", "url": "https://status.example.com" }
  ],
  "home": { "page": "guides/getting-started/quickstart" },
  "redirects": [{ "from": "/old-guides/*", "to": "/guides/*" }],
  "buttons": [{ "label": "Get an API key", "url": "https://example.com/signup", "style": "primary" }]
}

Tabs

tabs lists the top bar's tabs from left to right, up to 50. It is saved as a draft and goes live when you publish.

FieldWhat it does
labelThe tab's name in the top bar. Required
typeWhat the tab is (see below). Required
pathThe tab's first URL segment, e.g. guides. Leave it out to follow the label. Lowercase letters, numbers and single dashes
urlWhere a link tab goes
iconAn icon from the docs icon set, e.g. rocket. none shows no icon
hiddentrue keeps the tab out of the published top bar
itemsThe tab's sidebar, top to bottom (see Sidebar rows)
hidePages and endpoints this tab would list on its own that readers should not see

The four tab types:

typeWhat it is
contentA sidebar you build yourself from items
referenceEvery endpoint in your spec, kept current for you as the spec changes
changelogYour project's release feed. It has no sidebar
linkA top bar link to url, on another site or this one. It has no sidebar

A reference tab lists every endpoint without you naming them. Its items hold only the folders you arranged by hand; every other endpoint is listed for you. To keep an endpoint out of the reference sidebar, name it in hide:

json
{
  "label": "API Reference",
  "type": "reference",
  "path": "api-reference",
  "hide": ["GET /internal/health", "POST /internal/reindex"]
}

Two tabs cannot share a path, and a tab cannot take a path the docs site uses itself: api, changelog, docs, gate, no-project, not-found and theme-test. A refused path says so: "/api is used by the docs site itself. Pick another path."

A row in items takes one of five shapes. The simplest is a bare string.

A page is named by where it sits: its tab, its folders, then its name, the same path as its URL and as its file under pages/ in the Files panel.

json
"guides/getting-started/quickstart"
json
{
  "page": "guides/getting-started/quickstart",
  "label": "Quickstart",
  "icon": "rocket",
  "hidden": false,
  "path": "quickstart"
}

The fields a row can carry:

FieldRowsWhat it does
labelpage, endpoint, linkThe sidebar text. Leave it out to use the page's or endpoint's own title. Required on a link
iconallAn icon from the docs icon set, in lowercase with dashes. none shows no icon. Leave it out for the default mark
hiddenalltrue keeps the row out of the published sidebar. The page itself still exists
pathpage, endpoint, folderThis entry's own URL segment. Leave it out to follow the name
badgefolderA short word after the folder's name, up to 24 characters, e.g. New
badgeColorfolderaccent, gray, green, blue, amber, red or purple. accent is your primary colour
pagefolderA page the folder's name opens when clicked
collapsiblefolderfalse keeps the folder always open, with no chevron

A folder named "" groups its rows under no heading, for rows that belong together but need no title.

Naming a page that does not exist creates it

A page path that matches no page, such as "guides/getting-started/webhooks", makes a new page called webhooks in that place when you save, with a line of starter text for you to replace, just as Add → Page in the sidebar would. Its name must use lowercase letters, numbers and single dashes. If a page with that name already exists somewhere else, the save is refused and tells you its real path: No page at "guides/webhooks". The page named "webhooks" is at "guides/events/webhooks"; name it by that path.

Paths are read against the file as you opened it. If you move a row into another folder, keep naming the page by its old path in that save; the next time you open the file, it shows the new one.

Home page

home names the page served at the root of your docs site, /. It is saved as a draft.

json
"home": { "page": "guides/overview", "hideSidebar": true }
FieldWhat it does
pageThe page shown at /, named by its path
hideSidebartrue shows the home page without the sidebar and the On this page rail, for a landing page look

Set "home": null to have no home page.

Redirects

redirects sends readers from addresses that no longer exist to where the content lives now. Rules are checked in order, up to 500 of them, and are saved as a draft so a rename and the rule that keeps its old address working go live together.

json
"redirects": [
  { "from": "/getting-started", "to": "/guides/quickstart" },
  { "from": "/v1/*", "to": "/guides/*" },
  { "from": "/guides/old-page", "to": "/guides/new-page", "force": true }
]
FieldWhat it does
fromThe old path on your docs site, starting with /. End it in /* to match that path and everything under it
toWhere readers go instead, a path on the same site. End it in /* to carry the rest of the path across
forcetrue for a page that redirects even though it is still published. Set from the page's own dialog

How the rules behave:

  • A live page always wins. A rule only answers for a path where nothing is published, so a redirect can never hide a working page. The exception is a force rule.
  • Wildcards carry the rest of the path. With /v1/* → /guides/*, a reader at /v1/auth/keys lands on /guides/auth/keys. A to can end in /* only when its from does.
  • force rules come from the page dialog. Choosing Redirect to another page in a page's dialog writes a rule with force: true that names both pages by their permanent address, so renaming either page never breaks it. A force rule cannot use wildcards.
  • Octri already redirects old addresses of your own pages. When a page moves between folders, readers are sent on without a rule. Write rules for what Octri cannot guess: a page whose name changed, a section split in two, or paths from a docs site you had before Octri.

A rule is refused when:

ProblemMessage
The path is on another siteUse a path on your docs site, like /guides/install. Other sites can't be redirected to.
The path does not start with /Start the path with /.
The path has ? or #Leave out ? and #: a redirect matches the path only.
A * is anywhere but the endA * can only end the path, after a slash, as in /old/*.
from is the home pageThe docs home page can't be redirected.
from is an address the docs site uses itself/api is used by the docs site itself, so it can't be redirected.
from and to are the sameThis sends readers back to the same path.
The wildcard would loopThis sends readers somewhere inside the old path, which would redirect again and again.
The same from appears twice/old is already redirected above.

In the editor each message starts with the rule's number, e.g. Redirect 3: Start the path with /.

Top bar buttons

buttons adds up to 3 call-to-action buttons at the right of the top bar. They apply on save.

json
"buttons": [
  { "label": "Dashboard", "url": "https://app.example.com", "style": "outline" },
  { "label": "Get an API key", "url": "https://app.example.com/signup", "style": "primary" }
]
FieldWhat it does
labelThe button's text, up to 40 characters. Required
urlAn http(s) URL, a mailto: address, or a path starting with /
styleplain text, an outline button, or a filled primary button. Defaults to plain

Keep primary for the one action you most want readers to take.

socials

Social icons and links of your own, shown under each page. They apply on save.

json
"socials": {
  "github": "https://github.com/acme",
  "x": "https://x.com/acme",
  "discord": "https://discord.gg/acme",
  "custom": [
    { "label": "Community forum", "url": "https://forum.acme.com", "icon": "users" },
    { "label": "Book a demo", "url": "https://acme.com/demo", "icon": "" }
  ]
}

Each platform takes its URL: github, gitlab, x, linkedin, youtube, instagram, facebook, tiktok, reddit, discord, slack, telegram, whatsapp, twitch, mastodon, medium, devto, stackoverflow, npm, producthunt and website. Delete a platform's line to remove its icon.

custom holds up to 5 links of your own, in order. Each has a label (up to 40 characters), a url, and an icon from the docs icon set. Set icon to "" to show the label as a button instead of an icon.

seo

How your docs appear in search results and link previews. These settings apply on save.

json
"seo": {
  "enabled": true,
  "titleMode": "dynamic",
  "title": "Acme Docs",
  "titleTemplate": "%s · Acme Docs",
  "description": "Guides and API reference for the Acme payments API.",
  "ogImage": "https://acme.com/og.png",
  "keywords": "payments, api, sdk",
  "indexable": true,
  "twitter": "acme"
}
SettingDefaultWhat it does
enabledtruefalse turns off all search and social metadata: no link previews, no canonical links, no sitemap, no llms.txt, and every page asks not to be indexed
titleModedynamicdynamic gives each page its own title through titleTemplate. static uses title on every page
titleemptyThe site's title, up to 120 characters. Empty uses the project name
titleTemplateemptyHow a page title is written in dynamic mode. %s is the page's title. Empty means %s | <title>
descriptionemptyWhat search results and link previews show for the home page, and for any page without a description of its own. Up to 300 characters
ogImageemptyThe image link previews show on social sites and chat apps
keywordsemptyKeywords, separated by commas
indexabletruefalse asks search engines not to index or crawl the docs. Link previews keep working
twitteremptyYour X handle for link previews, without the @

A page's own description is the description in its page file's frontmatter. See SEO for how titles and descriptions are put together.

feedback

json
"feedback": { "enabled": true }

true asks "Was this helpful?" at the bottom of every page. Votes are anonymous and show in Analytics. It applies on save and defaults to false.

playground

The Try it panel on endpoint pages, where readers send real requests to your API. These settings apply on save.

json
"playground": {
  "enabled": true,
  "baseUrl": "https://sandbox.acme.com/v1",
  "auth": { "type": "bearer", "headerName": "Authorization" },
  "allowedOrigins": ["https://acme.com"],
  "timeoutMs": 15000
}
SettingDefaultWhat it does
enabledfalseShows Try it on endpoint pages
baseUrlemptyWhere Try it requests go. Empty follows your project's API server, as your SDKs do. Point it at a sandbox to keep test traffic off production
auth.typeinheritThe credential a reader fills in: inherit, none, apiKey, basic, bearer or oauth2. inherit follows your project's API settings
auth.headerNameAuthorizationThe header the credential is sent in, e.g. X-API-Key
allowedOriginsnoneSites besides your docs that may send requests through the playground, up to 20, e.g. https://acme.com
timeoutMs10000How long a request may run before it is abandoned, from 1000 to 60000 milliseconds

auth.type and auth.headerName are saved together, so changing one writes both.

customCode

json
"customCode": { "enabled": true }

true loads the files in custom-code/ on every docs page: styles.css, head.html for the page's <head>, and body-end.html for the end of its <body>. false keeps those files saved but unused, which is a quick way to rule them out when something looks wrong. The code itself is edited in those files, not here. Growth plan and above; applies on save.

components

The descriptions and props of your custom components, keyed by tag. Growth plan and above; applies on save.

json
"components": {
  "PlanCard": {
    "description": "A pricing plan with its price and a call to action.",
    "props": [
      { "name": "title", "type": "string", "description": "The plan's name." },
      { "name": "price", "type": "number", "default": "0" },
      { "name": "featured", "type": "boolean", "default": "false" },
      { "name": "accent", "type": "color" },
      { "name": "tier", "type": "select", "options": ["free", "pro", "enterprise"], "default": "pro" },
      { "name": "children", "type": "node", "description": "What the plan includes." }
    ]
  }
}
FieldWhat it does
descriptionWhat the component is for, shown in the editor's component menu
propsThe props an author can set, in the order the editor lists them, up to 40

Each prop has:

FieldWhat it does
nameWhat an author writes in the tag: title in <PlanCard title="Pro">. Letters, numbers and underscores, not starting with a number
typestring, number, boolean, color, node (takes markup) or select (takes one of options)
defaultThe value used when an author leaves the prop out
descriptionHelp text the editor shows beside the prop
optionsThe choices a select prop offers, in order, up to 20. A select prop needs at least one

This section edits components that already exist. Create a component from ⋯ on the Components group in the Files panel, or in Docs settings; its template and stylesheet are files under components/. Naming a tag that does not exist is refused with No component "Hero". Create components in Docs settings → Components; this file edits the ones that exist. A component you leave out of the list is left as it is, never deleted.

When a save is refused

A refused save writes nothing and lists each problem with where it is in the file. The ones you are most likely to meet:

MessageWhat to do
Not valid JSON: …A missing comma, an extra trailing comma or an unclosed bracket. The line number points at it
Unrecognized key(s) in object: 'colour'A misspelt or unknown key. Check the spelling against this page, or let the editor suggest it
A colour is #rrggbb, e.g. #ff6a00.Colours are six-digit hex, with the #
No endpoint GET /user in this project.The method or path does not match your spec. Copy it from the endpoint page
No page at "…". A new page's name uses lowercase letters, numbers and single dashes.The last part of a page path is not a valid page name
No page at "…". The page named "…" is at "…"; name it by that path.The page exists somewhere else. Use the path the message gives
A row needs one of "page", "endpoint", "link" or "folder", or is a bare string.A row object has none of the four keys that say what it is
A nav button links to an http(s) URL, a mailto: address, or a path starting with /.Fix the button's url
A select prop needs at least one choice in options.Add options to the select prop
Custom code is on the Growth plan and above.Remove the change to customCode or components, or upgrade