Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

OpenAPI 3.1 Specification Complete Guide

By Kokil Thapa | Last reviewed: September 2026

An undocumented REST endpoint is a liability waiting to happen. The OpenAPI 3.1 Specification Complete Guide you need starts with a single machine-readable contract that describes paths, schemas, security, and errors before a single controller ships. On production Laravel and Symfony projects I maintain, that contract is what keeps mobile clients, payment webhooks, and third-party integrators aligned when the API evolves. This guide walks through OpenAPI 3.1 from first openapi: 3.1.0 line to validated docs your team can trust — whether you build in Kathmandu or ship globally. For hands-on delivery, see our API development services in Nepal.

What is the OpenAPI 3.1 Specification and how does it differ from 3.0?

OpenAPI 3.1 is the current major line of the OpenAPI Specification (OAS). It was ratified by the OpenAPI Initiative and fully aligns schema definitions with JSON Schema draft 2020-12. That alignment removes years of friction where Swagger tooling and standard JSON Schema validators disagreed on the same field.

The specification itself lives at spec.openapis.org/oas/v3.1.0. Read that document when you need authoritative wording on edge cases. For day-to-day work, most teams author YAML and render it with Swagger UI, Redoc, or Stoplight — a workflow covered in our Redoc and Swagger UI documentation guide.

OpenAPI 3.1 Document Structureopenapi: 3.1.0Required version fieldinfoTitle, versionpathsOperationscomponentsReusable partswebhooksCallbackscomponents.schemasJSON Schema 2020-12type, properties, requirednullable via type arrayscomponents.securitySchemesBearer, API key, OAuth2Applied per operationShared across paths
OpenAPI 3.1 Specification Complete Guide — top-level document sections and where reusable schemas live

Breaking changes from OpenAPI 3.0

Three differences matter in daily work. First, schemas use JSON Schema 2020-12 syntax — not the OpenAPI-flavoured subset from 3.0. Second, nullable fields use a type array like ["string", "null"] instead of a separate nullable: true flag. Third, the top-level webhooks object is first-class, which suits event-driven APIs and payment callback designs I use on eSewa and Khalti integrations.

FeatureOpenAPI 3.0OpenAPI 3.1
Schema dialectCustom subsetJSON Schema 2020-12
Nullable fieldsnullable: truetype: ["string", "null"]
WebhooksVia callbacks onlyTop-level webhooks key
JSON Schema $schemaNot supportedAllowed in schemas
Path templatingSameSame RFC 6570 rules

Most tooling now accepts 3.1. Swagger UI 5.x, Redoc 2.x, and Spectral lint both versions. If your CI pipeline still pins Swagger Parser 9.x, upgrade before migrating production specs. Our testing and optimization services often start with a spec audit when client APIs drift from documented behaviour.

How do you structure an OpenAPI 3.1 document from scratch?

Start contract-first. Define the API surface before you write route handlers. On Laravel 13 projects with PHP 8.3 or higher, I keep the spec in docs/openapi.yaml and treat it as the review artefact in pull requests — the same discipline we apply on booking portals like Adventure Third Pole Trek.

Minimal valid skeleton

openapi: 3.1.0
info:
  title: Booking API
  version: 1.0.0
  description: Trek booking endpoints for Nepal operators.
servers:
  - url: https://api.example.com/v1
    description: Production
paths:
  /bookings:
    get:
      operationId: listBookings
      summary: List bookings
      responses:
        '200':
          description: Paginated booking list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BookingList'
components:
  schemas:
    BookingList:
      type: object
      required: [data, meta]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Booking'
        meta:
          $ref: '#/components/schemas/PaginationMeta'
    Booking:
      type: object
      required: [id, status]
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
          enum: [pending, confirmed, cancelled]
    PaginationMeta:
      type: object
      properties:
        page:
          type: integer
          minimum: 1
        per_page:
          type: integer
          maximum: 100

Paste that YAML into the JSON formatter tool after converting to JSON if you need to debug parser errors. YAML indentation mistakes are the most common first-day failure.

Design rules that save pain later

  1. Give every operation a unique operationId — code generators depend on it.
  2. Put shared models under components.schemas, not inline in each path.
  3. Document all error responses (400, 401, 422, 429, 500) — not just 200.
  4. Version via URL prefix (/v1) or header — pick one and document it in servers.
  5. Keep one spec per bounded context; split monoliths into multiple files with $ref.

For pagination and rate-limit headers, cross-read our API rate limiting guide. Document X-RateLimit-Remaining in response headers so client teams know what to expect.

Contract-First WorkflowWrite Specopenapi.yamlLint + ReviewSpectral rulesImplementLaravel routesValidateDredd / SchemathesisCommon Failure: Spec DriftController returns extra fields not in schemaMissing 422 validation response documentedAuth header name differs from securitySchemesFix: CI fails if spec and tests disagree
Contract-first OpenAPI 3.1 workflow — spec lint, implementation, validation, and the drift trap to avoid

How do you define schemas and request bodies in OpenAPI 3.1?

Schemas are the heart of any OpenAPI 3.1 Specification Complete Guide section on data modelling. Because 3.1 uses JSON Schema 2020-12, you can use keywords like allOf, oneOf, const, and pattern without vendor quirks.

Nullable and optional fields

In 3.0 you wrote nullable: true. In 3.1, express nullability inside the type array:

properties:
  middle_name:
    type: ["string", "null"]
    maxLength: 100
  cancelled_at:
    type: ["string", "null"]
    format: date-time

Optional but non-null fields omit the field from required — same as before. Do not confuse optional with nullable; a missing key and an explicit null value are different contracts.

Request bodies and content types

paths:
  /bookings:
    post:
      operationId: createBooking
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateBookingRequest'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Booking'
        '422':
          description: Validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationError'

Mirror your server-side Form Request rules in Laravel. If the spec says maxLength: 255 on email, the PHP validation must match. Mismatch is how mobile apps get surprise 422 responses in production.

Webhooks in 3.1

Payment gateways send server-to-server callbacks. Document them under the top-level webhooks key:

webhooks:
  paymentConfirmed:
    post:
      operationId: onPaymentConfirmed
      summary: Gateway notifies successful payment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PaymentWebhookPayload'
      responses:
        '200':
          description: Acknowledged

This pattern appears on every eCommerce project where Khalti or Stripe posts back to a Laravel route. The webhook spec is the integration contract your payment partner never reads unless you send it.

How do you document authentication and security in OpenAPI 3.1?

Security belongs in components.securitySchemes and attaches to operations via a security array. Laravel Sanctum bearer tokens and API keys both map cleanly. For a deep comparison of token strategies, read our Passport vs Sanctum authentication guide.

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
    apiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key

paths:
  /bookings:
    get:
      security:
        - bearerAuth: []
      responses:
        '401':
          description: Unauthenticated

Document OAuth2 flows only when you actually implement them. An authorizationCode block in the spec without a working /oauth/authorize endpoint misleads integrators and wastes support time.

OpenAPI 3.0 vs 3.1OpenAPI 3.0Custom schema subsetnullable: true flagCallbacks for webhooksWide tooling supportLegacy new projectsOpenAPI 3.1JSON Schema 2020-12type array for nullTop-level webhooksSpectral + Swagger UI 5Default for 2026 APIsupgrade
OpenAPI 3.1 Specification migration — schema dialect, nullable syntax, and webhook modelling compared to 3.0

How do you validate, lint, and test an OpenAPI 3.1 specification?

A spec nobody validates becomes fiction within two sprints. Treat linting and contract tests as CI gates — the same mindset as API contract testing with Pact, but driven from your YAML file.

Lint with Spectral

Install @stoplight/spectral-cli and add a .spectral.yaml ruleset:

extends: ["spectral:oas", "spectral:recommended"]
rules:
  operation-operationId: error
  operation-description: warn
  oas3-valid-media-example: error

Run spectral lint docs/openapi.yaml in GitLab CI before merge. Block deploys on error-level rules.

Validate responses against the spec

  • Dredd — HTTP-level tests from OpenAPI examples; good for smoke suites.
  • Schemathesis — property-based fuzzing from schemas; catches edge cases.
  • openapi-enforcer — Python middleware validation for microservices.
  • Laravel response assertions — custom test helper comparing JSON to schema.

On client portals like Mijar Law Associates, document upload endpoints need strict MIME type and size limits in the spec. Fuzz testing catches boundary violations before a lawyer uploads a 40 MB scan on mobile data.

Use the regex tester when defining pattern constraints on Nepali phone numbers or PAN strings. Bad regex in a schema rejects valid input silently at the client SDK layer.

How do you publish and maintain OpenAPI 3.1 documentation?

Generated docs are only as current as your deploy pipeline. Wire spec publication into the same GitLab CI job that ships PHP code on Deployer releases.

Render interactive docs

Swagger UI and Redoc both accept 3.1 when bundled with a current parser. Host static HTML on /api/docs behind auth in staging; public read-only docs for partner APIs on a subdomain like developers.example.com.

Deprecation and versioning

Mark sunset endpoints with deprecated: true on the operation. Add a description pointing to the replacement path and link to our API deprecation best practices for header-based sunset dates.

paths:
  /v1/bookings:
    get:
      deprecated: true
      description: |
        Sunset: 2026-12-31. Use GET /v2/bookings instead.
      responses:
        '200':
          description: Legacy paginated list

Split large specs using bundlers. @redocly/cli bundle docs/openapi.yaml -o public/api/openapi.json resolves all $ref pointers into one artefact for CDN hosting.

CI Pipeline for OpenAPI 3.1Git PushSpectral LintDredd TestsBundle SpecDeployPublished OutputsSwagger UI/api/docsRedoc HTMLPartner portalClient SDKopenapi-generator
OpenAPI 3.1 CI pipeline — lint, contract test, bundle, and publish interactive docs plus SDK artefacts

Laravel integration pattern

Laravel 13 does not ship OpenAPI generation natively. Common approaches: hand-author YAML (my preference for public APIs), use darkaonline/l5-swagger for annotation-driven specs, or export from scribe and refine. Annotations drift; contract-first YAML forces design conversations early.

For AI-assisted endpoints, document prompt and response schemas explicitly. Our Claude API developer guide shows why structured output belongs in your spec when LLM responses feed downstream code.

Enterprise teams needing governance across multiple services should read about enterprise application development patterns — one org-wide Spectral ruleset beats per-project conventions.

Key Takeaways

  • Set openapi: 3.1.0 and use JSON Schema 2020-12 syntax for every components.schemas entry.
  • Replace nullable: true with type arrays like ["string", "null"] when migrating from 3.0.
  • Assign unique operationId values and document 4xx/5xx responses on every path.
  • Run Spectral lint and response validation in CI so the spec cannot drift from production.
  • Document webhooks and security schemes explicitly — integrators depend on them.
  • Publish bundled specs to Swagger UI or Redoc on every deploy, not as a one-time PDF.

People Also Ask

Is OpenAPI 3.1 backward compatible with 3.0?

Most 3.0 documents upgrade cleanly with mechanical changes to nullable syntax and schema keywords. Tools that only parse 3.0 will reject a 3.1 file outright. Test your parser version before switching the openapi field.

Can OpenAPI 3.1 schemas use full JSON Schema?

Yes, with the constraints noted in the official spec. OpenAPI adds its own keywords like discriminator and ties schemas to HTTP contexts. Pure JSON Schema tooling may not understand those extensions.

What is the best tool to edit OpenAPI 3.1 specs?

VS Code with the OpenAPI (Swagger) Editor extension handles YAML autocomplete well. Stoplight Studio and Insomnia offer visual editing for teams that prefer GUI workflows. Plain Git-tracked YAML remains the most diff-friendly option.

How does OpenAPI relate to Swagger?

Swagger was the original name. The specification was donated to the OpenAPI Initiative and renamed. Swagger UI and Swagger Editor are tools; OpenAPI is the specification format they consume. See the overview at swagger.io/specification.

Ship APIs with a spec your team can trust

You now have a working OpenAPI 3.1 Specification Complete Guide playbook: structure, schemas, auth, webhooks, validation, and publication. The teams that win treat the YAML file as source code — reviewed, linted, tested, and deployed alongside PHP 8.3+ Laravel routes. Whether you are launching a legal-tech portal, a booking API, or a payment integration for Nepal and global markets, start with the contract. Need help authoring or migrating your spec? Contact us for API design and documentation support, or browse the portfolio for production API work. Continue with essential Laravel packages, custom software development, and ongoing API maintenance when your spec outgrows a single file.

Frequently Asked Questions

OpenAPI 3.1 is a machine-readable YAML or JSON contract describing API paths, schemas, security, and errors, aligned with JSON Schema 2020-12.

Three differences matter in daily work. Schemas now use JSON Schema 2020-12 syntax instead of the OpenAPI-flavoured subset from 3.0. Nullable fields use a type array like string and null together rather than a separate nullable flag. The top-level webhooks object is first-class, which suits event-driven APIs and payment callback designs. Path templating follows the same RFC 6570 rules as 3.0. Most tooling now accepts 3.1, including Swagger UI 5.x, Redoc 2.x, and Spectral, but if your CI pipeline still pins Swagger Parser 9.x, upgrade before migrating production specs.

Mostly yes with mechanical nullable and schema changes. Tools parsing only 3.0 reject 3.1 files outright, so test your parser version before switching.

Yes, with OpenAPI-specific constraints. Pure JSON Schema tooling may not understand extensions like discriminator that tie schemas to HTTP contexts.

Start contract-first and define the API surface before writing route handlers. A minimal valid document sets openapi to 3.1.0, adds info with title and version, lists servers, defines paths with operations and responses, and puts reusable models under components.schemas. On Laravel 13 projects with PHP 8.3 or higher, keeping the spec in docs/openapi.yaml and treating it as a pull-request review artefact enforces the same discipline used on booking portals. Give every operation a unique operationId, document all error responses including 400, 401, 422, 429, and 500, and version via URL prefix or header consistently documented in servers.

In 3.0 you wrote nullable true on a field. In 3.1, express nullability inside the type array, for example a middle name field typed as string and null with a maxLength constraint. Optional but non-null fields omit the field from the required array, same as before. Do not confuse optional with nullable because a missing key and an explicit null value are different contracts. Because 3.1 uses JSON Schema 2020-12, you can also use keywords like allOf, oneOf, const, and pattern without vendor quirks that plagued 3.0 tooling.

Payment gateways and similar services send server-to-server callbacks that belong in the spec, not as an afterthought. In 3.1, document them under the top-level webhooks key with a named entry, HTTP method, operationId, summary, requestBody schema reference, and expected response such as 200 acknowledged. This pattern appears on eCommerce projects where Khalti or Stripe posts back to a Laravel route. In 3.0, webhooks were modelled only via callbacks. The webhook spec is the integration contract your payment partner rarely reads unless you send it, so treat it with the same rigour as public REST paths.

Security belongs in components.securitySchemes and attaches to individual operations via a security array. Laravel Sanctum bearer tokens map cleanly as type http with scheme bearer and bearerFormat JWT. API keys map as type apiKey in header with a name like X-API-Key. Document a 401 unauthenticated response on protected paths. Document OAuth2 flows only when you actually implement them. An authorizationCode block in the spec without a working authorize endpoint misleads integrators and wastes support time. Match the spec to real middleware and guard configuration so mobile clients and third-party integrators know exactly which credentials each endpoint expects.

A spec nobody validates becomes fiction within two sprints. Install @stoplight/spectral-cli and add a .spectral.yaml ruleset extending spectral:oas and spectral:recommended, then run spectral lint docs/openapi.yaml in GitLab CI before merge and block deploys on error-level rules. For response validation, Dredd runs HTTP-level tests from OpenAPI examples, Schemathesis does property-based fuzzing from schemas, and openapi-enforcer provides Python middleware validation. On client portals with document upload endpoints, fuzz testing catches boundary violations before users hit MIME or size limits. Use a regex tester when defining pattern constraints on Nepali phone numbers or PAN strings because bad regex in a schema rejects valid input silently at the client SDK layer.

VS Code with the OpenAPI Swagger Editor extension handles YAML autocomplete well for developers who live in the editor. Stoplight Studio and Insomnia offer visual editing for teams that prefer GUI workflows over raw YAML. Plain Git-tracked YAML remains the most diff-friendly option and fits contract-first review in pull requests. Pick based on team workflow, not features alone. Visual editors help onboarding; Git-tracked YAML wins when you need clean diffs, Spectral lint gates, and the same file bundled and published on every Deployer release without manual export steps.

Swagger was the original name for the specification format. The specification was donated to the OpenAPI Initiative and renamed OpenAPI. Swagger UI and Swagger Editor are tools that consume the OpenAPI specification format; OpenAPI itself is the standard they render. When someone says they need Swagger docs, they usually mean an OpenAPI YAML or JSON file displayed in Swagger UI or Redoc. Keeping that distinction clear prevents confusion during migrations and tool selection. The authoritative overview lives at swagger.io/specification, while the normative 3.1 text is at spec.openapis.org/oas/v3.1.0.

Laravel 13 does not ship OpenAPI generation natively. Common approaches are hand-authoring YAML, which forces design conversations early and avoids drift, using darkaonline/l5-swagger for annotation-driven specs, or exporting from scribe and refining the output. Annotations drift from production behaviour over time; contract-first YAML kept in docs/openapi.yaml is the preference for public APIs. Mirror server-side Form Request validation rules in the spec so maxLength and enum constraints match PHP validation exactly. Mismatch is how mobile apps get surprise 422 responses in production. For AI-assisted endpoints, document prompt and response schemas explicitly when LLM output feeds downstream code.

Every operation needs a unique operationId because code generators depend on it to name client methods, server stubs, and test helpers consistently. Without unique IDs, generated SDKs produce collisions or ambiguous function names that break builds. The same operationId appears in webhook definitions and standard path operations, so name them descriptively like listBookings or onPaymentConfirmed rather than generic labels. Spectral can enforce this with an operation-operationId rule set to error level in your CI ruleset. Treat operationId assignment as part of API design review, not a post-hoc labelling step after routes already exist in Laravel or Symfony.

Generated docs are only as current as your deploy pipeline. Wire spec publication into the same GitLab CI job that ships PHP code on Deployer releases. Swagger UI and Redoc both accept 3.1 when bundled with a current parser. Host static HTML on /api/docs behind auth in staging and public read-only docs for partner APIs on a subdomain like developers.example.com. Mark sunset endpoints with deprecated true and a description pointing to the replacement path. Split large specs using bundlers: @redocly/cli bundle docs/openapi.yaml resolves all reference pointers into one artefact for CDN hosting. Publish bundled specs on every deploy, not as a one-time PDF.

Contract-first means defining the API surface in an OpenAPI 3.1 YAML file before writing route handlers or controllers. The spec becomes the review artefact in pull requests, reviewed, linted, tested, and deployed alongside PHP routes rather than written after the fact. This workflow catches schema mismatches, missing error responses, and undocumented webhooks before mobile clients or payment integrators depend on them. The drift trap to avoid is implementing endpoints first and backfilling documentation later, which guarantees the spec and production behaviour diverge within two sprints. Teams that win treat the YAML file as source code with Spectral lint and response validation as CI gates.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: