Getting ChatGPT to Write Accurate OpenAPI Specs Without Drifting From Your Code
You paste a route description into ChatGPT, ask for an OpenAPI spec, and get back something that looks plausible. Then you try to validate it and find invented query parameters, a security scheme that doesn't match your actual auth middleware, and response fields your API has never emitted. The more rounds of revision you do, the further the spec drifts from the real code.
The fix isn't a better prompt in isolation — it's a structured workflow that gives ChatGPT the right grounding material before you ask it to generate anything.
What You'll Learn
- How to feed ChatGPT concrete route signatures so it can't invent endpoints
- How to use a seed schema to constrain field generation
- How to anchor auth and response envelopes to your actual implementation
- How to validate the output with free tooling before you ship it
- The most common drift patterns and how to prevent them
Prerequisites
This guide assumes you already have a working API (REST, any language) and know roughly what OpenAPI 3.x looks like. You don't need to be a YAML expert, but you should be able to read a basic spec and spot an obviously wrong field. A free ChatGPT account is all you need on the AI side.
The Real Problem With AI-Generated OpenAPI Specs
ChatGPT's training data contains thousands of OpenAPI examples, but none of them are your API. When you give it a vague description like "write a spec for a user registration endpoint," it fills the gaps with statistically likely choices: a 201 response with a generic user object, a message field in error responses, maybe a Bearer token header. Some of those guesses will be wrong for your codebase.
The deeper issue is that OpenAPI specs have a lot of surface area. Parameters, request bodies, response schemas, security definitions, shared components — a hallucinated field in one place gets referenced elsewhere, and the spec becomes internally inconsistent in ways that are hard to catch by eye.
The solution is to treat ChatGPT as a YAML typist, not as the source of truth. You supply the facts; it handles the syntax and structure.
Give ChatGPT Your Actual Route Signatures, Not a Vague Description
The single highest-leverage thing you can do is paste your real route definitions into the prompt. ChatGPT can't invent a path parameter that isn't in your code if you show it the code.
For a Python FastAPI app, that means copying the function signature directly:
@router.post("/users/{org_id}/invite", status_code=201)
async def invite_user(
org_id: UUID,
body: InviteRequest,
current_user: User = Depends(get_current_user),
) -> InviteResponse:
...
Pair that with the Pydantic models:
class InviteRequest(BaseModel):
email: EmailStr
role: Literal["admin", "member", "viewer"]
expires_at: datetime | None = None
class InviteResponse(BaseModel):
invite_id: UUID
status: Literal["pending", "accepted"]
Then your prompt becomes surgical:
Given the FastAPI route and Pydantic models below, write an OpenAPI 3.1.0 path
item for POST /users/{org_id}/invite. Use $ref components for InviteRequest and
InviteResponse. Document a 201 on success, 422 for validation errors, and 401
for missing auth. Do not add any fields that are not present in the models.
[paste route + models here]
The explicit instruction "do not add any fields that are not present in the models" sounds obvious, but it measurably reduces hallucinated properties in practice.
If you're using Express or another framework without type annotations, paste the router file anyway. Even untyped JavaScript routes carry the path, method, and the shape of req.body if you validate it with a library like Zod or Joi — paste those schemas too.
Provide a Seed Schema and Let ChatGPT Extend It
A blank-slate generation is where drift is worst. Instead of asking ChatGPT to produce a full spec from scratch, start with a skeleton you control and ask it to fill in one section at a time.
Create a openapi-base.yaml that you maintain by hand:
openapi: 3.1.0
info:
title: Acme Platform API
version: "2.4.0"
servers:
- url: https://api.acme.io/v2
security:
- BearerAuth: []
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas: {}
paths: {}
Now your prompt for each endpoint becomes:
Here is my openapi-base.yaml. Add the path item and component schemas for the
route below. Return only the YAML blocks I need to merge in — the paths entry
and any new components.schemas entries. Do not change info, servers, or
securitySchemes.
[base yaml]
[route code]
This constraint prevents ChatGPT from rewriting your server URL, swapping your auth scheme, or changing version numbers on each pass. You're doing incremental, reviewable additions instead of one monolithic generation.
This pattern also pairs well with the workflow described in writing accurate webhook handlers with ChatGPT — the same principle of feeding real code rather than descriptions applies across spec types.
Anchor Auth Schemes to Your Middleware, Not Assumptions
Auth is where specs drift most silently. ChatGPT may generate an apiKey scheme when your API uses OAuth2, or it may document a X-API-Key header that your middleware doesn't actually check. This creates false documentation that misleads integrators.
Show ChatGPT your actual auth middleware or dependency code alongside the route. In FastAPI that's the get_current_user dependency; in Express it might be a Passport strategy or a custom middleware function. Even a pseudocode summary is better than nothing:
Auth is enforced via a JWT in the Authorization header (Bearer scheme). The
token is validated with RS256. There are no API keys. Public endpoints are
explicitly decorated with @public — all others require a valid token.
Document security accordingly. Do not invent alternative auth schemes.
Pinning the security definition in your base YAML (as shown above) reinforces this even further — ChatGPT has less room to improvise if the scheme is already defined and you're only asking it to reference it.
Lock Down Response Envelopes With Concrete Examples
Many APIs wrap responses in a standard envelope — something like {"data": {}, "meta": {}, "errors": []}. ChatGPT may omit the envelope entirely, put the payload at the top level, or invent an envelope shape it saw in training data.
The fastest fix is to paste one real JSON response from your API into the prompt and tell ChatGPT to match it exactly:
{
"data": {
"invite_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "pending"
},
"meta": {
"request_id": "req_01HX..."
}
}
The 201 response body always uses this exact envelope. data contains the
InviteResponse schema. meta always contains request_id as a string. There is
no errors key on success responses. Match this structure in the schema.
If you have error responses with a consistent shape, paste one of those too. Concrete JSON beats abstract prose every time when you're trying to constrain LLM output — the same lesson that applies when you're getting ChatGPT to write accurate API integration code.
Validate the Output Before You Trust It
Even with all the above constraints, you should validate every generated spec with tooling. ChatGPT can produce syntactically valid YAML that still violates OpenAPI rules — referencing a component that doesn't exist, using a type value that's invalid in OpenAPI 3.1, or nesting allOf incorrectly.
Swagger Editor (browser-based)
Paste the YAML into editor.swagger.io. It shows schema errors inline and renders a preview. This is the fastest sanity check for a single endpoint addition.
Spectral CLI
For anything going into a repo or CI pipeline, use Spectral. It enforces both the OpenAPI ruleset and any custom rules you define:
npx @stoplight/spectral-cli lint openapi.yaml --ruleset @stoplight/spectral-openapi
Run this locally after each ChatGPT-generated addition. If you want Spectral in your CI pipeline, the guide on accurate CI/CD pipeline configs covers how to structure that without broken steps.
Ask ChatGPT to Self-Review
After generating a spec section, follow up with:
Review the YAML you just wrote. Check for:
1. Any $ref that points to a component not defined in the spec
2. Any response code documented without a corresponding schema
3. Any parameter or field not present in the original route or model code
4. Any security scheme referenced but not defined
List issues found, then produce a corrected version.
This self-review pass catches the most common structural errors before you run external tooling. It doesn't replace Spectral, but it reduces the iteration count significantly.
Common Pitfalls That Cause Drift
Asking for the entire spec in one shot
The more endpoints you describe in a single prompt, the more ChatGPT has to interpolate. Generate one path item at a time, validate it, then move to the next. It takes a few more prompts but produces far fewer errors.
Describing behavior instead of showing code
"The endpoint accepts a user object" is ambiguous. "Here is the Zod schema for the request body" is not. Always prefer code over prose when the code exists. This mirrors the advice in making ChatGPT write idiomatic code in your stack — the model performs better when it can pattern-match against real artifacts.
Not pinning the OpenAPI version
OpenAPI 3.0.x and 3.1.0 are not wire-compatible in their schema handling. In 3.1.0, nullable is dropped in favor of JSON Schema's type: ["string", "null"]. If you don't specify the version, ChatGPT may mix both conventions in one spec. Always include the version in your seed YAML and repeat it in the prompt.
Letting ChatGPT infer error schemas
If you don't show ChatGPT what your 422 or 400 response bodies look like, it will invent one. Paste your actual error response shape once, define it as a shared component, then tell ChatGPT to reference that component for all error responses across every endpoint.
Treating revision prompts as safe
When you ask ChatGPT to "update the spec to add pagination," it sometimes rewrites sections it shouldn't touch. Always diff the output against your last accepted version. A simple git diff after saving the file will surface unintended changes before they get committed.
Wrapping Up: Keeping Specs Honest Over Time
The workflow above gets you an accurate spec for the current state of your API. Staying accurate over time requires a few additional habits:
- Keep your seed YAML in version control alongside your source code. When a route changes, update the spec in the same PR — use ChatGPT to generate the delta, not a full rewrite.
- Run Spectral in CI on every pull request that touches route files or the spec file. A failing lint step is cheaper than a wrong spec reaching your integrators.
- Add a spec review step to your PR template. A checkbox like "OpenAPI spec updated and validated" makes drift a conscious choice rather than an accident.
- Generate example requests and responses from the spec after each update using a tool like
openapi-examples-validatorand compare them against your actual API responses in a test environment. - Re-anchor ChatGPT on each session. Paste the current spec section at the start of every new conversation. ChatGPT has no memory of last session's constraints, so treating each session as if it's the model's first look at your API prevents compounding drift.
Frequently Asked Questions
Why does ChatGPT keep adding fields to my OpenAPI spec that don't exist in my API?
ChatGPT fills gaps in your prompt with statistically common patterns from its training data. If you describe your API in prose rather than showing the actual code, it has room to invent plausible-sounding fields. Fix this by pasting your real route signatures and model definitions directly into the prompt and explicitly instructing it not to add fields absent from those definitions.
How do I stop ChatGPT from changing the OpenAPI version or server URL on every revision?
Use a seed YAML file that you maintain by hand, containing your info block, servers, and security definitions. Ask ChatGPT to return only the path items and component schemas to merge in, explicitly telling it not to modify info, servers, or security schemes. This keeps the stable parts of the spec under your control.
What is the best free tool to validate a ChatGPT-generated OpenAPI spec?
Swagger Editor (editor.swagger.io) is the fastest browser-based option for catching structural errors. For CI pipelines and more thorough rule enforcement, the Spectral CLI from Stoplight is the standard choice and supports custom rulesets beyond the core OpenAPI rules.
Can I use ChatGPT to generate OpenAPI specs for an Express.js API that has no TypeScript types?
Yes, but you need to substitute type information with your request validation schemas. Paste your Zod, Joi, or express-validator schemas alongside the route definition — those carry the same field and type information that Pydantic or TypeScript types would in a typed codebase. The more concrete the schema you provide, the less ChatGPT has to guess.
How do I keep my OpenAPI spec from drifting when the API changes frequently?
Store your spec in version control next to your source code and update it in the same pull request as any route change. Running Spectral as a CI lint step on PRs that touch route files creates a forcing function to keep the spec current. Treat each ChatGPT session as stateless — re-paste the relevant spec section at the start of every new conversation.
📤 Share this article
Sign in to saveRelated Articles
Comments (0)
No comments yet. Be the first!