How to Structure the OpenAPI Specification for Complex Plugins: A Complete Guide
Structure complex OpenAPI specifications for plugins by splitting them into modular files (paths, components, security schemes) referenced via $ref, and include explicit versioning, pagination parameters, and security definitions to ensure maintainability and compatibility with the OpenAI plugin host.
Creating a well-structured OpenAPI specification is critical when building complex plugins that expose multiple endpoints, handle authentication, and require long-term maintainability. This guide examines proven patterns from the OpenAI Plugins repository, specifically analyzing how the Zoom and Cloudflare implementations organize their specifications. You will learn to architect modular specs that scale from simple endpoints to enterprise-grade APIs.
Why Modular Architecture Matters for Complex Plugins
Monolithic OpenAPI files become unmanageable as your plugin grows. The modular definition principle keeps specifications readable and allows different teams to own distinct API domains simultaneously.
According to the repository's reference implementations, splitting specs into multiple files and referencing them via $ref prevents merge conflicts and enables parallel development. This approach also supports versioning and backwards compatibility, ensuring that changes to one domain (like meetings) do not accidentally break another (like user management).
Recommended File Layout for Scalable OpenAPI Specs
Organize your specification using a directory structure that separates concerns by functional domain. The following layout is derived from best practices observed across the OpenAI plugins ecosystem:
openapi/
├── openapi.yaml # Root document (info, servers, security)
├── paths/
│ ├── users.yaml # /users/* endpoints
│ └── meetings.yaml # /meetings/* endpoints
├── components/
│ ├── schemas/
│ │ ├── User.yaml
│ │ └── Meeting.yaml
│ ├── parameters/
│ │ └── Pagination.yaml
│ └── securitySchemes/
│ └── OAuth2.yaml
└── external/
└── examples/ # Sample request/response payloads
Root Document Structure
The root openapi.yaml acts as the entry point, pulling together all sub-resources using $ref statements. Define global security schemes here to apply authentication consistently across operations.
openapi: 3.0.0
info:
title: Complex Plugin API
version: 1.2.0
description: |
A full-featured OpenAPI spec for a multi-service plugin.
servers:
- url: https://api.example.com/v1
description: Production server
security:
- OAuth2: [] # Applied globally; override per-operation if needed
paths:
/users:
$ref: './paths/users.yaml#/paths/~1users'
/meetings:
$ref: './paths/meetings.yaml#/paths/~1meetings'
components:
schemas:
$ref: './components/schemas/User.yaml#/User'
securitySchemes:
$ref: './components/securitySchemes/OAuth2.yaml'
Domain-Specific Path Files
Isolate each logical domain (users, meetings, files) into separate files under paths/. This separation allows independent versioning and ownership. Use kebab-case for path segments (/users/{user-id}), camelCase for query parameters, and snake_case for payload fields to maintain consistency across the specification.
Essential Components for Production-Ready Specs
Reusable Security Schemes
Explicitly define authentication methods in components/securitySchemes/ to simplify token handling for the plugin host. The specification supports OAuth2, API keys, and Bearer tokens.
In components/securitySchemes/OAuth2.yaml:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://auth.example.com/oauth/authorize
tokenUrl: https://auth.example.com/oauth/token
scopes:
read: Grant read access
write: Grant write access
Standardized Pagination
Implement pagination as a reusable parameter component to ensure consistent behavior across list endpoints.
In components/parameters/Pagination.yaml:
name: page
in: query
description: Page number for paginated results
required: false
schema:
type: integer
default: 1
minimum: 1
Reference this in your path files:
get:
summary: List users
parameters:
- $ref: '../components/parameters/Pagination.yaml'
responses:
200:
description: A paginated list of users
Rich Documentation and Examples
Add description, example, and summary fields for every operation and schema. This enables auto-generated client SDKs and improves the plugin's discoverability in API explorers.
Real-World Implementations in OpenAI Plugins
Zoom Plugin: Legacy Swagger 2.0 and SDK Generation
The Zoom skill in plugins/zoom/skills/rest-api/references/openapi.md demonstrates how to handle legacy specifications. It ships with deprecated Swagger 2.0 specs (openapi.v2.json) and uses OpenAPI Generator to create TypeScript and Python clients.
The reference file documents known issues such as enum mismatches and missing required fields, providing workarounds for generating functional SDKs from imperfect specs.
Generate a TypeScript client using the Zoom pattern:
# Download the spec
curl -o zoom-api-v2.json \
https://raw.githubusercontent.com/zoom/api/442998230a148f403c3d1de1fe7aa54937354fa9/openapi.v2.json
# Generate client via OpenAPI Generator
npm install @openapitools/openapi-generator-cli -g
openapi-generator-cli generate \
-i zoom-api-v2.json \
-g typescript-fetch \
-o ./zoom-client
Cloudflare Workers: Hono and Zod Integration
For modern implementations, the Cloudflare skill in plugins/cloudflare/skills/cloudflare/references/workers/frameworks.md embeds OpenAPI definitions directly in code using the Hono framework with @hono/zod-openapi.
This approach provides type-safe validation and automatic OpenAPI generation from Zod schemas:
import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi';
const app = new OpenAPIHono();
const route = createRoute({
method: 'get',
path: '/users/{id}',
request: {
params: z.object({ id: z.string() })
},
responses: {
200: {
description: 'User found',
content: {
'application/json': {
schema: z.object({
id: z.string(),
name: z.string(),
email: z.string().email()
})
}
}
}
},
});
app.openapi(route, (c) => {
const { id } = c.req.valid('param');
return c.json({ id, name: 'Jane Doe', email: 'jane@example.com' });
});
// Expose the generated spec
app.doc('/openapi.json', {
openapi: '3.0.0',
info: { version: '1.0.0', title: 'API' }
});
Step-by-Step Implementation Guide
Follow this workflow to structure your complex plugin specification:
- Analyze logical domains (users, meetings, files) and create separate path files for each.
- Define reusable schemas in
components/schemas/with completeexamplevalues. - Configure global security in
components/securitySchemes/and reference it in the root document. - Standardize pagination by creating shared parameter components.
- Validate the merged spec using Swagger Editor or
openapi-validatorbefore deployment. - Publish the specification at a public URL (e.g.,
https://api.example.com/openapi.json) as demonstrated inplugins/replayio/skills/replay-qa-api/SKILL.md. - Generate client SDKs using OpenAPI Generator to verify specification completeness.
Summary
- Modular file structure separates paths, components, and security schemes into reusable files referenced via
$ref. - Consistent naming conventions (kebab-case paths, camelCase parameters, snake_case payloads) reduce integration errors.
- Explicit security definitions in
components.securitySchemesenable proper authentication handling by the OpenAI plugin host. - Pagination parameters should be defined as reusable components to ensure consistent list behavior.
- Real-world examples in
plugins/zoom/andplugins/cloudflare/demonstrate both legacy (Swagger 2.0) and modern (Hono+Zod) approaches.
Frequently Asked Questions
How do I handle breaking changes in my OpenAPI specification?
Update the info.version field in your root openapi.yaml file following semantic versioning (semver) principles. Avoid removing or renaming existing paths, parameters, or response fields. Instead, deprecate old endpoints using the deprecated: true flag and introduce new versions alongside existing ones.
Can I split my OpenAPI spec across multiple files in the OpenAI plugins repository?
Yes, the modular approach using $ref pointers is fully supported. The root document should reference external files for paths, schemas, and security schemes. This pattern is documented in plugins/zoom/skills/rest-api/references/full-guide.md and plugins/cloudflare/skills/cloudflare/references/c3/api.md.
What authentication schemes does the OpenAI plugin host support?
The host supports OAuth2 (authorization code flow), API keys, and Bearer tokens. Define these in components/securitySchemes and reference them globally or per-operation. The Zoom plugin example shows OAuth2 implementation, while the Cloudflare references demonstrate API key patterns.
How do I validate my OpenAPI specification before deployment?
Use the Swagger Editor online tool or command-line validators like swagger-codegen validate or @apidevtools/swagger-parser. Validate both individual component files and the fully-resolved merged specification to catch $ref errors and schema mismatches before the plugin attempts to load your API definition.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →