- Writing OpenAPI Specs
- Reference for OpenAPI best practices and conventions. This skill provides guidance on taste, conventions, and grey areas not covered by the OpenAPI specification itself.
- When to Use This Skill
- Writing a new OpenAPI specification
- Improving operation naming and organization
- Expressing complex data types (enums, polymorphism, nullable)
- Handling file uploads, streaming, or server-sent events
- Making specs more code-gen friendly
- Understanding reusability patterns (components vs inline)
- Core Principles
- Naming Conventions
- Operation IDs
- Use lowercase with underscores, following resource_action pattern:
Good
operationId : users_list operationId : users_get operationId : users_create
Avoid
operationId : GetApiV1Users
Auto-generated, not semantic
- Component Names
- Use PascalCase for schemas, parameters, and other reusable components: components : schemas : UserProfile :
PascalCase
OrderHistory :
PascalCase
parameters : PageLimit :
PascalCase
- Tags
-
- Use lowercase with hyphens for machine-friendly tags:
- tags
- :
- -
- name
- :
- user
- -
- management
- description
- :
- Operations for managing users
- -
- name
- :
- order
- -
- processing
- description
- :
- Operations for processing orders
- For more details, see
- reference/operations.md
- and
- reference/components.md
- .
- Documentation Standards
- Use CommonMark
- All description fields support CommonMark syntax for rich formatting: description : | Retrieves a user by ID.
Authorization
Requires users
:
read scope.
Rate Limits
- -
- 100 requests per minute per API key
- -
- 1000 requests per hour per IP
- Be Specific
- Provide actionable information, not generic descriptions:
Good
description : Returns a paginated list of active users , ordered by creation date (newest first)
Avoid
- description
- :
- Gets users
- Use
- examples
- over
- example
- The plural examples field provides better SDK generation:
Good
examples : basic_user : value : id : 123 name : "John Doe" admin_user : value : id : 456 name : "Jane Admin" role : admin
Avoid single example
example : id : 123 name : "John Doe" For more details, see reference/examples.md . Reusability Create components for : Schemas used in multiple operations Common parameters (pagination, filtering) Common responses (errors, success patterns) Security schemes Keep inline for : Operation-specific request bodies Unique response shapes One-off parameters
Reusable schema
components : schemas : User : type : object properties : id : { type : integer } name : { type : string }
Reference it
- paths
- :
- /users/
- {
- id
- }
- :
- get
- :
- responses
- :
- '200'
- :
- content
- :
- application/json
- :
- schema
- :
- $ref
- :
- '#/components/schemas/User'
- For more details, see
- reference/components.md
- .
- Complex Patterns Quick Reference
- These patterns are commonly challenging. Brief examples below with links to detailed guidance.
- Enums
- Use string enums with clear, semantic values:
- type
- :
- string
- enum
- :
- -
- pending
- -
- approved
- -
- rejected
- -
- cancelled
- Avoid
- :
- Numeric strings ("0", "1", "2")
- Generic values ("value1", "value2")
- Unclear abbreviations ("pnd", "appr")
- See
- reference/schemas.md#enums
- for more.
- Polymorphism (oneOf/allOf/anyOf)
- oneOf
-
- Value matches exactly one schema (type discrimination)
- PaymentMethod
- :
- oneOf
- :
- -
- $ref
- :
- '#/components/schemas/CreditCard'
- -
- $ref
- :
- '#/components/schemas/BankAccount'
- -
- $ref
- :
- '#/components/schemas/PayPal'
- discriminator
- :
- propertyName
- :
- type
- mapping
- :
- credit_card
- :
- '#/components/schemas/CreditCard'
- bank_account
- :
- '#/components/schemas/BankAccount'
- paypal
- :
- '#/components/schemas/PayPal'
- allOf
-
- Value matches all schemas (composition/inheritance)
- AdminUser
- :
- allOf
- :
- -
- $ref
- :
- '#/components/schemas/User'
- -
- type
- :
- object
- properties
- :
- permissions
- :
- type
- :
- array
- items
- :
- {
- type
- :
- string
- }
- anyOf
- Value matches one or more schemas (flexible union) SearchFilter : anyOf : - $ref : '#/components/schemas/TextFilter' - $ref : '#/components/schemas/DateFilter' - $ref : '#/components/schemas/NumericFilter' See reference/schemas.md#polymorphism for detailed guidance. Discriminators Use discriminators with oneOf for efficient type identification: Pet : oneOf : - $ref : '#/components/schemas/Dog' - $ref : '#/components/schemas/Cat' discriminator : propertyName : petType mapping : dog : '#/components/schemas/Dog' cat : '#/components/schemas/Cat' Dog : type : object required : [ petType , bark ] properties : petType : type : string enum : [ dog ] bark : type : string Cat : type : object required : [ petType , meow ] properties : petType : type : string enum : [ cat ] meow : type : string See reference/schemas.md#discriminators for more. Nullable Types Handle null values differently based on OpenAPI version: OpenAPI 3.1 (JSON Schema 2020-12 compliant): type : [ string , "null" ]
or
type : string nullable : true
Still supported for compatibility
OpenAPI 3.0 : type : string nullable : true For optional fields, use required array: type : object properties : name : { type : string }
Can be omitted
email : { type : string }
Can be omitted
required : [ name ]
email is optional
See reference/schemas.md#nullable for more. File Uploads Use multipart/form-data for file uploads: requestBody : required : true content : multipart/form-data : schema : type : object properties : file : type : string format : binary metadata : type : object properties : description : { type : string } tags : type : array items : { type : string } required : [ file ] For base64-encoded files in JSON: requestBody : content : application/json : schema : type : object properties : filename : { type : string } content : type : string format : byte
base64-encoded
See reference/request-bodies.md#file-uploads for more. Server-Sent Events (SSE) Express streaming responses with text/event-stream : responses : '200' : description : Stream of events content : text/event-stream : schema : type : string description : | Server-sent events stream. Each event follows the format:
event
:
message
data
:
{
"type"
:
"update"
,
"content"
:
"..."
}
- examples
- :
- notification_stream
- :
- value
- :
- |
- event: message
- data:
- event
- :
- message
- data
- :
- {
- "type"
- :
- "notification"
- ,
- "message"
- :
- "Order processing complete"
- }
- See
- reference/responses.md#streaming
- for more patterns.
- Field Reference
- Detailed guidance for each major OpenAPI field:
- Operations
-
- Operation IDs, methods, tags, deprecation →
- reference/operations.md
- Schemas
-
- Data types, polymorphism, enums, nullable →
- reference/schemas.md
- Parameters
-
- Path, query, header, cookie parameters →
- reference/parameters.md
- Request Bodies
-
- Content types, file uploads, encoding →
- reference/request-bodies.md
- Responses
-
- Status codes, streaming, error patterns →
- reference/responses.md
- Components
-
- Reusable definitions, organization →
- reference/components.md
- Security
-
- Authentication schemes, scopes, granular control →
- reference/security.md
- Examples
-
- Single and multiple examples, placement →
- reference/examples.md
- SDK Generation Considerations
- When writing specs for SDK generation:
- Always define
- operationId
-
- Required for meaningful method names
- Provide rich examples
-
- Helps generate better documentation and tests
- Be explicit about required fields
-
- Affects SDK method signatures
- Use discriminators with oneOf
-
- Generates type-safe unions
- Document error responses
- Generates better error handling code Example SDK-friendly operation: paths : /users : get : operationId : users_list summary : List all users description : Returns a paginated list of users parameters : - name : limit in : query schema : { type : integer , default : 20 } - name : offset in : query schema : { type : integer , default : 0 } responses : '200' : description : Success content : application/json : schema : type : object required : [ data , pagination ] properties : data : type : array items : $ref : '#/components/schemas/User' pagination : $ref : '#/components/schemas/PaginationInfo' examples : success : value : data : [ { id : 1 , name : "Alice" } , { id : 2 , name : "Bob" } ] pagination : { total : 100 , limit : 20 , offset : 0 } This generates: sdk.users.list({limit: 20, offset: 0}) Common Pitfalls Don't : Use generic descriptions like "Gets data" or "Returns object" Mix naming conventions (pick one style and stick to it) Forget operationId (causes auto-generated names) Use example when you mean examples (plural is better) Make everything required (be thoughtful about optional fields) Inline everything (use components for reusability) Reference everything (inline simple one-off schemas) Forget to document error responses Use magic numbers without explanation Omit content types (be explicit) Do : Provide actionable, specific descriptions Use consistent naming patterns throughout Define clear operationId for every operation Use examples (plural) with named examples Carefully consider required vs optional fields Balance reusability with clarity Document all expected responses (success and errors) Explain constraints and validation rules Be explicit about content types and formats