What changed between OpenAPI 3.0 and 3.1
OpenAPI 3.1 makes the Schema Object fully compatible with JSON Schema draft 2020-12, where 3.0 used an extended subset of an older draft. Most migration work happens in schemas: nullable gives way to type arrays, exclusiveMinimum and exclusiveMaximum become numbers, and example becomes examples.
At the document level, 3.1 adds a top-level webhooks field and no longer requires paths. Although 3.1 is a minor version, the specification allows minor releases to include incompatible changes, and 3.1 has some. Tools written for 3.0 may misread a 3.1 document.
How OpenAPI 3.1 relates to JSON Schema
The 3.1 specification describes its Schema Object as a superset of JSON Schema draft 2020-12. A valid 2020-12 schema is a valid 3.1 schema, and OpenAPI adds a few keywords of its own: discriminator, xml, externalDocs and the now-deprecated example.
In 3.0 the Schema Object was an extended subset of JSON Schema Wright Draft 00. It left out some keywords, adjusted the meaning of others and added nullable, so a standard JSON Schema validator could disagree with OpenAPI tools about the same schema.
In 3.1 you can also use newer keywords such as const, if, then, else, prefixItems and unevaluatedProperties. A root jsonSchemaDialect field, or $schema inside a schema, can declare a different dialect.
Schema keywords that changed
These are the edits most 3.0 documents need:
- nullable: true is gone. List null as a type instead, as in type: [string, "null"], or use anyOf with a schema whose type is "null".
- exclusiveMinimum and exclusiveMaximum were booleans that modified minimum and maximum. They are now numbers, so minimum: 0 with exclusiveMinimum: true becomes exclusiveMinimum: 0.
- example in a Schema Object is deprecated in favor of examples, an array of sample values.
- 3.0 ignored keywords placed next to $ref. In a 3.1 Schema Object, they apply alongside the referenced schema.
- File content is described with contentMediaType and contentEncoding instead of format: binary and format: byte.
# OpenAPI 3.0
Product:
type: object
properties:
discount:
type: number
nullable: true
minimum: 0
exclusiveMinimum: true
example: 0.15
manual:
$ref: '#/components/schemas/Document'
description: Has no effect in 3.0
---
# OpenAPI 3.1
Product:
type: object
properties:
discount:
type: [number, 'null']
exclusiveMinimum: 0
examples: [0.15]
manual:
$ref: '#/components/schemas/Document'
description: Takes effect in 3.1Document-level additions in 3.1
Outside schemas, 3.1 adds a handful of fields:
- webhooks, a top-level map of requests your API sends to consumers, described like operations
- Optional paths: a document needs at least one of paths, components or webhooks
- info.summary, and license.identifier for an SPDX license expression
- components.pathItems, for reusable path items
- The mutualTLS security scheme type
- summary and description on a Reference Object, which by default override those of the referenced component
Migration checklist
Work through the document in this order, then confirm that nothing changed on the wire:
- Change openapi: 3.0.x to 3.1.0 or a later 3.1 patch release.
- Replace every nullable: true with a type array that includes "null". For a nullable $ref, use anyOf with the $ref and a "null" type. For a nullable enum, add null to the enum values too.
- Quote "null" in YAML type arrays. A bare null is the YAML null value, not the string the type keyword needs.
- Turn exclusiveMinimum: true or exclusiveMaximum: true into a number that replaces the paired minimum or maximum, and delete any set to false.
- Move schema example values into examples arrays. The example and examples fields on parameters and media types work as before.
- Check every $ref that has sibling keywords. 3.0 ignored them, so any that remain now take effect.
- Rewrite file fields: a base64 string (format: byte) gets contentEncoding: base64, and raw binary content uses contentMediaType instead of format: binary.
- Confirm that your validator, code generator, documentation renderer, gateway and mock server support 3.1. Keep a 3.0 copy if one of them doesn't.
- Validate the new document, then compare old and new with a contract diff. A pure migration should show no breaking changes.