OpenAPI design
Help Agents model resources, unify error bodies and cursor pagination, and produce validatable OpenAPI 3.x drafts for review and codegen; this page follows flow → metadata → paths → schemas → errors → versioning → lint → skeleton lab.
A SKILL should fix info.version, servers, and securitySchemes; use plural resources and a max nesting depth, and reuse components for query params and request bodies.
Error responses use Problem Details (RFC 7807) or a team envelope, with typical 4xx/5xx documented; success responses include request/response examples and discriminator notes.
Describe breaking changes and version coexistence via path or header; after generating YAML, run Spectral (or similar) in CI—Agents should output a fix list, not silently ignore violations.
- Declare required, nullable, and defaults explicitly in schema.
- Prefer cursor pagination; document offset risks on large datasets.
- Put webhooks and callbacks in dedicated tags with signature verification placeholders.
$ref and components reuse examples
# OpenAPI 3.0 $ref and components reuse — avoid repeating inline definitions
components:
schemas:
# Base entity
User:
type: object
required: [id, email]
properties:
id: { type: string, format: uuid }
email: { type: string, format: email }
name: { type: string, nullable: true }
# Cursor-paginated response (reusable)
UserPage:
type: object
required: [data, pagination]
properties:
data:
type: array
items:
$ref: '#/components/schemas/User' # reference, not inline
pagination:
$ref: '#/components/schemas/CursorPage'
# Cursor pagination vs offset pagination — OpenAPI definition comparison
CursorPage: # cursor-based: suitable for large datasets, stable traversal
type: object
properties:
nextCursor: { type: string, nullable: true, description: null means last page }
hasNextPage: { type: boolean }
limit: { type: integer, minimum: 1, maximum: 100 }
OffsetPage: # offset-based: simple but poor performance at large offsets; document the risk
type: object
description: "offset performance degrades on large datasets; switch to cursor above ~1M records"
properties:
total: { type: integer }
page: { type: integer, minimum: 1 }
limit: { type: integer, minimum: 1, maximum: 100 }
# oneOf + discriminator: polymorphic types
Notification:
oneOf:
- $ref: '#/components/schemas/EmailNotification'
- $ref: '#/components/schemas/SmsNotification'
discriminator:
propertyName: type # client uses this field to determine the concrete type
mapping:
email: '#/components/schemas/EmailNotification'
sms: '#/components/schemas/SmsNotification'
From requirements to mergeable draft
[ Resource and verb inventory ]
│
▼
┌─────────────┐ Set: info / servers / security / tags
│ Metadata │──── Version semantics, env URLs, auth model
└─────────────┘
│
▼
┌─────────────┐ Plural paths, nesting depth, query & pagination
│ paths draft │──── Each operation: summary, operationId, $ref params
└─────────────┘
│
▼
┌─────────────┐ Request/response schemas, examples, error refs
│ components │──── discriminator / oneOf polymorphism rules
└─────────────┘
│
▼
┌─────────────┐ Spectral / CI; breaking-change notes & migration
│ Lint & review│──── Emit fixes per rule; do not swallow violations
└─────────────┘
Merge order: align team template (errors, pagination, auth), then paths and examples, then run rules; avoid pasting large inline objects before security and error schemas exist.
info, servers, and securitySchemes
info.title / description target humans; contact and license per repo policy. servers lists dev/stage/prod or variables—avoid only-local hardcoded URLs.
# Security schemes — complete definition examples
components:
securitySchemes:
BearerJWT:
type: http
scheme: bearer
bearerFormat: JWT
description: "RS256-signed JWT, issuer=https://auth.example.com"
OAuth2:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://auth.example.com/authorize
tokenUrl: https://auth.example.com/token
scopes:
read:items: "Read item list"
write:items: "Create and update items"
ApiKey:
type: apiKey
in: header
name: X-API-Key
# Global security (default requirement) + per-operation override for exceptions
security:
- BearerJWT: []
paths:
/public/status:
get:
security: [] # overrides global: no auth required for this endpoint (add comment explaining why)
- Multi-tenant or region prefix: pick server variables or path prefix once and use consistently.
Paths and resource modeling
Use plural collections (/users, /users/{userId}); cap sub-resource nesting per team (often 2–3). operationId should be stable for codegen.
Prefer
- Filtering, sorting, field selection via query params; document defaults and caps
- Cursor +
limit; ifpageis optional, say when it applies - Bulk operations on dedicated paths or explicit
Prefersemantics if used
Avoid
- Verbs in paths (unless RPC style is explicitly approved)
- Multiple URL shapes for the same resource without migration notes
- Undocumented magic query toggles
components reuse and constraints
Shared types live in components.schemas; bodies and responses use $ref. Spell out required, nullable, default, enum, format (uuid, date-time, …).
- One schema each for paginated responses, errors, and empty successes—avoid rewrites everywhere.
- Polymorphism uses
discriminator; describe extension strategy in description. - Keep examples in sync with schema; CI can validate examples against schema.
Error bodies and status codes
Unify 4xx/5xx content: RFC 7807 application/problem+json or a team envelope (code, message, details, …).
- List meanings for
400,401,403,404,409,422,429,500, and business-specific codes. - Document
429withRetry-Afterbehavior at the doc layer.
Versioning and breaking changes
Choose one: path prefix (/v1), header (Accept-Version), or subdomain; document when to bump major and coexistence period.
Breaking examples: remove fields, change types, change enum meaning, tighten auth. List in description or a migration section; keep old paths in-doc or mark sunset.
Spectral and rule sets
Use a team Spectral ruleset or OWASP/API extensions; run in PR/CI and print rule id and JSON path on failure.
- For each finding, choose: change contract, change rule, or documented waiver—no silent
ignorewithout record. - Match JSON Schema draft to OpenAPI 3.0 vs 3.1; do not mix incompatible features.
OpenAPI skeleton lab
Pick OpenAPI version, auth, and error style, enter API title, then copy the generated YAML skeleton; add paths and components as needed.
Output has no concrete paths; $ref components.schemas.Problem or ErrorEnvelope from each operation’s responses. Do not treat Bearer and OAuth2 as the only scheme together unless the gateway supports it.
---
name: openapi-design
description: Draft OpenAPI 3 contracts, error models, and reviewable examples
---
# Essentials
1. components reuse and security conventions
2. Unified error body and common status codes
3. Linting and breaking-change callouts