# Writing API Descriptions an LLM Can Actually Use — Routebase

> A linter can tell you a description is missing. It cannot tell you the description is useless — and that is the one an agent chokes on. What belongs in a description, what to leave out, and how to check whether it worked.

Canonical page: https://routebase.dev/blog/api-descriptions-llms-can-use/
Published: 2026-08-25 · The Routebase Team · AI Agents, OpenAPI, API Design

Here is a description that passes every completeness check we know how to write:

```yaml
/deployments:
  post:
    summary: Create deployment
    description: Creates a new deployment.
```

Nothing is missing. There is a `summary`, there is a `description`, both are
non-empty strings. Run it through a style guide and it comes back clean.

Now read it as the only thing you know about this endpoint. What is a
deployment here? Can you create one for any environment, or does something
have to exist first? Does it return the finished thing, or start something
that finishes later? You cannot answer any of that — and neither can an agent,
which is why it will call this endpoint with a plausible guess and report
success it has no evidence for.

We have written this wrong at least as often as anyone. This is the checklist
we now use.

## What a linter can see, and where it stops

Routebase ships six built-in rules that look at descriptions: operations,
summaries, schemas, responses, parameters, and tags each have one. Every one of
them is the same check underneath — is this field empty or whitespace? — and
they are graded accordingly. A missing operation description is a warning. A
missing parameter description is an information notice.

That is the correct design, and it is worth being precise about why. Presence
is machine-checkable; usefulness is not. No rule can decide whether *"Creates a
new deployment."* tells a reader something they did not already know, because
answering that requires knowing what the reader already knew. So linting gives
you the floor: every description exists. Everything above the floor is a
writing problem, and it stays a writing problem no matter how many rules you
add.

The trap is that a clean lint report reads like a finished job.

## The four questions

A description is doing its job when a reader who has only the contract — no
tribal knowledge, no colleague to ask, no staging environment to poke — can
answer these:

1. **What does this do,** in terms of the domain rather than the mechanics?
2. **When do I use this one** instead of its neighbours?
3. **What has to be true first** — required state, prior calls, ordering?
4. **What comes back, and what do I do next** with it?

Question 2 is the one that gets skipped, and it is the one agents fail on.
A human choosing between `POST /deployments` and `POST /config-snapshots`
reads both, forms a theory, and tries one. An agent is doing the same thing
with far less context and no way to notice that its theory was wrong until
something downstream breaks.

Applied to the example:

```yaml
/deployments:
  post:
    summary: Deploy a config snapshot to an environment
    description: >
      Starts a deployment of an existing config snapshot to one environment.
      Create the snapshot first with POST /config-snapshots — this endpoint
      does not capture the current configuration, it only ships a snapshot
      that already exists.

      Returns 202 with a deployment id. The deployment is not finished when
      this returns; poll GET /deployments/{id} until status is "succeeded"
      or "failed".
```

Longer, yes. Long enough to answer all four questions, and no longer — the
prose is four sentences.

## The most common failure is a description that restates the name

`POST /deployments` → *"Creates a new deployment."* The method said "creates",
the path said "deployments", and the description spent a line saying both
again. It costs the reader a line and returns nothing.

We hold our own tooling to this: the rule set behind Routebase's description
suggestions says, in as many words, not to repeat the HTTP method or the path,
and to describe what the endpoint does rather than how it works internally.
Those two instructions kill most bad descriptions on their own.

The reason a restated name is worse than an empty field is that it looks
handled. An empty description is visibly a gap, and someone eventually fills
it. *"Creates a new deployment."* closes the ticket.

## Descriptions below the operation level

Operation descriptions get the attention. The ones that actually decide
whether a call is correct usually sit further down.

**Enums.** An enum tells a model which values are legal and nothing about what
they mean. `status: ["pending", "active", "suspended", "closed"]` — is a
suspended account billable? Can a closed one reopen? If the difference matters
to the caller, it belongs in the description, per value.

**Parameters that look obvious.** `limit`, `since`, `filter` — every API has
them and no two agree. What is the maximum for `limit`, and what happens if
you exceed it: clamped, or a 400? Is `since` inclusive? Answer it once in the
parameter description and stop answering it in support threads.

**Fields whose name is a lie by omission.** `email` — required or the address
the invitation goes to? `amount: 1250` — is that $12.50 in cents or $1,250.00
in whole units? The schema says `integer`, and both readings are type-correct.
The description is the only place the unit can live.

_Figure: The same type-correct field, charged twice. With the description slot empty, nothing says what 1250 counts — the agent reads dollars and moves a hundred times the intended money, while schema and linter stay green in both rows. One sentence in that slot is the difference between $1,250.00 and $12.50: for the unit, the description is the entire contract._

We learned this from the other side of the table. Routebase's own MCP server
exposes more than 400 tools, and one convention has earned its keep more than
any other: where a parameter takes a fixed set of actions, the description
enumerates every legal value, because that description is the only place an
agent ever sees them. The parameter is typed as a string; the error thrown on
an invalid value repeats the same list. Anything left out of that sentence
does not exist as far as the model is concerned.

That generalises past our own tools. The description is not commentary on the
contract. For everything the type system cannot express — units, bounds,
inclusivity, meaning — it *is* the contract.

## What to leave out

- **Internal implementation.** Which service handles it, which queue it lands
  in, which table it writes. It is noise to a caller and a liability the day
  it changes.
- **Interface instructions.** "Click Save to confirm." There is no Save button
  in a `POST`.
- **Restating the schema.** The schema already says `string`, `required`,
  `maxLength: 200`. Do not paraphrase it; say what a valid value *means*.
- **Marketing.** "Our powerful deployment engine." No consumer, human or
  otherwise, has ever made a better call because of a sentence like this.

## How to tell whether it worked

The honest test is cheap: hand the endpoint to an agent with no context beyond
the contract and ask it to perform a real task. Watch which endpoint it picks,
which parameters it fills, and where it invents a value. Every guess it makes
is a sentence you did not write. This is uncomfortable in the specific way
good tests are uncomfortable — it finds the gaps you had stopped seeing,
because you have known the answers for two years.

Then do the work, which is mostly not thinking. Reading several hundred
descriptions, finding the ones that restate their own name, writing the units
into the fields that lack them — that is transformation work, and it is what
an agent connected to the [Routebase MCP server](/mcp-server/) is for: it
proposes the rewrite, you accept or reject it, and what lands in the spec is a
diff you approved rather than a paragraph you typed. The descriptions your
agents will read can be drafted by one, as long as a person still signs off.

None of this is new advice. Descriptions have always been the part of a spec
that decides whether anyone can use it without asking you first. What changed
is that the reader who cannot ask you is now most of your traffic — and
[the way you design the rest of the API](/blog/designing-agent-ready-apis/)
has to hold up under the same reader.

---

[Routebase](https://routebase.dev/) — [Sign up](https://app.routebase.dev/): Every account starts with a 14-day Pro trial — no credit card required.
