How to Write OpenAPI Specs That Don't Lie to AI Agents
Your OpenAPI spec is the most important document in your codebase. Not for humans — for AI agents. Here's how to write OpenAPI specs that both humans and AI agents can trust.
How to Write OpenAPI Specs That Don’t Lie to AI Agents
Your OpenAPI spec is a lie.
Not intentionally. Nobody sits down to write a deliberately misleading API specification. But the spec says customer_id is a string. The code treats it as a UUID. The database stores it as an integer. And the AI agent that read your spec at 2 AM generated code that passes a UUID to an endpoint that expects an integer.
The spec lied. The agent believed it. Your production logs are the evidence.
This post is about writing OpenAPI specs that don’t lie — to humans or to AI agents. Not “better” specs. Not “more complete” specs. Honest specs. Specs where what’s written matches what the code does, down to the type, the format, the constraint, and the error.
Why OpenAPI Matters More Than Your Docs
Here’s a truth that the documentation industry doesn’t want to admit: your prose documentation is secondary. Your OpenAPI spec is primary.
When a human reads your docs, they’re reading your interpretation of the API. When an AI agent reads your docs, it’s reading your OpenAPI spec. The prose is context. The spec is the contract.
This means the accuracy of your OpenAPI spec matters more than the accuracy of your getting-started guide. A wrong code sample in a tutorial is annoying. A wrong type in your spec is a production failure waiting to happen — at machine speed, at scale, with no human in the loop to catch it.
And yet, most teams treat OpenAPI specs as an afterthought. They auto-generate them from code comments (which are wrong), or they write them by hand (and never update them), or they generate them from the code (but the code has drifted from the spec). The spec is the least-trusted document in the stack.
That needs to change.
The Five Lies OpenAPI Specs Tell
After reviewing hundreds of OpenAPI specs, we’ve identified five patterns of dishonesty that show up repeatedly. Each one is small. Each one is invisible until an AI agent acts on it.
Lie #1: The Type Mismatch
# What the spec says:
customer_id:
type: string
format: uuid
# What the code does:
# customer_id is actually an auto-incrementing integer
# The UUID is in a separate `external_id` field
This is the most common lie. The spec says one type, the code enforces another. Humans catch this because they test the API and see the error. AI agents don’t test — they trust. They generate code with a UUID. The code fails. The developer blames themselves.
The fix: Validate your spec against actual API responses. Not against the code — against the running API. If the spec says format: uuid but the API returns 12345, the spec is wrong.
Lie #2: The Phantom Parameter
# What the spec says:
parameters:
- name: include_deleted
in: query
type: boolean
default: false
# What the code does:
# This parameter was removed in v2.3
# The endpoint now always excludes deleted records
The parameter exists in the spec but not in the code. An AI agent sees it, includes it in generated code, and the API silently ignores it. No error. No warning. Just dead code that the developer doesn’t understand.
Or worse: the parameter was added to the code but not the spec. The agent doesn’t know it exists. The developer doesn’t use it. A feature ships and nobody uses it because the docs don’t mention it.
The fix: Every parameter in your spec should be testable. Send a request with the parameter. If the behavior doesn’t change, the spec is lying.
Lie #3: The Error That Never Happens
# What the spec says:
responses:
400:
description: "Invalid request body"
404:
description: "Resource not found"
500:
description: "Internal server error"
# What the code actually returns:
# 400: never returned (validation happens at a different layer)
# 404: returned as 204 No Content
# 500: returned as 503 with a retry-after header
# 429: returned but not documented at all
Error documentation is where specs go to die. Teams document the errors they intended to return, not the errors the code actually returns. The result is a spec that describes an API that doesn’t exist.
AI agents use error documentation to generate error handling code. If the spec says 400 but the API returns 429, the agent generates code that handles the wrong error. The retry logic never fires. The rate limit is never respected.
The fix: Log your actual error responses for a week. Compare them to your spec. Update the spec to match reality, not intention.
Lie #4: The Constraint That Isn’t
# What the spec says:
name:
type: string
minLength: 1
maxLength: 100
pattern: '^[a-zA-Z0-9_]+$'
# What the code does:
# Accepts any string up to 255 characters
# No pattern validation
# Unicode is fine
The spec describes constraints that don’t exist in the code. An AI agent reads the spec and generates client-side validation that matches the spec. The user enters a 150-character name with emoji. The client-side validation passes (because the agent trusted the spec). The API accepts it. The database truncates it. Data is lost.
Or the reverse: the code has constraints the spec doesn’t mention. The agent generates code without validation. The API rejects the request. The developer is confused because the spec said it was fine.
The fix: Constraints in the spec should be tested, not just documented. Send a request that violates each constraint. If the API accepts it, the spec is wrong.
Lie #5: The Silent Default
# What the spec says:
limit:
type: integer
default: 20
maximum: 100
# What the code does:
# Default is actually 50 (changed in a refactor, spec never updated)
# Maximum is actually 250 (increased for enterprise customers)
# Values above 100 require a special header
Defaults are the most dangerous lie because they’re the least visible. Nobody tests the default value. Nobody reads the default value. The agent just uses it. And when the default is wrong, the behavior is wrong in a way that’s hard to debug — because the code looks correct.
The fix: Document defaults explicitly. Test them. And when you change a default in the code, treat it as a breaking change — because it is.
The Validation Loop
Here’s the thing about these five lies: they’re not malicious. They’re structural. They happen because the spec and the code are written by different people, at different times, with different assumptions. The drift is natural. The dishonesty is a side effect.
The fix isn’t to write better specs. The fix is to validate specs continuously. Not once. Not during code review. Continuously.
Here’s what that looks like:
1. Generate test cases from your spec. Every endpoint, every parameter, every response code. If your spec says 429 is a possible response, there should be a test that triggers it.
2. Run those tests against your actual API. Not against a mock. Not against the code. Against the running API. If the test fails, either the test is wrong or the spec is wrong. Either way, something needs to change.
3. Compare the results to your spec. If the API returns errors that aren’t in the spec, update the spec. If the spec describes parameters that don’t exist, remove them. If the types don’t match, fix one or both.
4. Run this on every commit. Not on a schedule. Not during sprint planning. On every commit. Because drift happens on every commit. The spec that was accurate yesterday is inaccurate today because someone renamed a field, added a parameter, or changed a default.
This is the boringdocs thesis, applied specifically to OpenAPI: the spec is a contract, and contracts need continuous validation. Not manual review. Not good intentions. Automated, continuous, relentless validation.
What This Means for Your Team
If you’re building an API in 2026, your OpenAPI spec is not a documentation artifact. It’s a machine interface. AI agents read it. Code generators parse it. SDK builders consume it. Your spec is an API for APIs.
Treat it like one.
- Version it with your code. The spec for v2.1 should ship in the same commit as the v2.1 code. Not in a separate PR. The same commit.
- Validate it continuously. Every commit should trigger spec validation. Not linting — validation. Does the spec match the running API?
- Test the errors. Don’t just document 200 OK. Document and test every error response. Especially the ones that “should never happen.”
- Be honest about defaults. If you changed the default, update the spec. If you’re not sure what the default is, find out. Don’t guess.
The Boring Part
Here’s what nobody wants to hear: writing honest OpenAPI specs is boring. It’s not creative. It’s not strategic. It’s checking that the type in the spec matches the type in the code. It’s verifying that the error response you documented is the one the API actually returns. It’s testing that the default value is what you say it is.
It’s the kind of work that humans skip and AI agents don’t. Which is exactly why it matters.
Your OpenAPI spec is the most important document in your codebase. Not because it’s the most read — it’s not. But because it’s the most trusted. AI agents trust it. Code generators trust it. Your SDK pipeline trusts it.
Make it worth trusting.
Your OpenAPI spec is a contract. Contracts need validation. Join the waitlist — boringdocs continuously validates your specs against your code, so the AI agents reading your docs generate code that actually works.