TL;DR
OpenAPI 3.1 aligns schemas with JSON Schema Draft 2020-12 and introduces a top-level webhooks object. OpenAPI 3.2 adds support for the HTTP QUERY method and extends the specification for newer API patterns. Modern PetstoreAPI uses OpenAPI 3.2 to demonstrate these features with practical examples.
Introduction
When writing an OpenAPI specification, you must choose between OpenAPI 3.0, 3.1, and 3.2. That choice affects schema syntax, webhook definitions, supported HTTP methods, and compatibility with validators, documentation generators, and code generators.
The original Swagger Petstore uses Swagger 2.0. Modern PetstoreAPI uses OpenAPI 3.2 to demonstrate newer OpenAPI capabilities.
If you are building or testing REST APIs, Apidog supports importing OpenAPI 3.0, 3.1, and 3.2 specifications. You can validate schemas, inspect references, and test whether an implementation matches its contract.
This guide covers:
- The baseline features in OpenAPI 3.0
- The JSON Schema changes introduced in OpenAPI 3.1
- OpenAPI 3.2 features such as
QUERY - A practical migration process
- How to validate the result before upgrading production tooling
OpenAPI 3.0: The Baseline
Released in July 2017, OpenAPI 3.0 was a major upgrade from Swagger 2.0.
Start a 3.0 document with:
openapi: 3.0.3
info:
title: Petstore API
version: 1.0.0
1. Define multiple servers
OpenAPI 3.0 replaced Swagger 2.0's single host, basePath, and schemes configuration with a servers array:
servers:
- url: https://api.petstoreapi.com/v1
description: Production
- url: https://staging.petstoreapi.com/v1
description: Staging
This lets documentation and testing tools switch between environments without modifying every operation.
2. Use a dedicated request body
In Swagger 2.0, JSON request bodies were defined as body parameters. OpenAPI 3.0 introduced requestBody:
paths:
/pets:
post:
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
responses:
'201':
description: Pet created
You can also define different schemas for different media types:
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
application/xml:
schema:
$ref: '#/components/schemas/Pet'
3. Reuse definitions with components
OpenAPI 3.0 consolidated reusable definitions under components:
components:
schemas:
Pet:
type: object
properties:
id:
type: string
name:
type: string
responses:
NotFound:
description: Resource not found
parameters:
PetId:
name: petId
in: path
required: true
schema:
type: string
Reference these definitions with $ref:
parameters:
- $ref: '#/components/parameters/PetId'
4. Describe callbacks
Callbacks describe requests that the API provider sends after a client operation:
callbacks:
orderUpdate:
'{$request.body#/callbackUrl}':
post:
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/OrderUpdate'
responses:
'200':
description: Callback accepted
The callback URL can be derived from the original request using a runtime expression.
5. Connect operations with links
Links describe how a response value can be passed to another operation:
links:
GetPetByPetId:
operationId: getPetById
parameters:
petId: '$response.body#/id'
This is useful for documentation, API exploration, and generated workflows.
OpenAPI 3.0 limitations
OpenAPI 3.0 remains widely supported, but it has several constraints:
- Its schema format is not fully compatible with standard JSON Schema.
- Webhooks must be represented through operation-level callbacks.
- Polymorphic schemas can behave differently across tools.
- Nullable values require the OpenAPI-specific
nullablekeyword. - Some standard JSON Schema keywords are unavailable or behave differently.
For example, a nullable string in OpenAPI 3.0 is written as:
type: string
nullable: true
OpenAPI 3.1: JSON Schema Alignment
OpenAPI 3.1 aligns its Schema Object with JSON Schema Draft 2020-12.
Start a 3.1 document with:
openapi: 3.1.0
info:
title: Petstore API
version: 1.0.0
1. Replace nullable with JSON Schema types
OpenAPI 3.0:
type: string
nullable: true
OpenAPI 3.1:
type:
- string
- "null"
You can also use anyOf when the schema needs more complex validation:
anyOf:
- type: string
- type: "null"
JSON Schema alignment provides several practical benefits:
- Schemas can use standard JSON Schema validators.
- Schema definitions can be shared with other JSON Schema-based systems.
- You can use JSON Schema 2020-12 keywords.
- Validation behavior is more consistent across compatible tools.
You can explicitly declare the schema dialect:
jsonSchemaDialect: https://json-schema.org/draft/2020-12/schema
2. Define top-level webhooks
OpenAPI 3.1 introduced a top-level webhooks object.
In OpenAPI 3.0, webhook-like behavior was usually modeled as a callback attached to an operation:
paths:
/subscribe:
post:
callbacks:
orderUpdate:
'{$request.body#/callbackUrl}':
post:
responses:
'200':
description: Callback accepted
In OpenAPI 3.1, an API-initiated request can be documented directly:
webhooks:
orderUpdate:
post:
summary: Receive an order update
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/OrderUpdate'
responses:
'200':
description: Webhook accepted
Use a callback when the destination URL comes from a specific API operation. Use a top-level webhook when documenting an API-initiated request independently.
3. Model polymorphism with oneOf
OpenAPI 3.1 can combine JSON Schema composition with a discriminator:
components:
schemas:
Pet:
oneOf:
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Dog'
discriminator:
propertyName: petType
mapping:
cat: '#/components/schemas/Cat'
dog: '#/components/schemas/Dog'
A matching payload could look like:
{
"petType": "cat",
"name": "Fluffy"
}
Always test discriminator behavior with your target code generator. Implementations do not all handle polymorphism identically.
4. Use SPDX license identifiers
OpenAPI 3.1 allows an SPDX identifier in the Info Object:
info:
title: Petstore API
version: 1.0.0
license:
name: MIT
identifier: MIT
Use either identifier or url for the license, not both.
5. Reuse complete path items
OpenAPI 3.1 supports reusable Path Item Objects:
components:
pathItems:
PetsCollection:
get:
summary: List pets
responses:
'200':
description: Pet list
paths:
/pets:
$ref: '#/components/pathItems/PetsCollection'
This is useful when several paths expose the same operation structure.
Important 3.0-to-3.1 changes
nullable is removed
Replace:
type: string
nullable: true
With:
type: [string, "null"]
Exclusive bounds become numeric
OpenAPI 3.0 uses a boolean keyword:
type: number
minimum: 0
exclusiveMinimum: true
OpenAPI 3.1 uses the exclusive bound itself:
type: number
exclusiveMinimum: 0
Apply the same conversion to exclusiveMaximum.
Schema validation becomes stricter
Because OpenAPI 3.1 uses JSON Schema 2020-12 semantics, review:
-
exampleandexamples - Tuple and array validation
- Nullable fields
- Composition with
allOf,anyOf, andoneOf - Custom schema keywords
- Numeric boundary keywords
Do not upgrade only the openapi version string. Update the affected schemas and run them through a 3.1-compatible validator.
OpenAPI 3.2: Newer HTTP Patterns
An OpenAPI 3.2 document starts with:
openapi: 3.2.0
info:
title: Petstore API
version: 1.0.0
Before adopting 3.2, verify that your documentation renderer, validator, mock server, and code generator support it.
1. Describe the HTTP QUERY method
OpenAPI 3.2 adds support for the HTTP QUERY method, which is intended for safe, idempotent requests that need structured request content.
paths:
/pets/search:
query:
summary: Search pets with complex criteria
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
filters:
type: object
additionalProperties: true
sort:
type: array
items:
type: string
responses:
'200':
description: Search results
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
Use QUERY when:
- The request is logically read-only.
- Search criteria are too complex for practical query parameters.
- Your HTTP infrastructure supports the method.
- Your clients and code generators can send it correctly.
Do not switch existing GET endpoints automatically. Proxies, gateways, frameworks, and client libraries may not yet support QUERY.
2. Provide named examples
Named examples make generated documentation and test cases more useful:
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
examples:
availableCat:
summary: Available cat
description: A cat available for adoption
value:
id: "019b4132-70aa-764f-b315-e2803d882a24"
name: Fluffy
species: CAT
status: AVAILABLE
adoptedDog:
summary: Adopted dog
value:
id: "019b4127-54d5-76d9-b626-0d4c7bfce5b6"
name: Buddy
species: DOG
status: ADOPTED
Include examples for:
- Successful requests
- Successful responses
- Validation failures
- Authentication failures
- Boundary values
- Each polymorphic schema variant
Named Example Objects are not exclusive to OpenAPI 3.2, but they remain an important part of a useful 3.2 specification.
3. Fully document OAuth flows
A complete OAuth authorization code flow can include authorization, token, and refresh URLs:
components:
securitySchemes:
oauth2:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://petstoreapi.com/oauth/authorize
tokenUrl: https://petstoreapi.com/oauth/token
refreshUrl: https://petstoreapi.com/oauth/refresh
scopes:
pets:read: Read pets
pets:write: Create and update pets
orders:read: Read orders
Apply scopes at the document or operation level:
security:
- oauth2:
- pets:read
For an operation requiring write access:
paths:
/pets:
post:
security:
- oauth2:
- pets:write
refreshUrl is not itself new to OpenAPI 3.2, so it should not be the sole reason to upgrade. Add it when it accurately describes your OAuth implementation.
4. Make discriminator mappings explicit
For portable polymorphism, use explicit component references:
discriminator:
propertyName: type
mapping:
cat: '#/components/schemas/Cat'
dog: '#/components/schemas/Dog'
bird: '#/components/schemas/Bird'
Explicit $ref targets are generally easier for tools to resolve than relying on implicit schema-name matching.
5. Deprecate individual fields
Mark deprecated schema properties and tell clients what to use instead:
properties:
oldField:
type: string
deprecated: true
description: Use newField instead.
newField:
type: string
Deprecation metadata was available before OpenAPI 3.2, but it remains useful when maintaining a modern specification.
How Modern PetstoreAPI Uses OpenAPI 3.2
Modern PetstoreAPI demonstrates OpenAPI 3.2 features in a complete specification.
1. Inspect the full specification
The OpenAPI document is available at:
https://petstoreapi.com/openapi.json
Download it locally for validation:
curl -o openapi.json https://petstoreapi.com/openapi.json
Check the declared version:
jq '.openapi' openapi.json
2. Inspect the QUERY operation
The search operation follows this structure:
/pets/search:
query:
summary: Search pets with complex criteria
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PetSearchQuery'
When testing this operation, confirm that your HTTP client allows a request body with QUERY.
3. Inspect webhook definitions
Webhook operations are defined separately from normal API paths:
webhooks:
petStatusChanged:
post:
summary: Pet status changed
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PetStatusWebhook'
Use this definition to generate a mock webhook receiver or validate webhook payloads.
4. Inspect polymorphic pet schemas
The base Pet schema supports multiple concrete variants:
Pet:
oneOf:
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Dog'
- $ref: '#/components/schemas/Bird'
discriminator:
propertyName: species
Test at least one valid payload for each variant and one invalid payload with an unknown species value.
5. Use examples as test fixtures
The specification includes examples for different endpoint scenarios. Extract these examples and use them as:
- Documentation samples
- Mock server responses
- Contract test fixtures
- Regression test inputs
Examples should still be validated against their associated schemas. An example is useful only if it remains synchronized with the contract.
6. Return RFC 9457 problem details
Error responses can use the application/problem+json media type:
responses:
'400':
description: Bad Request
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
A representative response might look like:
{
"type": "https://petstoreapi.com/problems/invalid-request",
"title": "Invalid request",
"status": 400,
"detail": "The species field is required",
"instance": "/pets"
}
See the complete OpenAPI specification for all definitions and examples.
Migration Guide
Treat an OpenAPI upgrade as a compatibility project rather than a version-number change.
A safe workflow is:
- Create a migration branch.
- Update the
openapiversion. - Convert incompatible schema syntax.
- Run structural validation.
- Validate all examples.
- Regenerate clients and server stubs.
- Review generated-code changes.
- Run contract and integration tests.
- Confirm documentation and gateway compatibility.
Migrate from OpenAPI 3.0 to 3.1
Step 1: Replace nullable
Before:
type: string
nullable: true
After:
type: [string, "null"]
Search for every nullable field:
grep -R "nullable:" .
Do not forget nullable array items or properties inside referenced components.
Step 2: Convert exclusive numeric bounds
Before:
type: number
minimum: 0
exclusiveMinimum: true
After:
type: number
exclusiveMinimum: 0
Before:
type: number
maximum: 100
exclusiveMaximum: true
After:
type: number
exclusiveMaximum: 100
Step 3: Decide whether callbacks should become webhooks
Keep callbacks when the callback is tied to an operation and its destination is derived from that operation's request or response.
Use top-level webhooks for independently documented API-initiated requests:
webhooks:
orderUpdated:
post:
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/OrderUpdate'
responses:
'204':
description: Update accepted
Step 4: Validate JSON Schema behavior
Pay special attention to:
oneOf:
anyOf:
allOf:
not:
if:
then:
else:
dependentRequired:
unevaluatedProperties:
A schema that was accepted by an OpenAPI 3.0 tool may produce different results under JSON Schema 2020-12 semantics.
Step 5: Test downstream tools
Before merging, verify:
- Documentation renders correctly.
- Mock servers start successfully.
- Client generation completes.
- Server stub generation completes.
- API gateways accept the document.
- Contract tests still pass.
Migrate from OpenAPI 3.1 to 3.2
Step 1: Update the version
openapi: 3.2.0
Run your existing validation pipeline immediately. This reveals tools that reject 3.2 before you add any 3.2-specific features.
Step 2: Add QUERY only where appropriate
/pets/search:
query:
summary: Search pets
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PetSearchQuery'
Test the operation through every relevant layer:
- Client library
- Browser or frontend runtime
- Reverse proxy
- CDN
- API gateway
- Application framework
- Monitoring and logging stack
If any required component rejects QUERY, keep the existing GET or POST design until the infrastructure is ready.
Step 3: Improve examples
Add descriptive names and realistic values:
examples:
availablePet:
summary: Pet available for adoption
value:
name: Fluffy
species: CAT
status: AVAILABLE
adoptedPet:
summary: Pet already adopted
value:
name: Buddy
species: DOG
status: ADOPTED
Validate examples in CI so they do not drift from their schemas.
Step 4: Review security definitions
Check that OAuth definitions match the real authorization server:
authorizationUrl: https://petstoreapi.com/oauth/authorize
tokenUrl: https://petstoreapi.com/oauth/token
refreshUrl: https://petstoreapi.com/oauth/refresh
Also verify operation-level scopes. A valid security scheme can still be incorrect if operations request the wrong permissions.
Step 5: Check generator support
Generate a test client before adopting 3.2 in production. Confirm that the generator:
- Recognizes the document version
- Handles
QUERY - Generates request bodies correctly
- Preserves security scopes
- Resolves discriminator mappings
- Does not silently ignore unsupported fields
Testing OpenAPI Specifications with Apidog
Apidog can import OpenAPI 3.x specifications and help validate the resulting API contract.
Import the specification
- Open Apidog.
- Select Import.
- Choose OpenAPI 3.x.
- Paste the specification URL or upload the JSON or YAML file.
- Review the validation results.
- Confirm that endpoints, schemas, examples, and security schemes were imported correctly.
For Modern PetstoreAPI, use:
https://petstoreapi.com/openapi.json
Validate the contract
Review the imported specification for:
- Invalid schema keywords
- Missing or unresolved
$refvalues - Examples that do not match schemas
- Incomplete security definitions
- Missing required path parameters
- Invalid response definitions
- Duplicate or missing operation IDs
Test the implementation
Generate or configure test cases for:
- Required request fields
- Request schema validation
- Response schema validation
- Expected status codes
- Authentication and authorization
- Invalid enum values
- Polymorphic payloads
- Problem-detail error responses
For example, test a valid pet payload:
{
"name": "Fluffy",
"species": "CAT",
"status": "AVAILABLE"
}
Then test an invalid payload:
{
"name": "Fluffy",
"species": "UNKNOWN",
"status": "AVAILABLE"
}
The second request should fail if species is restricted to known values.
Compare versions before deployment
Import the current and migrated specifications, then review:
- Removed endpoints
- New endpoints
- Changed required properties
- Type changes
- Enum changes
- New authentication requirements
- Deprecated operations or fields
- Changed request and response schemas
Treat these changes as potentially breaking until client compatibility has been verified.
Which Version Should You Use?
Use OpenAPI 3.0 when:
- Your toolchain does not fully support 3.1.
- You depend on older code generators or API gateways.
- You do not need JSON Schema 2020-12 features.
Use OpenAPI 3.1 when:
- You want JSON Schema 2020-12 alignment.
- You need top-level webhooks.
- You want to share schemas with other JSON Schema tools.
- Your validators and generators support it.
Use OpenAPI 3.2 when:
- You need features such as the
QUERYmethod. - Your complete toolchain supports 3.2.
- You have tested generated clients, gateways, mocks, and documentation against the specification.
For many teams, OpenAPI 3.1 is the practical migration target while OpenAPI 3.2 support continues to expand across tools.
Conclusion
OpenAPI has evolved in three important stages:
- OpenAPI 3.0: Introduced servers, request bodies, components, callbacks, and links.
- OpenAPI 3.1: Aligned schemas with JSON Schema 2020-12 and added top-level webhooks.
-
OpenAPI 3.2: Added support for newer HTTP patterns such as
QUERY.
Modern PetstoreAPI demonstrates these features through an OpenAPI 3.2 specification. Use it to inspect syntax, test tool compatibility, and compare implementation approaches.
When migrating, update schema syntax first, validate every reference and example, and test all downstream tooling before deploying the new specification.
FAQ
Should I upgrade to OpenAPI 3.1 or 3.2?
Upgrade to OpenAPI 3.1 if you need JSON Schema 2020-12 alignment and your tools support it. Upgrade to 3.2 when you need 3.2-specific features such as QUERY and have verified your entire toolchain.
Will an OpenAPI 3.0 specification work with 3.1 tools?
Many 3.1 tools can read 3.0 specifications, but changing the document version to 3.1 requires schema updates. In particular, convert nullable, exclusiveMinimum, and exclusiveMaximum.
Do code generators support OpenAPI 3.2?
Support varies. Check your generator's documentation and run a generation test. A tool may accept the document while ignoring unsupported 3.2 features.
Can I use OpenAPI 3.2 features in a 3.1 document?
No. The declared OpenAPI version must match the features used by the document. If you define a QUERY operation, declare OpenAPI 3.2.
How do I validate an OpenAPI specification?
Import it into Apidog and review schema validity, references, examples, and security definitions. Also validate it in CI and test all generated artifacts before merging changes.
Where can I see a complete OpenAPI 3.2 example?
Modern PetstoreAPI provides its specification at:
https://petstoreapi.com/openapi.json
What is the difference between webhooks and callbacks?
A callback is attached to an API operation and often derives its destination URL from that operation. A top-level webhook documents an API-initiated request independently of a specific path operation.
Should I use JSON or YAML?
Both formats represent the same OpenAPI document.
Use YAML when human readability and manual editing are the priority. Use JSON when your workflow depends on JSON-native tooling or generated specifications. Modern PetstoreAPI provides a JSON specification that can be downloaded and inspected directly.
Top comments (0)