diff --git a/.claude/rules/mdx-capability-guides.md b/.claude/rules/mdx-capability-guides.md
new file mode 100644
index 0000000000..fa28bd8014
--- /dev/null
+++ b/.claude/rules/mdx-capability-guides.md
@@ -0,0 +1,96 @@
+---
+paths:
+ - "fern/products/*/pages/**/*.mdx"
+---
+
+# Capability guides — one capability, every surface
+
+Applies to a how-to that teaches one platform capability across more than one surface (SWML, Relay, the REST Calling API, the Browser SDK, Call Flow Builder). Exemplars: `fern/products/platform/pages/calling/voice/outbound-calling.mdx` (first-run walkthrough, complete samples) and `fern/products/platform/pages/calling/record-calls.mdx` (task sections, merged Relay and REST blocks). A single-surface how-to or an integration guide (the Vapi page) follows `mdx-style` alone. Voice, grounding, and `## Next steps` come from `mdx-style`; this rule fixes the skeleton and the sample conventions so guides match each other.
+
+## Skeleton
+
+Every guide answers the same questions in the same order: which product, how to start, how to do the thing, how to know it worked, where to go next.
+
+```text
+frontmatter: title = capability as a noun phrase ("Call streaming"); slug //;
+ description = one sentence naming the first-run outcome, then each extension in page order
+[reference-style link definitions]
+intro, 2–3 sentences, no heading: what the reader does, then "Start by , then ."
+## Pick the right product for capability matrix + one bullet per surface
+## Prepare for
+## How works (optional — see below)
+## (only for a reader starting from zero — see below)
+
+ ### Set your credentials and
+ ### (optional — "Choose a destination", "Run a WebSocket server")
+ ### ("Place the call", "Start the stream")
+ ### ("Answer the call", "Answer and speak") — the verification step
+
+## one per task the guide must cover
+ ### via one per surface that supports it
+## Examples
+ ###
+ #### via
+## Next steps
+```
+
+- **Pick the right product.** The first job of the page is to show which surface can do what, so the reader chooses before reading further. See "Capability matrix" below.
+- **Prepare.** "Have these values ready:" then bullets: Space URL, Project ID and API token with the **Voice** permission, a purchased number or verified caller ID, a destination the reader can answer, then topic-specific items. Hard constraints follow as titled ``s (trial and international limits, `wss://` only, TCPA). Add one pointer sentence when a reader might want a sibling guide instead.
+- **How it works.** Put it before the first run only when the reader can't interpret the result without the model (machine detection's outcome table). Otherwise conceptual depth goes after the first run, as its own `##` ("Handle the audio stream").
+- **First run.** A `` walkthrough is for a reader starting from zero: a first call, a first stream. A guide that picks up mid-stream (recording assumes you can already place a call) skips `` and opens with its first task section. The walkthrough targets the smallest thing the reader can confirm alone, usually calling their own phone: one code block per surface, no options, no error handling.
+- **Verification step** closes the ``: what the reader hears or sees on success, then the likeliest failure and what it means. A guide without `` puts the same check at the end of its first task section. Never skip it.
+- **Task sections** are the body of the guide. Name each `##` after what the reader wants ("Record the whole call"), never after the API surface that provides it ("The record_call verb"). One `###` per surface beneath it, surface named in the heading. Options get their own `##` after the task that introduces them ("Recording options: format, stereo, and direction").
+- **Track and control** is a task section like any other: REST gets `status_url`/`status_events` and a JSON callback payload; Relay gets `call.on(…)` handlers. One lifecycle diagram per surface family, themed SVG in `` with an `` mermaid `sequenceDiagram` twin (`mdx-diagrams`). Add `### Compare the surfaces` when defaults or parameter names differ by surface.
+- **Examples** hold the variations you could delete and still have a guide. If removing a section would leave a hole in the main path, it is a task section and belongs above `## Examples`. Each example is `### `: one-sentence goal, a titled `` when compliance applies, then a `####` per surface.
+- **Next steps** is required here as on every guide (`mdx-style`). Cards deepen this capability only.
+
+## Capability matrix
+
+One table under `## Pick the right product`: `| Function | SWML | Relay | REST Calling API | Browser SDK | Call Flow Builder |`, keeping every column where the capability plausibly lives and adding an API-area column (`Recordings API`) when a REST resource is part of the story. Rows are things the reader wants to do ("Get the recording's URL back in your own code, without a webhook"), not feature names. Three cell states:
+
+- `` shipped
+- `` not available
+- `` planned, not shipped — add the legend line under the table only when a cell uses it
+
+Follow the table with one bullet per column: the surface's canonical name linked to its reference, and one sentence on what it is for. Agents is never its own column: it generates SWML, so it can express anything the SWML column can.
+
+## Surface names
+
+Canonical names come from the glossary (`/docs/platform/glossary`); the ones this rule uses are `SWML`, `Relay`, `REST Calling API`, `Server SDKs`, `Browser SDK`, `Call Flow Builder`. Never "Realtime SDK", "Agents SDK", "the WebSocket API", or "CFB". How specific to be depends on where the name sits:
+
+- **Headings** name the surface in full: `### Stop recording the call via Relay`, `### Place the call via the REST Calling API`. Two shapes: ` via ` when the surface is a transport; ` from ` when it is a caller context ("Hold an AI chat conversation from a browser"). Every heading stands alone in a search result with no page title above it, so it carries the keywords: "Recording options: format, stereo, and direction", not "Recording options".
+- **Tab titles** are short: `REST`, `WebSocket (Relay)`.
+- **Code block titles** are ` — `: `Python — Relay client`, `TypeScript — Relay client`, `cURL — REST Calling API`, `Python — Agents`, `TypeScript — Agents`, `Python — REST client`, `TypeScript — REST client`, `JavaScript — Browser SDK`, `YAML`, `JSON`.
+
+## Surfaces: headings by default, tabs only inside Steps
+
+- A task shows each surface under its own `###`, never in tabs. Headings appear in the table of contents, in search, and in the Markdown export; tab panes hide from all three.
+- Inside ``, `###` is already the step heading, so a step that differs by surface uses `` with `` and ``. That is the only place tabs belong. Guides under one overview share a single `groupId` so the reader's choice follows them between pages; the voice guides use `outbound-api`.
+- **Relay and the REST Calling API are the same commands over two transports.** When only the transport differs (pausing a recording, stopping a stream), one `### via Relay` heading holds a single `` of `Python — Relay client`, `TypeScript — Relay client`, and `cURL — REST Calling API`. Split them into separate headings only when the flow genuinely differs: placing a call over HTTP with inline SWML is not the same flow as dialing over a live socket.
+- **SWML is one surface, authored several ways and delivered several ways.** Under `### via SWML`, show the document once, in the forms a reader authors it, in one ``: `Python — Agents` and `TypeScript — Agents` (`AgentBase`) first, then `YAML`, then `JSON`. Don't encourage hand-writing SWML where the SDK covers the verb. How the document reaches the call (served from your server, stored as a hosted script resource, passed inline in a REST `dial`) is a delivery choice: cover it once per guide, in the first-run section, and don't repeat it under every task. A REST `dial` that carries inline SWML is SWML delivery, not the REST Calling API surface.
+- Where the reference already documents a REST request, use `EndpointRequestSnippet` and `EndpointResponseSnippet` with a link to the reference page instead of hand-writing the request; describe only the fields the task needs. A first-run REST step that dials still ends with the "returns a call `id` and status `queued` … save the `id`" paragraph and ``.
+
+## Sample conventions
+
+- **Complete programs.** Every sample under `` and `## Examples` runs as pasted: imports, client setup, the call, and the wait or disconnect. A block under a task section may show only the changed lines, with line highlights (`{6-8}`), when its paragraph names the sample it extends.
+- **Header comments** on every complete sample. Python: `# Install: python -m pip install signalwire-sdk==3.4.1` then `# Save as .py and run: python .py`. TypeScript with no type annotations: `// Install: npm install @signalwire/sdk@2.0.5` then `// This sample also runs as JavaScript: save as .mjs,` / `// then run: node .mjs`. TypeScript that uses types: `// Save as .mts and run: npx tsx .mts`. Browser: `// Install: npm install @signalwire/js@4.0.0-rc.2 rxjs@7.8.2` plus the HTML elements the script expects, as comments.
+- **Pinned versions** are literal and identical across every guide: `signalwire-sdk==3.4.1`, `@signalwire/sdk@2.0.5`, `@signalwire/js@4.0.0-rc.2 rxjs@7.8.2`. Bump them in every guide in one PR, never one page at a time.
+- **Placeholders**: ``, ``, ``, ``, ``, ``, ``; new ones follow the same `` shape. Explain them once, in a `| Value | Replace with |` table before the first code block.
+- **Relay boilerplate**: `contexts=["default"]`, `timeout: 30` on the phone device, Python `async with client:` and `await call.wait_for_ended()`, TypeScript `await client.connect()` / `try … finally { await client.disconnect(); }` and `await call.waitForEnded()`. Hang up in an `on_completed`/`onCompleted` callback guarded by `call.state !== "ended"`.
+- **Builder gaps**: when the SDK's SWML builder lacks a verb, say so in a comment and use `add_verb`/`addVerb` with validation off for that verb only. Don't silently hand-write JSON where the builder works.
+- **Running example**: `Bayview Taxi` and its dispatcher `Ada` when the scenario has a business voice; a plain "Hello, welcome to SignalWire!" for pure mechanics (`mdx-style` Grounding).
+
+## Ships with the page
+
+- A `` on the parent overview (`fern/products/platform/pages/calling/voice/overview.mdx` for voice), and a fix for any FAQ or stale link there that the guide now answers.
+- The `fern/llms.txt` bullet: add or repoint, description copied from the page's frontmatter.
+- Redirects in `fern/docs.yml` only when the guide consolidates existing pages.
+- No changelog entry (documentation work). A nav yml entry only where the folder isn't folder-driven (`platform.yml` lists `pages/calling/` pages explicitly).
+
+## Before opening the PR
+
+Extract every fenced block and validate it against the pinned SDKs: JSON and YAML parse; bash syntax (`bash -n`); Python `py_compile` and `pyflakes`; TypeScript `tsc --strict` against the installed `@signalwire/sdk`; every SDK method, parameter, and event field checked against the installed package, not only the reference pages. Run each SWML builder chain and diff its output against the hand-written JSON sample beside it. Confirm every internal link against the target's `slug:` and every `#anchor` by hand. Then `yarn fern-md-check`.
+
+PR body sections: `## Summary` (bullets), `## Notes for reviewers` (cross-PR link dependencies, upstream SDK or doc inconsistencies worked around), `## Verification` (the checks above, stated as run), and the preview URLs of every page changed.
+
+*Sources: PRs #640 (write-guides template, record calls, AI chat), #660, #692, #693 (2026-09), reconciled 2026-09-16. [SW]*
diff --git a/.claude/rules/mdx-components.md b/.claude/rules/mdx-components.md
index daad576201..3079e5067d 100644
--- a/.claude/rules/mdx-components.md
+++ b/.claude/rules/mdx-components.md
@@ -16,15 +16,15 @@ Every component available on this site: Fern's full default library (description
| `Button` | Interactive button with styles, sizes, intents, icons | Unused |
| `Callout` → named variants `Note` `Info` `Warning` `Tip` `Check` `Error` `Success` `Launch` | Highlighted admonition block with intent and optional custom icon | House set: `Note`/`Tip`/`Warning`/`Info` = prereq / suggestion / hazard / pointer; `Error` rare (×2). Write the named variant, never generic `` |
| `Card` / `CardGroup` | Boxed content and link grids | In use — overviews and `## Next steps`. `` is an undocumented legacy alias (×2 old pages); use `CardGroup` |
-| Code blocks: fenced + `CodeBlocks` / `CodeBlock` | Syntax highlighting with `title="…"`, line highlights `{6,9}`, focus | House: single example = plain fence; several languages/formats = `CodeBlocks` of titled `CodeBlock`s. `CodeGroup` (Fern's `for=`-synced grouping) unused here |
+| Code blocks: fenced + `CodeBlocks` / `CodeBlock` | Syntax highlighting with `title="…"`, line highlights `{6,9}`, focus | House: single example = plain fence; several languages/formats = `CodeBlocks` of titled `CodeBlock`s, titled ` — ` (`Python — REST client`) in guides. `CodeGroup` (Fern's `for=`-synced grouping) unused here |
| `Copy` | Click-to-copy for arbitrary text | Unused |
| `Download` | Lets readers download PDFs, files, ZIP bundles | Unused |
| `EndpointRequestSnippet` | Embeds an endpoint's request example from the API Reference | In use — pair with a link to the reference page |
-| `EndpointResponseSnippet` | Embeds an endpoint's response example | In use |
+| `EndpointResponseSnippet` | Embeds an endpoint's response example | In use — prefer it, with `EndpointRequestSnippet`, over a hand-written REST request the reference already documents |
| `EndpointSchemaSnippet` | Embeds an endpoint's schema | In use — heavily, in SDK reference |
| `Files` | Interactive file tree with expandable folders | Unused |
| `Frame` | Image or diagram with caption and background variants | In use — wrap meaningful images (themed SVGs excepted — see `mdx-diagrams`) |
-| `Icon` | Inline Font Awesome icon with size/color/style | Rare (×2) — short-form icon syntax per `mdx-mechanics` |
+| `Icon` | Inline Font Awesome icon with size/color/style | In use — capability matrices in guides use `regular circle-check` / `regular circle-xmark` with `color="var(--status-success)"` / `color="var(--status-error)"` (`mdx-capability-guides`); otherwise rare. Short-form icon syntax per `mdx-mechanics` |
| `If` | Shows/hides content by product, version, or reader role | Unused |
| `Indent` | Left indentation for nested parameters and hierarchy | In use — nests `ParamField`s (blank lines inside) |
| `Markdown src="…"` | Includes a reusable snippet from `fern/snippets/`; supports `{{param}}` substitution | In use — self-closing; only genuinely shared content |
@@ -35,7 +35,7 @@ Every component available on this site: Fern's full default library (description
| `SchemaSnippet` | Displays a type definition as a JSON code block | Unused |
| `Steps` / `Step` | Auto-numbered sequential walkthrough | In use — ordered procedures in guides |
| Tables (markdown) | Rows and columns, optional sticky header | Native markdown — genuine enumerations only (see `mdx-style`) |
-| `Tabs` / `Tab` | Tabbed views with language synchronization | In use — switchable non-code variants |
+| `Tabs` / `Tab` | Tabbed views with language synchronization | In use — switchable non-code variants. In guides, surfaces get `###` headings, not tabs; the one exception is a step inside `` (`mdx-capability-guides`) |
| `Tooltip` | Contextual info on hover, for text and code | Rare (×6) — only where it genuinely helps |
| `Versions` | Content that switches with a version dropdown | Unused |
| `WebhookPayloadSnippet` | Embeds a webhook payload example from the API Reference | In use |
diff --git a/.claude/rules/mdx-diagrams.md b/.claude/rules/mdx-diagrams.md
index 63476c5f5f..e6e09dada8 100644
--- a/.claude/rules/mdx-diagrams.md
+++ b/.claude/rules/mdx-diagrams.md
@@ -9,7 +9,8 @@ paths:
## Themed SVG diagrams
Applies to hand-authored SVG diagrams under `fern/assets/images/img/`.
-Exemplar: `ai-agent-flow-themed.svg`.
+Exemplar: `ai-agent-flow-themed.svg`; for sequence diagrams,
+`outbound-call-lifecycle-themed.svg` and `machine-detection-flow-themed.svg`.
### One file, both modes
diff --git a/.claude/rules/mdx-style.md b/.claude/rules/mdx-style.md
index ce452e112a..e38055529d 100644
--- a/.claude/rules/mdx-style.md
+++ b/.claude/rules/mdx-style.md
@@ -6,14 +6,14 @@ paths:
# Writing style — SignalWire docs
-How pages should read. Mechanics: `mdx-mechanics`; components: `mdx-components`; ParamField/reference patterns: `mdx-reference-pages`; what's generated vs editable: `docs-conventions`.
+How pages should read. Mechanics: `mdx-mechanics`; components: `mdx-components`; ParamField/reference patterns: `mdx-reference-pages`; multi-surface how-to skeleton: `mdx-capability-guides`; what's generated vs editable: `docs-conventions`.
## Page types (Diátaxis) [D]
Pick by asking: is the reader **learning or working**, and do they need **action or understanding**?
- **Tutorial** (learning + action; get-started paths): a lesson that guarantees a working result. One path, concrete steps, visible progress early. Voice: encouraging and steady — build the reader's confidence. Keep out: options, digressions, theory.
-- **How-to guide** (working + action): starts from a goal, assumes setup. Voice: direct. Verb-first title, conditions before instructions, only meaningful choices. Keep out: re-teaching basics (link them), exhaustive edge cases.
+- **How-to guide** (working + action): starts from a goal, assumes setup. Voice: direct. Verb-first title, conditions before instructions, only meaningful choices. Keep out: re-teaching basics (link them), exhaustive edge cases. A how-to that teaches one capability across several surfaces follows the `mdx-capability-guides` skeleton.
- **Reference** (working + lookup): austere, neutral, factual, complete, structured like the product. Keep out: persuasion, instruction beyond usage notes.
- **Explanation** (learning + understanding): why it works this way. Voice: discursive and reflective — the one place tradeoffs, background, and opinions belong. Keep out: step-by-step procedures.
@@ -49,12 +49,12 @@ Tutorials and how-tos give the reader a way to confirm the result — a command
- Customer-facing only: no internal implementation detail (backend endpoints, transports, engine-side params). Placeholder data only — no real names, emails, or domains.
- Examples do real work: tool call → your server → system of record → response. No knowledge-trivia bots.
- Examples run as written: real imports, every required parameter, and only placeholders the reader can obviously fill. Verify each against the spec or SDK source — and against an actual run when the page ships a whole flow.
-- A cluster of guides shares one running example, so cross-links compound instead of restarting context. The AI guides use Bayview Taxi and its dispatcher Ada across `platform/pages/ai/overview.mdx` and `platform/pages/ai/guides/**`; adopt a cluster's existing cast when you add a page to it, and introduce a new one only for a new cluster. [SW]
+- A cluster of guides shares one running example, so cross-links compound instead of restarting context. The AI guides use Bayview Taxi and its dispatcher Ada across `platform/pages/ai/overview.mdx` and `platform/pages/ai/guides/**`, and the calling guides under `platform/pages/calling/voice/` borrow the same cast whenever a scenario has a business voice; adopt a cluster's existing cast when you add a page to it, and introduce a new one only for a new cluster. [SW]
- Don't cite other vendors' docs as authority; never disparage anyone's product.
## Modes
-- **New page**: pick the type above, then mirror the best sibling page of that type; frontmatter per `mdx-mechanics`.
+- **New page**: pick the type above. A multi-surface how-to follows the `mdx-capability-guides` skeleton and its exemplar; anything else mirrors the best sibling page of that type. Frontmatter per `mdx-mechanics`.
- **Editing**: everything you write or rewrite follows these rules; leave untouched text alone and flag its violations in your summary instead. Preserve meaning, structure, and length unless asked. "Polish" means structural first — mental model, complete example — then sentences.
- Legacy-page tells (strip from parts you rewrite; don't sweep the rest without an explicit cleanup task): `id:`/`x-custom`/`ported_from_readme` frontmatter, "In this guide…" intros, editorial "we", Title-Case headings. [SW]
diff --git a/CLAUDE.md b/CLAUDE.md
index 8d947d3f24..5d678b7ae3 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -4,7 +4,7 @@ Fern-powered documentation site. Two sources of truth: MDX pages under `fern/pro
## Authoring rules
-`.claude/rules/` holds the binding conventions. They are path-scoped and fire when a matching file is read — so when you create a file from scratch, open the matching rule first: `mdx-style`, `mdx-mechanics`, `mdx-components`, `mdx-reference-pages`, `docs-conventions` for anything under `fern/`; `spec-conventions` for `specs/`. Rationale and depth behind the style rules: `.claude/docs/mdx-style-reference.md` (read on demand, never auto-loads).
+`.claude/rules/` holds the binding conventions. They are path-scoped and fire when a matching file is read — so when you create a file from scratch, open the matching rule first: `mdx-style`, `mdx-mechanics`, `mdx-components`, `mdx-reference-pages`, `docs-conventions` for anything under `fern/`; `mdx-capability-guides` for a how-to that covers one capability across several surfaces; `spec-conventions` for `specs/`. Rationale and depth behind the style rules: `.claude/docs/mdx-style-reference.md` (read on demand, never auto-loads).
## Root llms.txt is hand-maintained