What Is an API Schema? A Practical Guide to Request and Response Contracts
What an API schema constrains in a JSON message, how OpenAPI 3.1 relates to JSON Schema, and which decisions the schema file does not make.
An API schema is a written constraint on the shape of a message. It names the fields a sender may use, the types those fields take, and which names have to be present. It is not, by itself, the whole contract. Status codes, side effects, and the promise that an old client keeps working are separate statements. This note is about the schema layer. Which field changes stay compatible is the next question, and how a version records a compatibility promise is the one after that.
What the document is constraining
JSON, as RFC 8259 defines it, is a text format whose values are objects, arrays, numbers, strings, true, false, and null. A schema does not replace that format. It narrows which of those values are acceptable in one place.
The OpenAPI Specification 3.1.0 describes an HTTP API. In that specification, data types are based on JSON Schema Draft 2020-12, with integer added as a number that has no fraction or exponent. Models are Schema Objects. The specification says a Schema Object is a superset of that JSON Schema draft: the usual JSON Schema keywords apply, and the object may also carry other keywords.
Two version fields in the same file are easy to mix up. The openapi field is the version of the OpenAPI Specification the document uses. The info.version field is the version of the OpenAPI document, and the specification says that string is distinct from both the specification version and the API implementation version. Neither field, on its own, tells a client whether a change is safe to ignore. That judgment is the subject of versioning, not of the schema keywords.
Request schema and response schema
A useful description keeps the request and the response apart. They are different messages, often with different required fields. In OpenAPI the request body and each response can carry their own schema through a Media Type Object. Reusing one schema for both directions feels tidy and then fails the first time the server returns an id the client must not send.
The illustration below is not a captured message. It shows the split, not a live API.
A document can hold all three. The boxes are a map of the split, not a message trace.
{
"request": {
"type": "object",
"required": ["sku", "quantity"],
"properties": {
"sku": { "type": "string" },
"quantity": { "type": "integer", "minimum": 1 }
}
},
"response": {
"type": "object",
"required": ["id", "sku", "quantity"],
"properties": {
"id": { "type": "string" },
"sku": { "type": "string" },
"quantity": { "type": "integer" }
}
}
}
Illustration, not a captured message. The two objects are separate schemas.
The same split applies to failures. The shape of an error body is a schema too, and it should not be an undocumented variant of the success body.
There is a second “required” that is not the JSON Schema keyword. In OpenAPI, a parameter object has a boolean required. For a path parameter that boolean is required and must be true. For other parameters it defaults to false. A request body object has its own boolean required, defaulting to false. Those booleans say whether the parameter or body has to appear. The JSON Schema required array says which property names have to appear inside a JSON object. Mixing the two up produces a document that looks strict and is not.
Required, optional, and null
In JSON Schema Draft 2020-12, an object is valid against required when every name in that array is a property of the instance. The keyword is about presence of the name. It does not, by itself, say that the value is non-null, non-empty, or inside a range. Those are other keywords (type, minLength, minimum, and so on).
Absence and null are different JSON values. RFC 8259 includes null as a value. A schema that says "type": "string" does not accept null unless null is also an allowed type. A client that omits a field and a client that sends the field as null are making two different statements. If both are meant to be accepted, the schema has to say so on purpose.
Listing a name under properties does not forbid other names. JSON Schema’s additionalProperties keyword is what constrains properties that properties and patternProperties did not already cover. Setting it to false rejects unknown names. Leaving it off does not. That default is useful when a response may grow, and it is a trap when a receiver wanted a closed list and never wrote the keyword. What to validate, and where comes back to this, because a validator can only enforce the keywords that are actually in the schema.
Enums and unknown values
The enum keyword lists the values an instance may take. It is a closed list for validation: a value outside the list fails that schema. It does not say whether a future document may add a value, or what an old client should do when it sees one. Those are compatibility decisions.
If consumers treat the list as exhaustive in their own code, adding a value is not a free change even though the old messages still validate. Record that assumption next to the enum. The compatibility note walks through how response enums stay compatible with old clients.
What the schema does not decide
A schema can be precise and still leave the contract unfinished.
| Question | Where it is answered | Typical mistake |
|---|---|---|
| Which property names and types are allowed? | Schema keywords such as type, properties, required, enum |
Treating an example value as if it were a constraint |
| May unknown property names appear? | additionalProperties, when it is present |
Assuming properties closes the object |
| Which HTTP status is returned? | The API contract, not the schema keywords | Hiding the error inside a 200 response with a boolean flag |
| Will an old client survive a new field? | An explicit compatibility rule | Inferring the rule from info.version alone |
The middle column is a classification used by this desk. Only the schema keywords are defined by the cited JSON Schema and OpenAPI documents. The compatibility and status rows are not measurements.
OpenAPI allows an example on a Schema Object, and marks that property deprecated in favor of the JSON Schema examples keyword. An example shows one valid-looking instance. It does not add a constraint unless a keyword does. Reviewers should not “read” a required field out of an example that happens to include it.
A field checklist before you publish a schema
Use this before a schema is treated as something clients can build against. It is a review list, not a score.
- The request schema and each response schema, including the error body, are separate objects where their fields differ.
- Every name in
requiredis also described, and the description says what absence means. - Fields that may be JSON
nullallownullin their type. Fields that may be omitted are simply not required. additionalProperties: falseis present only where unknown names must fail. Otherwise the document does not pretend the object is closed.- Each
enumsays whether new values are expected, and where that decision is recorded. - OpenAPI parameter and request-body
requiredbooleans are not confused with the schemarequiredarray. info.versionis not used as a substitute for a compatibility note.- Examples are labeled as examples. They are not the only definition of the type.
Open questions
A schema document does not tell you when to run it. Some teams validate only in tests. Some validate every request in production and reject unknown fields. Both can be consistent with the same file. The file becomes a contract only after the publisher says which failures a client will actually see, which is the point of when a schema is enforced.
Sources
- OpenAPI Specification 3.1.0 — OpenAPI Initiative
- JSON Schema Validation, Draft 2020-12, section 6.5.3 (required) — JSON Schema
- JSON Schema Core, Draft 2020-12, section 10.3.2.3 (additionalProperties) — JSON Schema
- RFC 8259, The JavaScript Object Notation (JSON) Data Interchange Format — IETF