> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Docs contribution guide

> How to add a page to this docs site: where the page-type patterns live, how to register a page in the docs.json nav, and how to write the source-pin test that keeps the page intact.

# Docs contribution guide

This page is for anyone who edits this docs tree — the whole guide is one
step-by-step "add a page" path plus a catalog of the page patterns that
recur across the \~750 pages in the corpus. If you prefer to read up on how
locales behave instead, see [Docs languages and
translations](/docs-languages); this page covers the page patterns
themselves, not localization.

## Where the page-type patterns live

Pages in this tree fall into a small set of recognizable patterns. Before
you open a blank `.mdx`, walk the current corpus for a sibling page of your
kind and copy its shape — that costs less than inventing a new layout, and
readers learn one shape per class of page.

* **Guides** (`guides/*.mdx`) — how-to pages a tenant follows end to end:
  onboarding walks, first sends, channel setup, console walkthroughs, and
  the hub **orientation maps** (see [The orientation page pattern](#the-orientation-page-pattern)).
  A guide is the class the reader "does something with."
* **Concepts** (`concepts/*.mdx`) — one-page explanations of a model or a
  capability: what it is, how its pieces fit, where its limits sit. A
  concept is the class the reader "understands something with."
* **Channels** (`channels/*.mdx`) and **Voice** (`voice/*.mdx`) —
  per-channel/per-surface anchor pages.
* **API Reference** (`api-reference/*.mdx`), **SDKs** (`sdks/*.mdx`),
  **Reference** (`reference/*.mdx`), **Legal** (`legal/*.mdx`) — the
  contract-facing and legal surface.
* The **Getting Started** anchor pages (`introduction.mdx`, `quickstart.mdx`,
  `authentication.mdx`, `docs-languages.mdx`, this page) — the pages a first
  reader of the whole site needs before anything else.

When no sibling matches your page's shape, pick the closest class above and
note the new shape in your commit message so the next writer sees it in
history.

## How to add a page

Adding a page is three steps. All three land in the same commit — a page
registered in `docs.json` but without a pin, or a pin without a nav
registration, is the failure mode this guide exists to stop.

### 1. Write the page with front matter

Every page opens with YAML front matter carrying the two strings the site
and the pin both depend on:

```
---
title: "Page title shown to the reader"
description: "One sentence on what this page answers — used by search and the pin."
---

# The page's H1 repeats the title
```

Titles and descriptions are what the docs search index puts in front of a
reader; write them to the reader's question, not to the internal feature
name. Match the voice of the sibling pages in your class (guides are
second-person, imperative; concepts are declarative; orientation maps are
pivot-style "What it answers" per tile).

### 2. Register the page in docs.json

`docs.json` at the tree root declares every nav group and page path. Find
the tab your page belongs to (`Documentation`, `Guides`, `API Reference`,
`SDKs`, `Reference`), then the group inside the tab, and place the page path
(without the `.mdx` extension, relative to the tree root) in the group's
`pages` array. **Register the English path only** — never the
locale-prefixed path (`tr/...`), because the locale prefix is rewritten by
the site (see
[Docs languages and translations](/docs-languages) rule 3) and the variant
wires itself in.

For most writers the group is one of "Getting Started", "Channels", "AI
Agents", "Concepts", or "Guides" under their tab; pick the group that already
holds your page's siblings.

### 3. Write the source-pin test

A **source-pin test** is a structural check (in a `__tests__` dir) that
asserts the page exists, its front matter is intact, its promised sections
are all present, and its `docs.json` registration reads exactly once. The
pin is what stops a later edit from quietly dropping a section, a tile, or
the nav registration — and it is **the only harness that can hold a .mdx**,
because an `.mdx` cannot be imported as a module.

Write the pin as `<page-slug>.source-pin.test.mjs` in the `__tests__`
directory adjacent to the page's own directory (e.g. `guides/__tests__` for
a guides page, `concepts/__tests__` for a concepts page, top-level
`__tests__` for a root page). The harness is the vitest `test` global when
present, falling back to `node:test`, so it runs under both drivers:

```js theme={null}
/**
 * STRUCTURAL-PIN: <page path> — <what class of page and what it pins>.
 * A docs .mdx cannot be imported, so this pins rendered file structure
 * (the only viable pin for a content surface).
 * DUAL-HARNESS SAFE: register through the vitest global when present,
 * falling back to node:test.
 */
import { readFileSync, existsSync } from "node:fs";
import { test as nodeTest } from "node:test";
import assert from "node:assert/strict";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";

const test = globalThis.test ?? nodeTest;
const HERE = dirname(fileURLToPath(import.meta.url));
const DOC = join(HERE, "..", "<page-file>.mdx");
const NAV = join(HERE, "..", "..", "docs.json");

test("<page> exists with valid frontmatter", () => {
  assert.ok(existsSync(DOC), "<page>.mdx missing");
  const src = readFileSync(DOC, "utf8");
  assert.ok(/^---\ntitle: "/.test(src), "frontmatter must open with a title");
  assert.ok(/description: "/.test(src), "frontmatter must carry a description");
});

test("<page> carries the sections it promises", () => {
  const src = readFileSync(DOC, "utf8");
  for (const marker of ["## <section heading>", "<promised surface>"])
    assert.ok(src.includes(marker), `missing section: ${marker}`);
});

test("<page> is registered in docs.json exactly once", () => {
  const raw = readFileSync(NAV, "utf8");
  let count = 0;
  JSON.stringify(JSON.parse(raw), (_k, v) => {
    if (v === "<page-path-without-extension>") count += 1;
    return v;
  });
  assert.equal(count, 1, "docs.json must reference <page> exactly once");
});
```

Run the pin yourself before committing (`node --test <your-pin>.test.mjs` —
a plain-node invocation of a content pin is sanctioned; the
`pnpm/tsc/vitest` ban does not apply to content pins the writer can run as
`node`). A pin you never ran is the single largest cause of merge-gate
failures.

## The orientation page pattern

The orientation map exists when a single dashboard hub accumulates enough
consoles that the reader's question is no longer "how do I use this
console" but "**where do I run this channel / what does each tile answer**" —
that threshold has been roughly **eight consoles on one hub** in this docs
set, and it is the point where the tree needs one page per hub instead of
leaf pages per console.

A hub orientation map (the Audience, Messages, and Developer hubs have one)
follows a fixed shape:

1. **The one-sentence question that brings the reader in**, answered by the
   whole map (e.g. *where do I run a channel?*).
2. A **tile map** — one section per console on the hub, each holding the
   console's live route in a code span (`/messages/sms`), the one-sentence
   question that console answers, **first-visit skip conditions** (what to
   do when the console has no sender / no registration yet), and a
   **Docs:** line of links to the in-depth guides behind that console.
3. A **closing table** that links the in-depth pages the map promised, so
   the reader's next click off the hub is already open.

Register the orientation page in the same `docs.json` group its hub's leaf
guides live in, and write a source-pin that asserts every tile route the
map claims. The [Messages hub orientation](/guides/messages-hub-orientation)
and its pin `guides/__tests__/messages-hub-orientation.source-pin.test.mjs`
are the canonical reference — every section heading, every tile route, and
every `Docs:` link the map promises is asserted there.

## The concept model page pattern

A concept page under `concepts/*.mdx` answers *how does this capability
actually work — what are its pieces, their order, and their limits?* in one
page a reader can read once and remember.

The shape that has worked across the corpus:

1. **A one-paragraph capability statement** at the top — the single
   paragraph that says what the capability does for the tenant, in the
   tenant's vocabulary (not the internal subsystem's).
2. **A model diagram or an ordered model section** — the piece-by-piece
   walk of how the capability fits together (the audit-ledger model's
   "three planes", the termination model's travel modes, every lifecycle's
   ordered states). Readers remember the model; the rest of the page is
   there to fill it in.
3. **Limits and boundaries** — what the capability deliberately does NOT
   do, where the limits sit, which surfaces move it (consoles, API routes,
   exports).
4. **Cross-links out** — a concept page ends by naming the guide(s) that
   put the model to work and the hub page that maps it.

The [audit ledger model](/concepts/audit-ledger-model) is the reference
shape: one paragraph of capability, the model in prose order, limits
spelled out, three links out. Its pin
(`concepts/__tests__/audit-ledger-model-nav.source-pin.test.mjs`) asserts
every section the page promises.

## The guide page pattern

A guide is a tenant-owned how-to: follow it end to end and one real outcome
lands (a first send, a channel registered, a guard set, a checklist walked).

The working shape across `guides/*.mdx`:

1. **Who the guide is for and the outcome it produces**, in the first
   paragraph.
2. **First-visit skip conditions** — the state a tenant in which this guide
   does not apply ("if you only need plain text, SMS covers it; skip this")
   and the prerequisite console state ("until a sender is registered the
   console routes you to the sender-onboarding flow first"). A guide that
   names its skip conditions saves the reader a dead walkthrough.
3. **Live console paths** — every console route the guide touches appears
   as a literal code span (`/messages/whatsapp`, `/audience/segments`), and
   the guide matches the live route 1:1 (the pin asserts these routes).
4. **Docs links** — the in-depth concept page(s) the guide leans on, in a
   closing "Docs:" line, so the guide never re-teaches the concept.

Every guide page must document **tenant-owned controls only** — settings
you set for your own workspace, the same carve-out the compliance posture
page ([Compliance posture overview](/compliance/posture-overview)) states;
the product has one platform-global guard (the US federal dialing window)
and everything else is tenant-configurable. The guide register is second
person, imperative voice.

## When to choose each pattern

* **If you are mapping a single dashboard hub with more than about eight
  consoles**, write one **orientation page** under `guides/`, with a
  tile-map pin.
* **If the reader's question is "how does X actually work" (a model, a
  lifecycle, a capability with pieces)**, write a **concept page** under
  `concepts/` (capability paragraph → model → limits → links out).
* **If the reader is DOING something end-to-end (a first send, a channel
  onboarding, a console walkthrough, a checklist)**, write a **guide**
  under `guides/` with the first-visit skip conditions named.
* **Every page** — register the English path in `docs.json` **in the same
  commit**, run the pin, and never wire a locale-prefixed path into nav.

A page that follows none of these shapes is the cost the tree pays per
pickup — pick the shape that matches, copy its sibling, and the next writer
inherits a pattern instead of a one-off.
