Development

How to Write an OpenAPI Spec That Generates Working Examples

Antonio Evans8 min read

A developer's desk with two monitors showing a YAML OpenAPI spec on the left and a generated API reference page with sample JSON on the right, linked by a CI pipeline diagram on a whiteboard.
Key points

Key points

  1. 1A spec generates working examples only when every operation, parameter and schema is explicit enough for a tool to construct a valid request without guessing.
  2. 2Design-first, where the description is written before the code, is the approach the OpenAPI Initiative recommends because not every HTTP API can be described in OpenAPI.
  3. 3Named component schemas, unique operationIds and consistent tags are what let generators produce clean method names and reusable types instead of anonymous inline blobs.
  4. 4Running validation, example generation and client stub generation in CI is what keeps docs, SDKs and tests from drifting apart.

Start with the decision that determines everything else

Before you write a single path, decide who will build the spec and what you want out of it. There are two broad approaches, and they produce very different results downstream.

Design-first means you write the OpenAPI description before you write the implementation. The OpenAPI Initiative is blunt about its preference here: OpenAPI is not capable of describing every possible HTTP API, so if you code first and describe later, you eventually hit a construct you cannot express. You then either rewrite the code or twist the description until it "more or less" matches, which produces unintuitive and incomplete docs the OpenAPI best practices page.

Code-first means the implementation comes first and the description is derived from annotations, framework metadata or traffic sniffing. It has real appeal because developers do not need to learn another syntax, but the annotation tooling has historically lagged behind the current specification versions, and annotations drift from the code they sit next to Bump.sh on code-first workflows.

A decision rule that holds up in practice:

SituationStart withWhy
New public API, external consumersDesign-firstThe spec is the contract; SDKs and docs generate from it before code exists
Internal service, single team, rapid churnCode-first with a generated specFaster iteration, but you must validate the output in CI
Spec already exists but generates poor clientsFix the spec, keep design-firstInline schemas and missing operationIds cause most bad output
Multiple languages consuming the APIDesign-firstOne spec, many client stubs, one place to change

Whatever you choose, there is one rule that overrides the rest: keep a single source of truth. If annotations generate the spec and you also commit the spec, newcomers will not know which one is live, so either pick one or add a CI check that fails when the two diverge the OpenAPI best practices page. Treat the description as a first-class source file, commit it early and let it drive testing and documentation generation.

Structure paths and operations so generators can name things

Generators do not read your spec the way a human does. They derive method names, namespaces and file paths from specific fields, and if those fields are missing or inconsistent you get a client nobody wants to use.

operationId is the method name. It must be unique in the document and it is case sensitive. A consistent pattern such as listDrinks, createDrink, updateDrink beats getDrinksListV2 every time, and Speakeasy explicitly warns that frameworks which auto-generate operationIds can produce long identifiers that turn into non-idiomatic method names Speakeasy's OpenAPI guidance. If your framework emits drinks_controller_get_all_drinks, override it.

Tags are namespaces. With a tag called drinks, generated methods land under drinks.listDrinks(). A tag named menu.drinks creates a nested namespace menu.drinks.listDrinks(). Define tags deliberately rather than letting them fall out of your router structure.

servers must not be empty. An empty or missing servers list defaults to localhost or an example domain in some tools, which breaks anyone trying to actually call the API from the generated docs APIMatic's best practices list. List production and staging explicitly.

Keep names short. Titles, operationIds, parameter names, schema property names and summaries work best under 50 characters, because SDK generators name code components after them and long names cause collisions and code generation failures APIMatic.

A minimal operation that generates cleanly looks like this:

paths:
  /drinks:
    get:
      operationId: listDrinks
      summary: List drinks
      tags:
        - drinks
      parameters:
        - $ref: "#/components/parameters/PageSize"
      responses:
        "200":
          description: A list of drinks
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DrinkList"
              examples:
                twoDrinks:
                  value:
                    drinks:
                      - name: Coffee
                        price: 2.5
                      - name: Tea
                        price: 1.8

Write schemas that produce valid sample data

This is where most "the generated examples are useless" complaints originate. A generator can only invent plausible data if the schema constrains what plausible means.

The rule is simple: no inline schemas. Define every request and response body as a named schema under components/schemas and reference it. Inline schemas get their names inferred from the parent node, which produces unfriendly names and duplicate types, and it breaks the reuse that makes client generation worthwhile APIMatic. It also prevents a generator from producing a single shared type for the same structure across operations Speakeasy.

Beyond naming, these fields change whether generated data is valid:

  • type and format on every property, so a generator knows to emit a number rather than a string.
  • required lists, so generated requests include fields the server will reject as missing otherwise.
  • enum values, so generated examples use accepted values rather than free text.
  • examples at the media type level, which some tools use directly as test fixtures.

On that last point: examples should not be empty or null. They demonstrate the intended payload and some tools turn them straight into API test cases APIMatic.

Error responses deserve the same treatment. Define dedicated error schemas per status code, such as 400 and 404, with structured bodies, so generated clients get real error classes instead of a stringly typed blob Speakeasy.

A worked comparison makes the difference concrete. Suppose a Drink schema has no required list, no format on price, and no enum on size. A generator emits something like {"name": "", "price": "0", "size": "unknown"}, the mock server rejects it, and the example in your docs is wrong. Add required: [name, price], type: number on price, and an enum of small, medium, large, and the same generator emits {"name": "Coffee", "price": 2.5, "size": "medium"}. Same generator, same spec size, different outcome: the difference is entirely in the constraints.

Put example generation in CI

Writing a good spec once is not the hard part. Keeping it true is. Docs-as-code is the practice of writing documentation with the same tools as code: version control, pull requests, automated tests and CI the Write the Docs guide. In practice this means validation and generation treat the spec as an artifact rather than a wiki page.

The UK Government Digital Service adopted this approach so that documentation sits in the same repository and workflow as the code, letting writers and engineers review and iterate on the same pull request GDS's account of docs as code. The same pattern applies to a spec: a change to an endpoint and a change to its description should be reviewed together.

Four CI steps cover most of the value:

  1. Lint and validate the spec against the OpenAPI schema. Validation tools exist in quantity, and running them in CI lets you change the description with confidence that deviations get caught the OpenAPI best practices page.
  2. Generate the client stubs and fail the build if generation errors.
  3. Generate the documentation from the spec, so the reference page is never hand-edited.
  4. Run contract tests against a mock server built from the spec's examples.

A skeleton workflow:

name: spec
on: [pull_request]
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Lint spec
        run: npx @redocly/cli lint openapi.yaml
      - name: Generate client
        run: npx @hey-api/openapi-ts -i openapi.yaml -o src/client
      - name: Fail on drift
        run: git diff --exit-code src/client

That last step is the one most teams skip. Checking that regenerated code produces no diff means a spec change that alters the client cannot merge silently. It is the same consistency check the OpenAPI Initiative suggests when two sources of truth exist the OpenAPI best practices page.

If you would rather send generated requests to a live model than to a stub, Quanta Inference exposes text, image, video, audio and embedding models through one OpenAI-compatible API key, which makes it a straightforward target for a generated client to call in a smoke test.

Fit the spec into a documentation system

A spec is reference material, and reference is only one of four kinds of documentation. Diátaxis separates tutorials (learning-oriented lessons), how-to guides (goal-oriented directions), reference (accurate, complete facts) and explanation (context and background), and it argues that blurring these boundaries is the root of many documentation problems Diátaxis. Generated API reference sits squarely in the reference quadrant: neutral, structured, and reflecting the shape of the thing it describes.

That matters for what you do next. A generated reference page will not teach anyone to make their first call. The tutorial that walks a developer through authentication belongs beside it, written by hand and linked from the reference, not crammed into schema descriptions. GitHub's documentation guidance makes the same point about structure: put the most important information first, keep each document focused on one topic, and move tangential material into separate linked pages GitHub's developer documentation guide.

Write schema descriptions for the reader who is scanning generated reference at speed. Postman notes that reference documentation describes every endpoint, its methods, parameters and accepted data types in plain language, while examples and code samples show what to expect in practice Postman's API documentation guide. Your spec feeds both, so the description fields need to read like prose, not like field names with underscores removed.

One more habit worth building: publish the raw spec file alongside the rendered docs. Consumers may want the source description to generate their own bindings or build tooling, and the OpenAPI Initiative treats making the description available to users as a straightforward bonus the OpenAPI best practices page.

The checklist before you merge

Run this against any spec change. Each item maps to a failure mode that shows up as broken examples or ugly client code.

CheckFails when ignoredWhere it bites
Every operation has a unique operationIdMethod names collide or come out unreadableClient stubs, docs anchors
Every operation has at least one tagGenerated code has no namespacesSDK ergonomics
No inline schemasDuplicate types, inferred names, bad codegenSDK types
Every property has type, and format where relevantExample values are the wrong typeMock servers, docs samples
Required fields are listedGenerated requests are rejectedContract tests
Enums declared for closed value setsExamples use invalid valuesTest fixtures
Examples present and non-nullDocs show empty payloadsReference pages, tests
servers lists are populatedGenerated docs default to localhostFirst call
Error responses have their own schemasClients get untyped errorsError handling

Two habits keep this cheap. First, generate rather than hand-write whenever possible; the OpenAPI Initiative notes that hand-written descriptions are the most terse but that approaching a large project that way is impractical, and it recommends editors, DSLs or annotations instead the OpenAPI best practices page. Second, treat the spec as part of the definition of done, which is exactly what docs-as-code asks for: block merging a feature that does not include its documentation, so descriptions get written while the change is fresh the Write the Docs guide.

When the work expands beyond a single API, or when the spec needs to feed a portal, an SDK pipeline and a set of agent tools at once, it can be worth putting a specialist on it. Quanta builds sites, apps and documents with you, and a spec-driven developer portal is a reasonable first job to hand over.

FAQ

FAQ

What is the single most common reason generated examples are invalid?

Missing constraints. When a schema omits required, type or format, the generator has to guess, and its guess will not satisfy your validator or your server. Adding explicit types, required lists and enums fixes most complaints about generated sample data APIMatic.

Should I write the OpenAPI file by hand?

For anything larger than a toy, no. The OpenAPI Initiative recommends editors, domain-specific languages or code annotations over hand-editing, while acknowledging that hand-written descriptions can be the most terse the OpenAPI best practices page. Pick one generation path and make it the source of truth.

How do I stop the spec and the client code from drifting?

Generate in CI and fail the build on any diff. Regenerating the client on every pull request and checking that the working tree is clean means a spec change cannot merge without the corresponding client change the OpenAPI best practices page.

Does a good spec replace the rest of my docs?

No. A spec produces reference material, which is one of four documentation types in Diátaxis, alongside tutorials, how-to guides and explanation Diátaxis. You still need a getting-started tutorial and task-focused guides that link to the generated reference.

What belongs in a schema description versus the guide?

Descriptions describe the field: what it holds, its units, its allowed values. Context, worked examples and multi-step workflows belong in separate guides, because keeping each document focused on one topic is a core documentation principle GitHub.

Sources

Sources

  1. Best Practices - OpenAPI Documentation
  2. Code-first: How to Generate OpenAPI from Code
  3. OpenAPI overview and best practices - Speakeasy
  4. 14 Best Practices to Write OpenAPI for Better API Consumption - APIMatic
  5. Docs as Code - Write the Docs
  6. Why we use a 'docs as code' approach for technical documentation
  7. Diátaxis
  8. Documentation done right: A developer's guide - The GitHub Blog
  9. API Documentation: How to Write, Examples & Best Practices | Postman
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles