Building Tags, Examples, Schemas and Responses That Work
A technically correct OpenAPI specification is not yet good documentation. Tags structure navigation, examples enable direct integration, schemas validate and explain at the same time, and standardized responses make errors manageable. This guide shows how to get all four dimensions right together.
Table of Contents
- 1. What good API documentation must deliver
- 2. Tags: group structure and navigation
- 3. Examples: data that is genuinely useful
- 4. Schemas: setting descriptions and constraints correctly
- 5. Responses: standardizing success and failure
- 6. RFC 9457 Problem Details as the error standard
- 7. x-extensions for tooling-specific metadata
- 8. Good vs. bad documentation practices compared
- 9. Summary
- 10. FAQ
1. What good API documentation must deliver
Good API documentation fulfills three functions at once: it explains why an endpoint exists and when it is used, it shows how it is actually called with realistic example data, and it defines what comes back in an error case. Documentation that only maps the technical schema answers only the third question. Teams that document nothing but the code, without context and examples, produce documentation that nobody reads, and that in turn generates support requests that cost development time.
The most common complaints frontend teams have about backend APIs: missing example data, unclear error codes, no explanation of when which status comes back, and schemas without descriptions. All of these problems are solvable in OpenAPI, with tags for structure, examples for concrete data, schema descriptions for context, and standardized error responses. The decisive point: this information belongs directly in the OpenAPI specification, not in a separate Confluence page that stops being maintained after the first release.
OpenAPI documentation should be treated as a single source of truth. Mock servers generate realistic test data from the spec, code generators create client SDKs, contract tests validate the implementation against the spec. The more complete and precise the documentation, the more automation is possible without manual rework. The effort spent on good examples and descriptions pays off in less support, faster frontend integration and more reliable tooling.
2. Tags: group structure and navigation
Tags are the organizational system of OpenAPI. Each operation can be assigned to one or more tags, and in Swagger UI and Redoc they then appear as collapsible groups. Tags without a root-level definition still appear in the documentation, but have no description text and no external documentation links. That is the common mistake: tags get assigned in operations but never defined with descriptions at the root level.
A good tag structure follows the domain boundaries of the API, not the technical modules. orders, products, customers and auth are better tags than OrderController or v1_endpoints. Tags should use singular or plural consistently, and all operations under a tag should describe a coherent resource. Operations with multiple tags appear in multiple groups, which is sometimes useful (for example an endpoint that belongs both to orders and to payments), but should be used sparingly.
# Define tags fully at the root level (OpenAPI 3.1 best practice)
tags:
- name: orders
description: |
Create, query and manage orders.
## Lifecycle
`pending` → `confirmed` → `shipped` → `delivered`
Cancellations are only possible in status `pending` or `confirmed`.
Cancelled orders have status `cancelled` and cannot be reactivated.
externalDocs:
description: Order lifecycle documentation
url: https://docs.mironsoft.de/guides/order-lifecycle
- name: products
description: |
Read the product catalog and query stock levels.
Product data is read-only through this API; changes
happen in the admin backend.
externalDocs:
description: Product data model
url: https://docs.mironsoft.de/guides/product-model
- name: auth
description: |
Authentication and token management.
Use `POST /auth/token` for initial authentication.
Tokens are valid for 1 hour.
`POST /auth/token/refresh` extends validity without new credentials.
- name: webhooks
description: |
Webhook configuration for asynchronous event notifications.
Supported events: order.created, order.status_changed, payment.received
3. Examples: data that is genuinely useful
Examples in OpenAPI are more than placeholder data. They are the direct communication channel between backend developers and all API consumers. Good examples are realistic (no foo/bar values), complete (all relevant fields filled in), annotated (via the summary and description fields) and thorough (multiple scenarios per endpoint). The examples section in requestBody and responses allows multiple named examples, which is especially valuable for different use cases.
The key principle: examples should be centralized in components/examples and referenced via $ref. That way the same example can be used across multiple operations, for instance an order object that appears both in the POST response and the GET response. Examples in components/examples also have the option of using an externalValue field that points to an external JSON file. That allows very long example data without making the YAML file unreadable.
# Examples in components: reusable and documented
components:
examples:
StandardOrder:
summary: Regular order with two products
description: |
Typical example of an order in status 'confirmed'.
Shows the full structure with all required fields.
value:
id: "550e8400-e29b-41d4-a716-446655440000"
status: confirmed
items:
- productId: "prod-001"
productName: "Mironsoft Developer License"
quantity: 1
unitPrice: { amount: 199.00, currency: "EUR" }
- productId: "prod-002"
productName: "API Documentation Package"
quantity: 2
unitPrice: { amount: 49.00, currency: "EUR" }
total: { amount: 297.00, currency: "EUR" }
shippingAddress:
street: "Hauptstraße 1"
city: "Berlin"
postalCode: "10115"
country: "DE"
createdAt: "2026-05-10T14:30:00Z"
updatedAt: "2026-05-10T14:35:00Z"
CreateOrderRequest:
summary: Create a new order
description: Minimal example for creating an order
value:
items:
- productId: "prod-001"
quantity: 1
shippingAddressId: "addr-123"
note: "Please deliver by 2pm"
ValidationErrorExample:
summary: Validation error on missing required fields
value:
type: "https://mironsoft.de/errors/validation-failed"
title: "Validation Failed"
status: 400
detail: "The request body contains validation errors"
instance: "/orders"
errors:
- field: "items"
message: "At least one item is required"
- field: "items[0].quantity"
message: "Quantity must be greater than 0"
4. Schemas: setting descriptions and constraints correctly
A schema without descriptions is a type declaration, not a document. Every property that is not self-explanatory needs a description field. The description explains the what and the why: not "date of creation" but "ISO 8601 timestamp of when the order was created in the system, read-only, set by the server". Constraints like minimum, maximum, minLength, maxLength, pattern and enum are not optional documentation but part of the API specification, they drive mock server validation and client-side form validation in generated SDKs.
The example field at the schema level is meant for simple values (a single string, a number). The examples field on operations allows multiple full datasets. The schema should also set readOnly: true for server-generated fields (id, createdAt) and writeOnly: true for fields that only appear in requests (password hashes, tokens). That affects which fields code generators include in request vs. response classes.
5. Responses: standardizing success and failure
Every operation should document all possible HTTP status codes it can return, not just 200. At minimum: the success response, 400 for validation errors, 401 for missing authentication, 403 for missing authorization, 404 for resources not found, and 500 for server errors. Covering these six responses lets client developers implement robust error handling without surprises.
All error responses should be centralized in components/responses. Instead of repeating 404: {description: Not Found, content: {application/json: {schema: {...}}}}} in every operation, components/responses/NotFound is defined once and referenced in all operations via $ref: '#/components/responses/NotFound'. For an API with 50 endpoints and 5 error types, that saves 200 lines of YAML and guarantees that every error response uses the same schema.
# Standardized responses in components: RFC 9457 Problem Details
components:
responses:
ValidationError:
description: Validation error in request data
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
examples:
missing_field:
$ref: '#/components/examples/ValidationErrorExample'
Unauthorized:
description: Authentication missing or token invalid
headers:
WWW-Authenticate:
schema:
type: string
example: 'Bearer realm="mironsoft.de", error="invalid_token"'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
example:
type: "https://mironsoft.de/errors/unauthorized"
title: "Unauthorized"
status: 401
detail: "Bearer token is missing or has expired"
NotFound:
description: Resource not found
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
example:
type: "https://mironsoft.de/errors/not-found"
title: "Not Found"
status: 404
detail: "The requested resource does not exist"
InternalServerError:
description: Internal server error
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
6. RFC 9457 Problem Details as the error standard
RFC 9457 (formerly RFC 7807) defines a standardized format for HTTP API error messages: Problem Details for HTTP APIs. The schema has five standard fields: type (a URI identifying the error type), title (a human-readable short description), status (the HTTP status code), detail (a human-readable description of this specific error) and instance (a URI identifying this specific occurrence). All fields are optional except type, and custom extension fields are allowed.
The benefit of the standard: frontend teams and API consumers only need to know one error format, regardless of which endpoint returns an error. The Content-Type for Problem Details is application/problem+json (not application/json). That lets middleware and load balancers correctly recognize error responses. In OpenAPI, application/problem+json is specified as the media type in error responses, which is also picked up automatically in generated clients.
7. x-extensions for tooling-specific metadata
OpenAPI allows custom extension fields with the x- prefix. These are ignored by standard parsers but evaluated by specific tools. Commonly used extensions: x-codegen-request-body-name for code generators, x-logo for Redoc to display a logo in the documentation, x-tagGroups for Redoc to build a two-level tag hierarchy, x-internal to hide internal endpoints from public documentation, and custom extensions like x-rate-limit for per-endpoint rate-limiting information.
Custom extensions are a powerful tool but should be used sparingly. Every extension is implicitly a documentation debt: its meaning has to be explained somewhere, and if the tool does not evaluate it, it leaves unused fields in the spec. A sensible convention: extensions that are only relevant for internal build processes get grouped in their own namespace, for example x-mironsoft-deprecated-by instead of just x-deprecated-by.
8. Good vs. bad documentation practices compared
The quality of OpenAPI documentation shows up in the integration experience of consumers. Bad documentation generates support requests, good documentation prevents them. The following table shows typical anti-patterns and the recommended alternative.
| Area | Anti-pattern | Best practice | Effect |
|---|---|---|---|
| Examples | "value": "string" |
Realistic production-like data | Frontend can copy directly |
| Errors | Only 200 documented | All 4xx/5xx with schema | Robust error handling possible |
| Descriptions | Field named updatedAt |
When, format, timezone, read-only | No follow-up questions on semantics |
| Schemas | Inline in every operation | In components/schemas | One change, effective everywhere |
| Tags | Without root-level definition | With description and externalDocs | Redoc/Swagger show context |
9. Summary
Good OpenAPI documentation is the result of four consistently applied practices. Tags with complete root-level definitions and descriptions structure the documentation for consumers, not for the generator. Examples in components/examples with realistic data and multiple scenarios per endpoint make integration possible without follow-up questions. Schemas with descriptions, constraints and readOnly/writeOnly markers deliver machine-readable and human-understandable specifications at the same time. Responses in components/responses standardized on RFC 9457 Problem Details enable robust and consistent error handling.
The simplest first step for teams with existing OpenAPI specifications: centralize all inline error responses in components/responses and introduce RFC 9457 as the error schema. That immediately improves consistency and reduces the size of the spec without changing the actual API.
OpenAPI Documentation: the essentials at a glance
Tags
Define at the root level with description and externalDocs. Domain-based, not module-based. At most two tags per operation.
Examples
Centralize in components/examples. Realistic data, multiple scenarios, summary and description per example. No foo/bar.
Schemas
Every non-self-explanatory field needs a description. Constraints as specification, not decoration. Set readOnly/writeOnly.
Responses
Document all 4xx/5xx. Centralize errors in components/responses. RFC 9457 Problem Details as the unified error format.
Mironsoft
REST API design, OpenAPI documentation and developer experience
API documentation developers actually use?
We build and improve OpenAPI documentation with complete examples, consistent schemas and standardized error responses, so frontend teams can integrate without filing support requests.
Documentation audit
Reviewing existing OpenAPI specs for completeness and quality
Example authoring
Realistic examples for all endpoints and error scenarios
Schema standardization
Introducing the RFC 9457 error format and a consistent schema library