
Most “the API is broken” bugs are not bugs in the API. They are bugs in the contract — the OpenAPI 3.x schema says the field is id: integer, the backend returns id: "42" as a string, and every consumer downstream has to special-case it. The fastest way to surface that mismatch is to validate a real response against the declared schema, in the browser, on a single payload. The API Response Contract Validator does exactly that: paste the spec, paste the response, point it at the path/method/status, and the tool resolves the matching response schema, then highlights missing fields, type mismatches, enum violations, and undocumented extras. This field guide walks through the seven most common contract-drift patterns, the exact validator inputs that surface them, and the workflow that turns a 30-minute spec-vs-payload reconciliation into a 30-second paste.
How the validator resolves a schema
The contract validator is not a generic JSON Schema engine. It accepts an OpenAPI 3.0 or 3.1 document, walks the paths tree to the operation you name, then resolves the response schema for the status code you specify. If the status code is missing, the validator walks the default response next, then the closest 2xx/4xx branch. Once the schema is in hand, the response payload is parsed and compared field-by-field.
The four options that matter for every paste:
- OpenAPI Spec — paste YAML or JSON. The validator auto-detects the format.
- Response JSON — paste one runtime payload. Trailing commas and unquoted keys fail parse before validation starts.
- Path / Method / Status Code — the operation pointer.
Pathis the templated form (/users/{id}), not the rendered URL. - Disallow Additional Properties — toggles whether undocumented fields are reported as warnings or silently ignored.
The OpenAPI/Swagger samples page provides a Pet Store spec and a User Management spec that resolve cleanly against the validator. For a first run, paste openapi-basic-petstore from the samples page, then paste the matching runtime response from the Pet Store demo. You should see zero findings — a clean baseline.
Why contract validation belongs in CI
A type system is only as strong as the inputs it sees. An OpenAPI document is a promise; the runtime response is the truth. The two diverge the moment a migration lands, a default value changes, or a new field is added without a spec update. A contract validator pinned to CI turns that drift into a build failure, before the consumer is updated and before the spec is forgotten.
The cost of the integration is small: the spec and the response are both strings, the validator runs in milliseconds, and the output is a structured findings array. The cost of skipping it is larger: a field rename in production, three days of “works on staging” debugging, and a postmortem about a missing CI check.
The seven contract-drift patterns
Every contract-drift bug I have shipped or inherited falls into one of seven buckets. The validator flags each with a distinct label, so once you know the pattern you can read the report in seconds.

- Missing field — the schema requires
emailand the payload omits it. Common when a server is rolled out before the consumer is updated. Reported asMISSING_REQUIRED. - Type mismatch —
idis declaredintegerbut the server emitsid: "42". Reported asTYPE_MISMATCH. This is the dominant class of bug in any JSON-over-HTTP API older than six months. - Enum violation — the schema enumerates
[pending, active, closed]and the payload says"in_progress". Reported asENUM_VIOLATION. Almost always a sign of an unmerged migration. - Format violation —
format: emailagainst"not-an-email",format: date-timeagainst"2026-13-99". Reported asFORMAT_VIOLATION. These pass syntactic JSON parsing but fail semantic validation. - Additional property — the payload includes a
legacy_user_idfield that the schema does not declare. By default the validator ignores it; withDisallow Additional Propertiestoggled on, it is reported asADDITIONAL_PROPERTY. - Null on a non-nullable —
nullable: false(or the OpenAPI 3.1 absence of anulltype) but the payload has"deleted_at": null. Reported asTYPE_MISMATCHwith anullexpected type. Common after a soft-delete column lands in the database. - Array item shape — the schema declares
items: {type: object, required: [id]}and one array element is missingid. Reported asMISSING_REQUIREDbut at the items path, not the root.
Run those seven checks against any production response and you will catch every regression the type system can express.
The OpenAPI 3.1 type union gotcha
OpenAPI 3.1 (released July 2021, increasingly the default) aligned the spec with JSON Schema 2020-12. The biggest user-visible change is type is now a single string or a list of types, and nullable: true is gone. The new pattern is to write type: [string, "null"] for a nullable string. The validator honors both forms, so a 3.0 spec with nullable: true and a 3.1 spec with type: [string, "null"] validate the same way.
The gotcha is in the consumer’s expectation. If the OpenAPI document is 3.1 but the consumer’s generated client library is older, the library may serialize null as the empty string and the validator will report TYPE_MISMATCH for every nullable field. The fix is either to regenerate the client with a 3.1-aware codegen, or to downgrade the spec to 3.0 with explicit nullable: true flags and re-validate. The validator does not care which you choose; it just needs the spec and the payload to be self-consistent.
Disallow Additional Properties: when to turn it on
The toggle only affects the report. It does not change the validator’s parse of the spec or the payload. So you can iterate with it off, then re-run with it on once the report is clean against the documented fields.
- Turn it on for greenfield APIs where the schema is the source of truth and the server is the only producer. Any field the schema does not declare is a bug.
- Turn it on for third-party integrations where you are testing how a vendor responds versus the contract you agreed to. Unexpected fields often signal undocumented behavior you will be forced to support forever.
- Turn it off for legacy internal APIs with hand-maintained JSON. You will drown in
ADDITIONAL_PROPERTYwarnings for fields nobody can remove, and the report will become useless.
Pointing at the right operation
The most common reason a validation comes back empty is the path/method/status pointer does not match. Three rules resolve 90% of mis-pointing:
- Path is templated. Write
/users/{id}, not/users/123. The validator uses the spec’s key, not the rendered URL. - Method is uppercase.
GET,POST,PUT,PATCH,DELETE. Anything else returns zero operations. - Status is the exact declared code. If the spec declares
200and201for a POST, validate against each. If the spec only declares adefaultresponse, the validator falls back to that branch automatically.
A second, more subtle mis-pointing happens with oneOf and anyOf at the operation level. The validator walks the response schema as a single branch; if the spec uses oneOf to express content-negotiation, the validator may flag a TYPE_MISMATCH on a perfectly valid response because it picked the wrong branch. The workaround is to validate against the specific sub-schema the consumer uses, not the operation’s top-level response.

Workflow: from spec change to green build

The workflow that keeps contract drift out of production has four steps, all of which fit inside a CI job that runs in under ten seconds:
- Snapshot the spec at build time. The validator accepts the spec as a string, so a
cat openapi.yamlpiped to a fixture file is enough. - Snapshot a known-good response from staging. The validator accepts JSON, so a
curl -s $STAGING/users/42 > response.jsonis enough. - Run the validator headless. With the spec, the response, the path, the method, and the status in hand, the validator returns a structured findings array. CI fails on any non-empty findings array.
- Block on
ADDITIONAL_PROPERTYfor production. Same workflow, with the toggle on, deployed to prod. Any field the server emits that the spec does not document is a deploy-blocker.
This is the same workflow the JSON Schema Generator tool covers for the inverse case — generating a schema from a sample payload — and the OpenAPI Diff & Breach Detector covers for the diff case (spec changed, what broke). The three tools together form a round-trip: generate a schema from samples, validate live responses against the schema, diff a candidate spec against the published one.
Patterns worth memorizing
- A clean validator run on staging is the cheapest contract regression test you can write. It runs in seconds, requires no test framework, and catches every type the type system can express. Run it on every PR.
Disallow Additional Propertiesis a policy decision, not a technical one. The toggle does not change parsing; it changes the report. Decide per-API whether undocumented fields are a bug or a feature.- Type unions and
nullgotchas are the first thing to check when a validator run flags a field the consumer treats as nullable. The mismatch is almost always on the consumer’s end, not the spec’s. - Path is templated, status is exact, method is uppercase. Memorize these three rules and you will never have to debug a “validator says zero findings but the response is clearly wrong” report again.
- Snapshot the spec and the response in CI, not just the test fixtures. Contract drift is a runtime property. Testing against hand-written fixtures is testing the spec against itself.
The contract validator is one of those tools that looks trivial on the first run and becomes load-bearing on the second. Once you have it wired into CI, you stop arguing about what the API is supposed to do and start shipping the answer. Browse the full Elysia Tools catalog for the rest of the pipeline — schema generation, OpenAPI diffing, and the rest of the response-payload tooling that turns an OpenAPI document into a working contract.