---
title: Write for agents & GEO
description: "Authoring for agents and the answer engines that cite you, in two
  halves: what to write (the non-obvious, not restatement) and how to structure
  it (lead with the answer, question-form headings, self-contained sections)."
related:
  - title: Evals
    href: /docs/concepts/evals
    description: The eval evidence behind 'document the gotchas' and 'lead with the answer'.
  - title: Optimize docs for agents
    href: /docs/build/optimize-docs-for-agents
    description: Generate the discovery and attribution files once your content
      earns its keep.
---
Writing docs agents and answer engines actually use comes down to two things: **write the non-obvious** (not a restatement of your types), and **structure it so a model can find the answer, quote it, and trust it**. This page covers both — and follows its own rules.

Both halves are backed by the repo's [evals](/docs/concepts/evals): docs earn their keep by stopping agents from *confidently guessing wrong*, and that only works if the right fact is present **and** structured so the model actually reaches it. (The hard part isn't generating `llms.txt` or the markdown mirrors — the pipeline does that — it's the content itself.)

## What to write: the non-obvious

> ℹ️ **Info:**
> Document the non-obvious. Error conditions, defaults, ordering constraints, and surprising behavior pay off. Restating what your CLI --help, .d.ts types, or README already expose adds \~0.

### Why restatements add nothing

An agent reading your library already has its compiled code, type signatures, and CLI help text in context. A doc page that paraphrases a function signature gives the agent a second copy of something it can already see — no new information, just more tokens to read.

The repo's [evals](/docs/concepts/evals) measured this directly. Per-fixture lift, pooled across five models, concentrated **entirely** on behavioral gotchas; conventional restatements were flat:

|Fixture|What it tests|Lift|
|--|--|--|
|`nav-unknown-group`|A page declares a `group` the config doesn't know → the build fails|**+28**|
|`search-when-embeddings`|When the default BM25 index is enough vs. when to add embeddings|**+28**|
|`explain-cli-flag`, `mounted-changelog-urls`, `bundle-rationale`, `custom-generate-script`|Facts already in `--help`, the `.d.ts` types, or the README|−2 to +6|

The mechanism is that docs stop agents from **confidently guessing wrong**. Without the `nav-unknown-group` doc, agents don't say "I don't know" — they assert the *wrong* answer. With it, the confident-wrong rate drops to near zero. The conventional fixtures don't move because the agent could already recover those facts from the package itself.

### The litmus test

Before you write a page or paragraph, ask:

> Could the agent recover this from the type signature, the CLI help, or by reading the code?

* **Yes** → skip it, or compress it to a one-line pointer. The package already self-documents it.
* **No** → write it. This is where the docs earn their keep.

### What counts as non-obvious

1. **Error conditions** What fails, and the exact failure mode. "An undeclared `group` makes the build fail" is invisible in the types — the field is just a `string`.

2. **Defaults and when to override them** The default behavior *and* the rule for choosing otherwise. "Search uses a static BM25 index by default; add embeddings only when…" — a decision boundary no signature encodes.

3. **Ordering and sequencing constraints** What must run before what. "Run `generate` before your app build," "register `remarkInclude` before the default plugin stack" — order that types can't enforce.

4. **Surprising behavior** Anything that violates a reasonable default expectation — silent fallbacks, values that look interchangeable but aren't, side effects that aren't obvious from the call site.

### Write it like a gotcha

Lead with the surprising fact, not the API. Compare:

**Skip — restates the type:**

> The `group` field on a page is an optional string that associates the page with a navigation group.

**Write — documents the behavior:**

> If a page declares a `group` that isn't defined in your config, **the build fails** — leadtype won't silently drop it or invent the group. Declare every group in `docs.config.ts` first.

The second version is the one an agent can't reconstruct from the type. It's also the one that prevents a confident wrong answer.

### Why it's also cheaper

Documenting the non-obvious doesn't just raise correctness — it makes agent runs cheaper. When the answer is one short doc instead of a fan-out of `grep`/`read`/`list` calls probing your package, agents read fewer tokens and make fewer tool calls. The [evals](/docs/concepts/evals#every-run-gets-cheaper) show every model spent 32–54% fewer tokens with docs present, including frontier models that barely needed them for correctness.

## How to structure for answer engines (GEO)

Generative Engine Optimization (GEO) is SEO for answer engines: ChatGPT, Claude, Perplexity, and AI Overviews don't rank a page — they **retrieve a passage, quote it, and attribute it**. The unit that gets cited is a *section*, not a page. Structure each one so it survives being pulled out on its own.

The same eval mechanism applies: a correct fact the model can't locate, or quotes out of context, still produces a confident wrong answer. Structure is what closes that gap.

### Lead with the answer

Put the answer in the **first sentence** of the section, then explain. Answer engines excerpt the top of a matched passage; if it opens with preamble ("Before we discuss rate limits, it's worth understanding…"), the citation is useless and the model keeps guessing. Cut the runway.

### Phrase headings as the question a user would ask

Models match a user's query against heading text. A heading that mirrors the question wins:

* **Write:** `### How do I rotate an API key?`
* **Skip:** `### Key management`

Question-form `H2`/`H3`s turn your outline into a set of retrievable answers.

### Make every section self-contained

A model quotes one section without its neighbors, so each must stand alone:

* Define (or link) a term the first time it appears in the section — don't rely on a definition three headings up.
* Use the **specific noun**, not a pronoun: write "the API key", not "it" or "this value". Once a passage is excerpted, the antecedent is gone.
* Don't open a section with "As mentioned above" — there is no "above" in a citation.

### Be specific, not vague

Replace adjectives with values. "Flexible and powerful" tells a model nothing it can quote; **"returns a 429 once you exceed 100 requests per minute"** is exactly what it surfaces. Exact status codes, limits, ranges, defaults, and version numbers are what get retrieved and trusted — and they double as the non-obvious facts from the first half.

### Use one term per concept

Pick one name and keep it. Alternating between "API key", "access token", and "credential" for the same thing fragments retrieval (each phrasing matches different queries) and signals inconsistency a model reads as lower trust.

### Keep the heading hierarchy sequential

Go `H2 → H3 → H4` without skipping a level. The heading tree *is* the topic map an engine parses to understand how your concepts nest; a jump from `H2` to `H4` breaks that map. (This page follows its own rule.)

### Label every code block

Tag the language on every fenced code block — a `ts`-labeled fence, not a bare one. The label tells an engine which stack the snippet answers, so it surfaces your TypeScript example for a TypeScript question instead of guessing.

### Describe images in alt text

Answer engines can't see your diagrams. Put the information the image conveys into its alt text — "Pipeline: MDX → convert → llms.txt + search index + markdown mirror", not "diagram". If the alt text is empty, that content simply doesn't exist for an agent.

### Let leadtype check the mechanical ones

> ℹ️ **Info:**
> leadtype lint flags the structural signals — geo:heading-skip, geo:code-language, and geo:image-alt — and leadtype score rolls them into a 0–100 agent-readiness score mapped to the ora rubric. The editorial tactics above (lead with the answer, question-form headings, specificity) are yours to apply — a linter can't judge them.

### Test it

GEO has a tight feedback loop: ask your product's real questions in ChatGPT, Claude, and Perplexity, and check whether your docs are cited, accurate, and recommending the right approach. When they're not, it's usually a structure problem — the fact is there but buried, or quoted without its context.
