Real APIs don't just return 200. They return 400 when the input's malformed, 401 when auth's missing, 404 when the resource doesn't exist, 429 when the reader's rate-limited. NookDocs renders every response declared in your OpenAPI spec as a tab in the endpoint page, each with its own schema and example, colour-coded by status class.
How responses render
The endpoint page's response panel has a tab strip — one tab per status code in your spec, ordered numerically. Status tabs are semantically coloured:
| Status class | Tab colour | Examples |
| 2xx success | Green | 200 OK, 201 Created, 204 No Content |
| 3xx redirect | Blue (info) | 301, 302, 304 — rare in REST APIs |
| 4xx client error | Red (danger) | 400, 401, 403, 404, 422, 429 |
| 5xx server error | Amber (warning) | 500, 502, 503, 504 |
Reader clicks a tab → sees that status's description, content-type, response schema (full field table), and the example response body.
Minimal shape
Every response needs description + content with a media-type and schema:
paths:
/users/{id}:
get:
operationId: getUser
responses:
'200':
description: User found
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
description: User not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'Two tabs render: 200 (green) and 404 (red). Each with its schema field table + a synthesised example.
Adding examples
Schemas auto-synthesise an example (from example / examples on the schema, or from type + format fallbacks). Override per response:
responses:
'200':
description: Successful user lookup
content:
application/json:
schema:
$ref: '#/components/schemas/User'
example:
id: "usr_abc123"
email: "jane@acme.com"
createdAt: "2026-04-20T10:30:00Z"
'404':
description: No user with that ID
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
code: "user_not_found"
message: "No user exists with id usr_doesnotexist"
statusCode: 404The example appears in the example-response tab strip alongside the schema tab. Readers flip between "what the shape is" and "what a real body looks like."
Multiple examples per status
Sometimes 200 responses can vary by scenario — a GET /search returns different shapes for "found results" vs "no results". Declare with examples:
responses:
'200':
description: Search completed
content:
application/json:
schema:
$ref: '#/components/schemas/SearchResults'
examples:
hasResults:
summary: Results found
value:
query: "carbon"
total: 42
items:
- { id: "r_1", title: "Carbon pricing" }
- { id: "r_2", title: "Carbon capture methods" }
empty:
summary: No results
value:
query: "kdjhfksjdhf"
total: 0
items: []Each named example appears as a sub-tab within the status tab. Reader clicks 200 → Results found or 200 → No results.
Error response schema pattern
Centralise your error shape in a single Error schema and reference it everywhere:
components:
schemas:
Error:
type: object
required: [code, message]
properties:
code:
type: string
description: Machine-readable error slug (e.g. `user_not_found`, `rate_limited`).
example: "user_not_found"
message:
type: string
description: Human-readable explanation.
example: "No user exists with that ID."
statusCode:
type: integer
description: HTTP status code, mirroring the response status.
requestId:
type: string
format: uuid
description: Passthrough for our logs. Include in support tickets.Then across every endpoint:
'400': { $ref: '#/components/responses/BadRequest' }
'401': { $ref: '#/components/responses/Unauthorized' }
'404':
description: Not found
content:
application/json:
schema: { $ref: '#/components/schemas/Error' }Response-level $ref (#/components/responses/...) lets you centralise not just the schema but the full description + example, so your error handling docs stay consistent endpoint to endpoint.
Rate-limit + auth responses
Two responses every endpoint should document even if you don't enforce them globally:
'401':
description: Missing or invalid API key
content:
application/json:
schema: { $ref: '#/components/schemas/Error' }
example:
code: "invalid_api_key"
message: "API key is missing or invalid."
statusCode: 401
'429':
description: Rate limit exceeded
headers:
X-RateLimit-Limit:
schema: { type: integer }
description: Request quota per minute.
X-RateLimit-Remaining:
schema: { type: integer }
description: Requests remaining in the current window.
Retry-After:
schema: { type: integer }
description: Seconds to wait before retrying.
content:
application/json:
schema: { $ref: '#/components/schemas/Error' }
example:
code: "rate_limited"
message: "Too many requests. Retry after 60 seconds."
statusCode: 429Response headers declared via headers: render in the response panel below the body — readers see X-RateLimit-Remaining is a thing before they have to discover it the hard way.
Default response
OpenAPI's default response catches everything not enumerated:
responses:
'200':
description: Success
content:
application/json:
schema: { $ref: '#/components/schemas/User' }
default:
description: Unexpected error
content:
application/json:
schema: { $ref: '#/components/schemas/Error' }Rendered as a default tab, neutral-coloured (no status number to classify). Useful for APIs where you don't want to enumerate every 5xx possibility but still want the error shape documented.
Status descriptions
The description field is the tab's subtitle — readers scan these to figure out which tab they care about. Write them as "what happened" sentences, not just status name repetition:
'200': { description: "User retrieved successfully" } # Better
'200': { description: "OK" } # Noise
'404': { description: "No user exists with the provided ID" } # Better
'404': { description: "Not Found" } # NoiseContent-type other than JSON
NookDocs handles multiple media types per response:
responses:
'200':
description: Export completed
content:
application/json:
schema: { $ref: '#/components/schemas/ExportJob' }
application/pdf:
schema:
type: string
format: binary
text/csv:
schema:
type: stringThe media-type appears below the status tab header. Schemas render independently per type.
Limitations
No
linksrendering. OpenAPI'slinkskeyword (for describing how one response's ID feeds the next endpoint's request) is parsed but not yet rendered as a UI affordance. Useful for HATEOAS-style docs — on the roadmap.No response callback docs for WebSockets.
asyncapi.yamlis the right format for event-driven APIs; we parse but don't fully render it yet — see AsyncAPI setup (roadmap).No response-time SLA annotation.
x-response-time-p99and similar extensions pass through but aren't surfaced. If you need latency docs, put them in the endpoint description.
Related
Complex types —
oneOf/anyOf/allOfresponse schemasPlayground — Try-It panel where readers see real responses
SDK examples —
x-codeSamplesfor hand-written code snippetsManual API pages — escape hatch with
<ResponseExample>+<ResponseField>