The quickstart, authentication guide, endpoint pages, error reference and changelog an API needs, plus sheets that track missing errors and broken examples.
API documentation is the set of pages a developer needs to use your API without asking you. It usually includes a quickstart, an authentication guide, a reference page for every endpoint, an error reference and a changelog. Good API documentation is complete and accurate: every endpoint and known failure is listed, every example works as printed, and each error tells the caller what to do next.
Document what your API actually does, not only what its spec file declares. The two drift apart, and developers find the gaps by hitting them.
Check the docs against what the code actually does. Photo: ThisisEngineering on Unsplash
A key and the first two or three calls, ready to copy
Skips the error most newcomers hit first
Authentication
Key types, scopes, rotation and webhook signatures
No word on which scope each call needs
Endpoint reference
One page per endpoint, in the same order every time
Shows only the success response
Error reference
One entry per error type, with its cause and fix
One row per status code
Changelog
Every change a working integration could notice, dated
Deprecations with no date or replacement
Start from the spec, then fill its gaps
If you have an OpenAPI file, use it as your inventory, but know its limits. In the OpenAPI Specification, a list of responses needs only one response code2, and a lone code should be the success response. So a spec file can be valid while declaring no errors at all.
The specification still expects documentation to cover "a successful operation response and any known errors." A catch-all default response with a one-line description falls short: it tells readers that failures exist, not what they look like. A linter can flag the gap. Redocly, which sells API documentation tools, has a rule in its recommended settings that warns when an operation has no 4xx error response3.
Then compare the file with what's running: your routes, your gateway config and a week of access logs. An endpoint that shipped without making it into the spec is invisible to every generator. Add each one you find, or mark it internal.
Write every endpoint page the same way
Developers learn where to look once, so give every endpoint page the same order. Define shared fields once, too: if a field like status appears on several endpoints, explain it in one place, such as a data dictionary, and link to it.
Summary: one sentence on what the call does
Authentication: the scheme and the exact scope it needs
Parameters: type, whether required, and units
Request and response examples that work as printed
Errors: every type the call can return
Rate limit, and what happens when a client hits it
ExamplePart of an endpoint pageAn illustrative example written for this guide, using the made-up shipping API in the template's files.
POST /v1/shipments buys a shipping label for a rate you quoted earlier. It needs a key with the shipments:write scope and allows 60 requests a minute.
Send an Idempotency-Key header with every request, so a retry returns the first label instead of buying a second one.
Errors: rate-expired (409), label-already-generated (409), insufficient-postage-balance (402) and carrier-account-suspended (403), each linked to its entry in the error reference.
The scope is named. A key that can quote rates but not buy labels fails here, so the page says which scope it needs.
Errors by type. Two errors share a 409, so each is named by type and links to its own fix.
Check that every example works
Examples are what developers copy, so one that doesn't match its schema breaks their code. OpenAPI says an example should be compatible with its schema2, and leaves tools free to check or not. If nothing checks, an example can keep showing a field you renamed months ago.
Spectral, an open-source linter from the API design company Stoplight, checks examples against their schemas4 in its recommended OpenAPI rules and reports a mismatch as an error. Run a check like it on every pull request.
Valid isn't the same as working. An example can match its schema and still fail when sent, because an ID in it has expired or the record it creates already exists. Send each one to a sandbox before you publish, and keep one example per request or response so two copies can't disagree.
Document errors by type, not status code
One status code often covers several errors that need different handling. RFC 9457, the IETF standard for error details in HTTP APIs, gives each kind of error a type URI. It says consumers must use that URI as the problem type's primary identifier5, and treats the status in the body as advisory.
For each error type, the RFC says you must document a type URI, a short title and the status code it goes with. Add the extra fields it carries, what causes it and what the caller should do. The RFC adds that a type URI should lead to documentation on resolving the problem, so make each one open its own entry in your error reference.
Error types from the template's sample shipping API
Error type
Status
What the caller should do
rate-expired
409
Get a new quote, then retry once
label-already-generated
409
Stop retrying and fetch the existing label
insufficient-postage-balance
402
Top up the balance, then retry
about:blank (no specific type)
404
Stop: this account has no shipment with that ID
Code that branches on 409 alone can't tell the first two apart.
Don't invent a type for everything. The RFC says truly generic problems are usually better expressed as plain status codes. A response with no type counts as about:blank, which means the status code says it all, so use it only where that's true.
Write the quickstart last
Write the quickstart once the reference and errors are settled, so it can link to them. Postman, which sells API tools, says the most important API metric is time to first call6: how long a new developer takes to make a first successful request.
Keep it to the few calls that produce something real, each ready to copy, with the response it returns. List the scopes the key needs before the first call, and show the failure newcomers hit most, with its fix. Then hand it to a colleague who has never used the API, and time them.
Record every change a client could notice
Add a dated changelog entry for anything a working integration could notice: a new required field, a removed field, a new error, a changed limit or a corrected example. Say who is affected and what they need to do.
Deprecations need the most care. OpenAPI's deprecated field2 is only true or false, with no room for a date or a replacement, so put those in the changelog. RFC 97457, published in March 2025, defines a Deprecation response header that warns clients at runtime. A Sunset header can add the date the endpoint stops responding.
Before you publish
Every live endpoint is in the spec, or marked internal
Every endpoint lists its known errors, not just a default
Every example validates against its schema and runs in the sandbox
Every error type has a URI that opens its own entry
The quickstart reaches a successful call and shows the common failure
Every deprecation has a date, a replacement and a migration note
Start with the Documentation Standard, which suggests a default for each rule your team needs to settle, then fill the three registers before you write the published pages. The sheets and published documents come filled in for Waypost, a made-up shipping API, so you can see how they fit together.
Or open it in River with Edit with AI and send your OpenAPI or Swagger file, plus error-handling code or logs if you have them. River fills the endpoint register, checks each example against its schema, lists the error types your API really returns, and reports what the spec leaves out.
1
Download it or open it in River
Get the seven Word documents and three CSV sheets free, or open the template in River to work through it with AI.
2
Send your spec and error logs
Attach an OpenAPI or Swagger file, a URL or a Postman collection, plus error-handling code or recent error logs if you have them.
3
Get the registers filled in
River counts each endpoint's declared errors, checks every example against its schema and lists each error type your API returns.
4
Fix the biggest gaps first
River suggests which findings to fix first. Then ask it to write the error reference, the quickstart or a changelog entry.
What's in the template
The operation that declares no failure, and the example nobody validatedEvery state in these three sheets is one a conforming OpenAPI document is allowed to be in. Default lint rules flag only a few of them.
API Documentation and Reference Template
Endpoint Register
Calibri11
Endpoint Register
Illustrative rows for a fictional parcel shipping API, Waypost.
Operation
Status
Responses declared
Errors declared
Examples present
Examples validating
Owner
POST /v1/rates
published
200, 400, 422, 429
3 of 4
2 of 2
2 of 2
Priya N
POST /v1/shipments
published
201 only
0 of 1
1 of 1
0 of 1
Tomas K
GET /v1/shipments/{id}
published
200, 404, default
1 of 2, plus a bare default
1 of 1
1 of 1
Tomas K
DELETE /v1/shipments/{id}
partial
204, 409, 422
2 of 3
0 of 0
n/a, none present
Tomas K
GET /v1/tracking/{number}
partial
200, 404, 429
2 of 3
1 of 1
0 of 1
Ada W
POST /v1/labels
undocumented
not in the specification
unknown
0 of 0
n/a
unowned
POST /v1/webhooks
deprecated
201, 400, 409
2 of 3
1 of 1
1 of 1
Ada W
GET /internal/v1/rate-cards
internal, published by mistake
200, 403
1 of 2
1 of 1
1 of 1
Priya N
Row two is a conforming specification. The Responses Object needs one response code and the guidance is that a lone code should be the successful one, so a document declaring only a 201 passes validation. That operation returns four distinct failures in production and the generated reference page shows one green row.
A bare default is never credited as an error. Row three declares a default with a description and no schema, which is exactly what the specification permits. It renders as a row telling a reader that failures exist and nothing about their shape, and every 5xx lands there undifferentiated.
Row six is in the router and not in the document. No generator can report it, because a tool cannot see what is missing from its own input.
Example Coverage
Calibri11
Example Coverage Check
Present, validates and runs are three separate columns. A build checks the second only if it runs a lint rule for it.
Operation
Kind
Lives in
Present
Validates
First failure
Overridden by
Runs
POST /v1/rates
request
media type
yes
yes
none
nothing
yes
GET /v1/tracking/{number}
response
media type
yes
no
events is array<object> in the schema, array<string> in the example
nothing
no
POST /v1/addresses/validate
request
schema object
yes
yes
none
the media-type example
never rendered
POST /v1/addresses/validate
request
media type
yes
no
sends postcode, schema requires postal_code
nothing, this one wins
no
POST /v1/shipments
response
media type
yes
no
rate_id required in the schema, absent from the example
nothing
no
DELETE /v1/shipments/{id}
response
nowhere
no
n/a
n/a
n/a
n/a
GET /v1/shipments/{id}
response, default
nowhere
no
n/a
n/a
n/a
n/a
POST /v1/webhooks
request
media type
yes
yes
none
nothing
no, returns 409
The tracking example has been wrong for eleven months. In JSON Schema 2020-12 the examples keyword sits in the vocabulary for basic meta-data annotations, and validity against the associated schema is recommended rather than required. OpenAPI says an example should match its schema. The build runs no lint rule that checks examples, so nothing broke.
Rows three and four are the same field, twice. Under OpenAPI 3.0 and 3.1 a media-type example overrides a schema-level one, and the schema-level field is deprecated in the current specification version. The correct example is in the document and no reader can reach it; the stale one predates a field rename and is the one that renders.
The last row validates cleanly and returns a 409 when sent, because a schema cannot know that a webhook URL is already registered.
Problem Types
Calibri11
Problem Type Register
One row per type URI, not per status code. Two of these share a 409.
Type
Status
Extension members
What to do
URI resolves
In spec
rate-expired
409
rate_id, quoted_at, expires_at
Re-quote, retry once with the new rate_id
yes
no
label-already-generated
409
shipment_id, label_id, generated_at
Stop retrying. Fetch the existing label
yes
no
address-unverifiable
422
field, suggested_value, confidence
Show the suggestion to the user. Never substitute it
yes
yes
insufficient-postage-balance
402
balance, required, top_up_url
Top up, then retry the same request
no, 404s
no
carrier-unavailable
503
carrier, retry_after_seconds
Retry after the delay, or re-quote excluding that carrier
no
lands in the bare default
carrier-account-suspended
403
carrier, suspended_at, contact
Stop. Contact the carrier. Retrying never works
yes
no
about:blank
401, 404
none
Refresh the token, or stop. Deliberate
registered by the standard
404 only
(no type member sent)
429
none, plain JSON body
Honour Retry-After. The window is fixed
n/a
yes
The first two rows are why a status-code table is the wrong artifact. Both return 409. Retrying the first usually succeeds and retrying the second never does, so code branching on the number produces a retry loop against an operation that buys postage. RFC 9457 requires consumers to use the type URI as the primary identifier and makes the status member advisory.
Row four returns a type URI that 404s. The standard says dereferencing a locator type URI should provide human-readable documentation for the problem type, so this is a promise made on the API’s behalf and broken. It is also the cheapest row here to fix.
The last row sends no type member, so its type is assumed to be about:blank, which claims the status code carries the whole meaning. On a 429 with a retry window, it does not.
Documentation StandardEleven decisions with suggested defaults, from what counts as published to who owns each area.
Reading a SpecificationThe rules from OpenAPI, JSON Schema and RFC 9457 behind each register column, quoted from the sources.
Getting StartedA three-call quickstart for a sample shipping API, with the failures newcomers hit and how to handle them.
Authentication GuideKey types, scopes, rotation, idempotency keys and how to verify webhook signatures.
Endpoint ReferenceTwo finished endpoint pages in a fixed order, and three unfinished ones with what each lacks.
Error ReferenceOne entry per error type: its URI, status, extra fields, cause and what the caller should do.
ChangelogDated entries for every change a client could notice, with deprecation dates and replacements.
Endpoint Register sheetOne row per endpoint and method: declared responses and errors, examples, auth, rate limit and owner.
Example Coverage Check sheetOne row per example: where it lives, whether it validates, its first failure and whether it runs.
Problem Type Register sheetOne row per error type URI, with status, extra fields, cause, the fix and whether it's documented.
Common questions
What should an API documentation template include?
A quickstart, an authentication guide, one reference page per endpoint, an error reference and a changelog. Each endpoint page needs its parameters, a working request and response example, every error it can return and its rate limit. This template adds three sheets that show which endpoints, examples and error types are still incomplete.
Can I generate API documentation from an OpenAPI file?
Yes, for the reference pages, but a generator can only show what the file declares. OpenAPI accepts a file that lists one success response per operation, so errors you never declared won't appear, and neither will endpoints missing from the file. The quickstart, error reference and changelog still need someone who knows how the API behaves.
How should API errors be documented?
Give each kind of error its own entry, keyed by a type rather than by status code, because two errors can share a status and need opposite handling. RFC 9457 sets the standard format. Each entry needs the type URI, a short title, the status, any extra fields, the cause and what the caller should do.
How do you keep API documentation up to date?
Keep the spec in the same repository as the code, and check it on every pull request. Lint it, validate each example against its schema, and compare it with your routes. Add a changelog entry with each release that changes anything a client could notice, and update the error reference whenever a new error ships.
Is this API documentation template free?
Yes. Download the seven Word documents and three CSV sheets with no signup. They come filled in for Waypost, a made-up shipping API, so you can see a finished set before you swap in your own endpoints. Edit with AI opens the same template in River.
What does Edit with AI do?
It opens the template in a free River workspace, where River asks for your spec and any error logs or error-handling code. It records every endpoint with its declared errors, validates each example against its schema and builds the error type register from what your API returns. It never invents an error your API doesn't return.
Chandler has spent the last six years working in AI, building tools for millions of users and Fortune 100 companies. In 2025, he co-founded River to build a new kind of operating system for AI agents.