Documenting OpenAPI: Building Tags, Examples, Schemas and Responses That Work
AI generated
{ }
GET
OpenAPI · Tags · Examples · Schemas · Responses · Documentation
Documenting OpenAPI
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.

16 min read Tags · Examples · Schemas · Responses · RFC 9457 · Redoc OpenAPI 3.1 · YAML · Swagger UI · Redoc

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

10. FAQ: OpenAPI Tags, Examples, Schemas and Responses

1Are root-level tags mandatory?
Technically no, but without a root-level definition, descriptions and externalDocs links are missing from the documentation. Always define them fully.
2example vs. examples?
example: a single value at the schema level. examples: an object with multiple named examples in MediaType objects. For complete request body/response examples, always use examples.
3What is RFC 9457?
Problem Details for HTTP APIs: a standardized error format with type, title, status, detail, instance. Content type: application/problem+json. Consumers only need to know one error format.
4How many examples per endpoint?
At least one minimal and one complete example per request type. For error responses: a concrete example of the most common error. More is better than fewer.
5Preventing schema duplication?
All schemas in components/schemas, never inline. Shared properties as their own schemas. Centralize error responses in components/responses.
6When to set additionalProperties: false?
Almost always for request schemas. For response schemas, use caution, it prevents adding new fields later without a breaking change.
7Documenting paginated responses?
A PaginatedResponse schema in components/schemas with meta (total, page, perPage) and data as an array. Cursor-based: next/prev links instead of page numbers.
8x-tagGroups in Redoc?
A Redoc extension that organizes tags into higher-level groups. Ideal for large APIs with many endpoints. x-tagGroups with name and tags array at the root level.
9Documenting enum values?
Yes, every value should be explained. x-enumDescriptions extension or a tabular description in the description field. No enum values without context.
10Testing examples against schemas?
Spectral or the Redocly CLI: redocly lint openapi.yaml. Add it as a CI step to catch invalid examples early.