API Documentation

OpenAPI 3.1 vs 3.0: What Changed and Why It Matters

OpenAPI 3.1 brought full JSON Schema compatibility, webhooks, and subtle breaking changes. Here is what you need to know before migrating.

Morgan KotterMarch 2, 20266 min read

The Big Picture

OpenAPI 3.1, released in February 2021, was the first major version update since 3.0 landed in 2017. The headline change — full alignment with JSON Schema (draft 2020-12) — sounds incremental. In practice, it touched nearly every corner of the specification and introduced subtle breaking changes that catch teams off guard during migration.

This article covers every significant difference, explains why each change was made, and gives practical migration guidance.

What Changed: The Complete Breakdown

Full JSON Schema Compatibility

This is the change that motivated the entire 3.1 release.

In OpenAPI 3.0, the Schema Object was a superset of JSON Schema Draft 4 with OpenAPI-specific extensions. This meant that standard JSON Schema validators rejected OpenAPI schemas, and OpenAPI validators had to implement custom logic for keywords like nullable.

OpenAPI 3.1 adopts JSON Schema draft 2020-12 without modifications. A schema that validates in JSON Schema now validates in OpenAPI, and vice versa.

Practical impact:

# OpenAPI 3.0 — nullable required a special keyword
type: string
nullable: true

# OpenAPI 3.1 — uses standard JSON Schema
type:
  - string
  - "null"

The nullable keyword is removed entirely. If your tooling generates nullable: true, it will be ignored in 3.1 — silently, in most cases.

Webhooks as a Top-Level Object

OpenAPI 3.0 had no native way to describe webhooks. Teams either abused callbacks or documented webhooks outside the spec entirely.

3.1 adds a webhooks object at the root level:

webhooks:
  orderCompleted:
    post:
      summary: Order completed notification
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrderEvent'
      responses:
        '200':
          description: Webhook received

This is a significant improvement for event-driven APIs, which are increasingly the norm.

type Can Be an Array

One of the most visible changes for schema authors. In 3.0, type was always a string. In 3.1, it can be an array:

# 3.0
type: string

# 3.1 — field accepts string or integer
type:
  - string
  - integer

This replaces the oneOf workaround that was previously necessary for union types, making schemas shorter and easier to read.

New Keywords from JSON Schema

OpenAPI 3.1 inherits several useful keywords that were previously unavailable:

  • if / then / else — Conditional schemas
  • dependentRequired — Field X is required only when field Y is present
  • prefixItems — Replaces the tuple form of items
  • $dynamicRef — Advanced recursive schema references

exclusiveMinimum and exclusiveMaximum Are Now Numbers

In JSON Schema Draft 4 (used by 3.0), these were booleans. In draft 2020-12 (used by 3.1), they are numbers:

# 3.0
minimum: 0
exclusiveMinimum: true

# 3.1
exclusiveMinimum: 0

This is a silent breaking change. Your 3.0 schemas will parse without errors in most 3.1 tools — but the semantics will be wrong.

example vs examples

In 3.0, the Schema Object had an example keyword (singular). In 3.1, JSON Schema's examples keyword (plural, an array) is preferred:

# 3.0
example: "john@example.com"

# 3.1
examples:
  - "john@example.com"
  - "jane@example.com"

The singular example still works for backward compatibility, but examples is the recommended path forward.

Complete Comparison Table

Feature OpenAPI 3.0 OpenAPI 3.1
JSON Schema version Draft 4 (modified) Draft 2020-12 (unmodified)
nullable keyword Supported Removed — use type: [string, "null"]
type as array Not supported Supported
Webhooks Not supported natively Top-level webhooks object
exclusiveMinimum Boolean Number
example keyword Singular only Singular + plural examples array
if/then/else Not available Available
$ref with siblings Not allowed Allowed
discriminator Available Available (unchanged)
pathItems in webhooks N/A Reuses Path Item Object

Breaking Changes to Watch For

1. The nullable removal

This is the most common migration issue. Every schema that uses nullable: true needs to be rewritten to use type arrays. In a large API, this can affect hundreds of fields.

2. Boolean exclusiveMinimum / exclusiveMaximum

These will silently produce incorrect validation results, not errors. Test thoroughly.

3. Tooling support

Not all tools support 3.1 yet — or they support it partially. Before migrating, verify that your code generators, validators, and documentation renderers handle 3.1 correctly.

Migration Tips

  1. Audit your nullable usage first. This is the highest-volume change. Write a script to find and replace.
  2. Update your toolchain before your spec. Ensure every tool in your pipeline handles 3.1.
  3. Migrate incrementally. Move one service at a time, not your entire API surface at once.
  4. Run both validators. During migration, validate against both 3.0 and 3.1 to catch regressions.
  5. Use Specway's auto-detection. When you import a spec into Specway, it detects the OpenAPI version automatically and renders correctly for both 3.0 and 3.1.

Should You Migrate?

If you are starting a new API — yes, use 3.1. There is no reason to start on 3.0 in 2026.

If you have an existing 3.0 spec — migrate when your toolchain is ready and when you have a concrete need (webhooks, JSON Schema alignment, or union types). A forced migration for its own sake rarely justifies the testing overhead.

Conclusion

OpenAPI 3.1 is a meaningful step forward, primarily because it eliminates the gap between JSON Schema and OpenAPI schemas. The migration is worth doing, but it is not trivial — particularly the nullable removal and the exclusiveMinimum change, which can break validation silently. Plan for it, test it, and migrate on your own timeline.

openapi
swagger
api-specs

Written by

Morgan Kotter

Ready to Get Started?

Build your first workflow in minutes. No credit card required.