| name | openapi-spec |
| description | Write and update OpenAPI 3.0 specification files from REST API code. Use when: documenting REST endpoints, creating API specs, generating swagger.yaml, updating API documentation from implementation. |
| argument-hint | Describe the API endpoints to document or specify the code files containing REST handlers |
OpenAPI 3.0 Specification Writer
When to Use
Load this skill when asked to:
- Create an OpenAPI/Swagger specification from existing REST API code
- Document REST endpoints in OpenAPI 3.0 format
- Update an existing
openapi.yaml or swagger.yaml file
- Generate API documentation that follows OpenAPI 3.0 standards
- Extract endpoint definitions, parameters, and responses from code
Ground Rules
- Target specification: OpenAPI 3.0.x (not Swagger 2.0)
- Output format: YAML (preferred) or JSON
- Follow OpenAPI 3.0 Specification
- Include examples for complex schemas
- Document all response codes actually returned by the implementation
Procedure
1. Analyze the REST API Code
Static Route Registration
Search for and identify:
- HTTP request handlers and route definitions (e.g.,
server.on(), route arrays)
- HTTP methods (GET, POST, PUT, PATCH, DELETE, OPTIONS)
- Path patterns and parameters (
:id, {id}, wildcards)
- Query parameters from
request->getParam() or equivalent
- Request body parsing (JSON, form data, multipart)
- Response codes and JSON structures
- Authentication/authorization requirements
- Error responses and status codes
Dynamic Route Registration
Some REST APIs use plugin or service architectures where endpoints are registered dynamically:
Common patterns to search for:
getTopics() or registerTopics() methods in plugins/services
- Topic handler or topic registration services
- Topic constants (e.g.,
TOPIC_CONFIG, TOPIC_STATUS)
- Dynamic endpoint builders that construct paths from entity IDs and topic names
Topic-based endpoint patterns:
- Plugin endpoints:
/api/v1/display/uid/{uid}/{topic} or /display/alias/{alias}/{topic}
- Service endpoints:
/api/v1/{entityId}/{topic}
- System endpoints:
/api/v1/{topic} (empty entityId)
- Indexed endpoints:
/api/v1/{entityId}/{index}/{topic}
How to find dynamic endpoints:
- Search for
getTopics() implementations - these list available topics
- Find
getTopic() and setTopic() methods - these handle GET/POST requests
- Locate topic registration code - shows how topics become REST endpoints
- Check for
TopicHandlerService or similar dynamic registration systems
- Look for topic constants defined in header or source files
Example (C++):
const char* TOPIC_CONFIG = "config";
const char* TOPIC_STATUS = "status";
void Plugin::getTopics(JsonArray& topics) const
{
topics.add(TOPIC_CONFIG);
topics.add(TOPIC_STATUS);
}
bool Plugin::getTopic(const String& topic, JsonObject& value) const
{
if (topic.equals(TOPIC_CONFIG)) {
}
}
bool Plugin::setTopic(const String& topic, const JsonObjectConst& value)
{
if (topic.equals(TOPIC_CONFIG)) {
}
}
2. Discover All Endpoints
Critical: Don't assume you've found all endpoints after discovering static routes.
Complete discovery workflow:
- Static routes: Find route registration arrays or explicit route handlers
- Plugin topics: Search for classes implementing plugin interfaces
- Look for
getTopics() implementations
- Each plugin may expose multiple topics as REST endpoints
- Service topics: Search for service classes that register topics
- Services often register multiple topics (e.g., files, upload, remove)
- Topic registration: Find the topic handler or registration service
- Understand how topics are converted to REST paths
- Check for path prefix construction (e.g., base + entityId + topic)
- Validate completeness: Cross-reference existing OpenAPI spec
- Check for endpoints in old spec that might still exist
- Verify each documented endpoint still exists in code
- Add missing endpoints found in code
Search patterns:
grep -r "TOPIC_" --include="*.cpp" --include="*.h"
grep -r "getTopics" --include="*.cpp"
grep -r "getTopic\|setTopic" --include="*.cpp"
grep -r "registerTopic" --include="*.cpp"
3. Build the OpenAPI Structure
Start with the base template:
openapi: 3.0.3
info:
title: [API Name]
version: [Version from VERSION constant or git tag]
description: [Brief API description]
contact:
name: [From LICENSE or README]
email: [If available]
servers:
- url: http://{host}/rest/api/v1
description: REST API base path
variables:
host:
default: localhost
description: Device hostname or IP
paths:
components:
schemas:
responses:
securitySchemes:
4. Document Each Endpoint
For each route/handler found:
/path/{param}:
get:
summary: [One-line description from code comments]
description: [Detailed behavior from docstrings/comments]
operationId: [camelCase unique identifier]
tags:
- [Logical grouping]
parameters:
- name: param
in: path
required: true
schema:
type: string
description: [From parameter docs]
- name: query
in: query
required: false
schema:
type: integer
description: [From code inspection]
responses:
'200':
description: Success
content:
{}
5. Extract Response Schemas
From JSON response building code, create reusable schemas:
components:
schemas:
SuccessResponse:
type: object
required:
- status
- data
properties:
status:
type: string
enum: [ok]
data:
type: object
description: Endpoint-specific response data
ErrorResponse:
type: object
required:
- status
- msg
properties:
status:
type: string
enum: [error]
msg:
type: string
description: Human-readable error message
6. Document Authentication
If authentication is present:
components:
securitySchemes:
basicAuth:
type: http
scheme: basic
description: HTTP Basic Authentication
paths:
/protected:
get:
security:
- basicAuth: []
7. Validate and Refine
- Check that all paths start with
/
- Verify all
$ref references exist
- Ensure required fields are present
- Add examples for complex request/response bodies
- Group related endpoints with
tags
- Document error responses consistently
Output Format
Save the specification as:
docs/openapi.yaml or docs/swagger.yaml (YAML preferred)
docs/api-spec.yaml (alternative naming)
Include a comment header:
Common Patterns
REST API with CRUD Operations
/items:
get:
summary: List all items
responses:
'200':
description: Array of items
post:
summary: Create new item
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ItemCreate'
responses:
'201':
description: Item created
/items/{id}:
get:
summary: Get single item
put:
summary: Update item
delete:
summary: Delete item
Query Parameters
parameters:
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20
- name: offset
in: query
schema:
type: integer
minimum: 0
default: 0
File Upload
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
format: binary
path:
type: string
Common Pitfalls & Lessons Learned
File Paths vs Numeric IDs
Issue: APIs may use numeric IDs internally instead of file paths.
Example:
parameters:
- name: iconPath
schema:
type: string
example: "/images/icon.bmp"
parameters:
- name: iconFileId
schema:
type: number
example: 1234
How to identify: Check the actual implementation - look for FileId types, ID resolution methods like getFileFullPathById(), or file manager services that map IDs to paths.
Missing Dynamic Endpoints
Issue: Forgetting to document endpoints that are registered dynamically via plugin/service systems.
Solution:
- Don't rely only on static route definitions
- Search for
getTopics(), registerTopics(), or similar methods
- Check for topic registration services that create REST endpoints
- Look for topic handler implementations
- Verify each plugin/service that registers topics
Example: In Pixelix, sensor endpoints (/sensors/{index}/{channelName}) are created dynamically by SensorDataProvider registering topics via TopicHandlerService, not found in static route arrays.
Path Pattern Consistency
Issue: API paths may have multiple formats depending on registration method.
Check for:
- Service endpoints:
/{serviceId}/{topic}
- Plugin endpoints by UID:
/display/uid/{uid}/{topic}
- Plugin endpoints by alias:
/display/alias/{alias}/{topic}
- Indexed endpoints:
/{entityId}/{index}/{topic}
- System endpoints:
/{topic} (no prefix)
Parameter Types from Code
Issue: Documentation doesn't match actual parameter types used in code.
Verify:
- Check actual JSON key names in
getTopic()/setTopic() implementations
- Confirm data types (string, number, boolean, array, object)
- Note optional vs required parameters
- Check for parameter validation rules (min/max, enums)
Configuration vs Command Topics
Issue: Some topics serve dual purposes.
Pattern:
- Config topics: GET returns current config, POST updates and persists config
- Command topics: GET returns status, POST executes actions with
action parameter
- Status topics: GET only, returns current state
Example:
/display/uid/{uid}/playCtrl:
post:
parameters:
- name: action
schema:
type: string
enum: [next, previous, pause, continue]
Tips
- Use
$ref for reusability: Common responses and schemas should be defined once in components
- Include examples: Especially for complex nested objects
- Document all response codes: Even error cases (400, 401, 403, 404, 500)
- Add operation IDs: Unique, descriptive
operationId for code generation tools
- Group with tags: Logical grouping improves generated documentation (e.g., by plugin name or service name)
- Version properly: Use
info.version matching your API versioning scheme
- Add curl examples: Include practical curl command examples in endpoint descriptions to show authentication and parameter usage
- Consistency matters: Keep response schemas, error formats, and authentication patterns consistent across all endpoints
- Trace the implementation: Don't guess parameter types or structures - read the actual code that builds JSON responses
Validation
After generating the spec, validate it using:
- Swagger Editor - paste YAML to check for errors
swagger-cli validate openapi.yaml - CLI validation
- VS Code OpenAPI extensions for real-time validation
References