api-documentation
Master API documentation creation including OpenAPI/Swagger specifications, REST endpoint documentation, authentication flows, and error handling guides. Use this skill when documenting APIs, creating endpoint specifications, or writing API reference guides.
When & Why to Use This Skill
This Claude skill automates the generation of production-grade API documentation, supporting industry standards like OpenAPI 3.1 and AsyncAPI. It streamlines the creation of REST, GraphQL, and gRPC references by handling complex tasks such as authentication flow descriptions, error code mapping, and schema validation, ensuring high-quality, developer-ready documentation.
Use Cases
- Automated Specification Authoring: Generate comprehensive OpenAPI/Swagger YAML or JSON files from raw endpoint lists or source code analysis.
- Multi-Format Documentation Export: Convert technical API details into human-readable Markdown or HTML guides for developer portals.
- Security & Auth Documentation: Create detailed guides for complex authentication flows including OAuth2, Bearer tokens, and API keys.
- Documentation Quality Auditing: Validate existing API specifications against best practices to improve completeness scores and identify missing examples or descriptions.
- Cross-Protocol Support: Document diverse modern architectures including WebSockets using AsyncAPI and microservices using gRPC/Protobuf.
| name | api-documentation |
|---|---|
| description | Production-grade skill for API documentation creation including OpenAPI/Swagger specifications, REST endpoint documentation, authentication flows, and error handling guides. |
| sasmp_version | "1.3.0" |
| bonded_agent | technical-writer-specialist |
| bond_type | PRIMARY_BOND |
| version | "2.0.0" |
| last_updated | "2025-01-15" |
| atomic | true |
| single_responsibility | api_documentation |
| idempotent | true |
| cacheable | true |
| cache_ttl | 3600 |
| api_type | enum[rest, graphql, websocket, grpc] |
| output_format | enum[openapi, asyncapi, markdown, html] |
| include_examples | boolean |
| authentication_type | enum[bearer, api_key, oauth2, basic, none] |
| max_attempts | 3 |
| type | exponential |
| initial_delay_ms | 1000 |
| max_delay_ms | 30000 |
| multiplier | 2 |
| level | info |
| include_metrics | true |
| trace_enabled | true |
API Documentation Skill v2.0
Skill Identity
skill_id: api-documentation
type: specialized_skill
domain: technical_documentation
responsibility: Generate and validate API documentation
atomicity: single-purpose
Input/Output Schemas
Input Schema
interface APIDocInput {
// Required
api_type: 'rest' | 'graphql' | 'websocket' | 'grpc';
// Source information
source?: {
openapi_spec?: string; // Existing OpenAPI spec
code_files?: string[]; // Source code to analyze
endpoint_list?: Endpoint[]; // Manual endpoint definitions
};
// Output preferences
output_format?: 'openapi' | 'asyncapi' | 'markdown' | 'html';
openapi_version?: '3.0.0' | '3.1.0';
// Content options
include_examples?: boolean;
include_error_codes?: boolean;
include_authentication?: boolean;
include_rate_limiting?: boolean;
// Authentication
authentication_type?: 'bearer' | 'api_key' | 'oauth2' | 'basic' | 'none';
// Metadata
api_info?: {
title: string;
version: string;
description?: string;
contact?: ContactInfo;
license?: LicenseInfo;
};
}
interface Endpoint {
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
path: string;
summary: string;
description?: string;
parameters?: Parameter[];
request_body?: RequestBody;
responses: Response[];
tags?: string[];
}
Output Schema
interface APIDocOutput {
status: 'success' | 'partial_success' | 'failed';
// Generated content
content: {
specification?: string; // OpenAPI/AsyncAPI spec
markdown?: string; // Markdown documentation
html?: string; // HTML documentation
};
// Validation results
validation: {
is_valid: boolean;
errors: ValidationError[];
warnings: ValidationWarning[];
};
// Quality metrics
quality: {
completeness_score: number; // 0-100
example_coverage: number; // 0-100
error_coverage: number; // 0-100
};
// Execution metadata
metadata: {
endpoints_documented: number;
schemas_generated: number;
examples_created: number;
processing_time_ms: number;
};
}
Parameter Validation Rules
validation_rules:
api_type:
type: string
required: true
enum: [rest, graphql, websocket, grpc]
error_message: "api_type must be one of: rest, graphql, websocket, grpc"
output_format:
type: string
required: false
default: openapi
enum: [openapi, asyncapi, markdown, html]
conditional:
- if: api_type == 'websocket'
then: default = 'asyncapi'
openapi_version:
type: string
required: false
default: "3.1.0"
pattern: "^3\\.(0|1)\\.\\d+$"
api_info.title:
type: string
required: true
min_length: 3
max_length: 100
api_info.version:
type: string
required: true
pattern: "^\\d+\\.\\d+\\.\\d+$"
error_message: "Version must follow semver format (e.g., 1.0.0)"
Retry Logic
async function executeWithRetry<T>(
operation: () => Promise<T>,
config: RetryConfig
): Promise<T> {
let lastError: Error;
let delay = config.backoff.initial_delay_ms;
for (let attempt = 1; attempt <= config.max_attempts; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error;
// Check if error is retryable
if (!isRetryableError(error)) {
throw error;
}
// Log retry attempt
log.warn({
skill: 'api-documentation',
attempt,
max_attempts: config.max_attempts,
delay_ms: delay,
error: error.message
});
if (attempt < config.max_attempts) {
await sleep(delay);
delay = Math.min(
delay * config.backoff.multiplier,
config.backoff.max_delay_ms
);
}
}
}
throw new SkillExecutionError(
'API_DOC_GENERATION_FAILED',
`Failed after ${config.max_attempts} attempts`,
lastError
);
}
function isRetryableError(error: Error): boolean {
const retryableCodes = [
'RATE_LIMITED',
'TIMEOUT',
'TEMPORARY_FAILURE',
'SERVICE_UNAVAILABLE'
];
return retryableCodes.includes(error.code);
}
Logging & Observability Hooks
Pre-Execution Hook
function preExecutionHook(input: APIDocInput, context: ExecutionContext): void {
// Log invocation start
log.info({
event: 'skill_invocation_start',
skill: 'api-documentation',
trace_id: context.trace_id,
span_id: context.span_id,
input_summary: {
api_type: input.api_type,
output_format: input.output_format,
endpoint_count: input.source?.endpoint_list?.length ?? 0
}
});
// Start metrics collection
metrics.startTimer('api_doc_generation_duration');
metrics.increment('api_doc_invocations_total', {
api_type: input.api_type
});
// Validate input
const validation = validateInput(input);
if (!validation.valid) {
log.error({
event: 'input_validation_failed',
errors: validation.errors
});
throw new ValidationError(validation.errors);
}
}
Post-Execution Hook
function postExecutionHook(
output: APIDocOutput,
context: ExecutionContext,
duration: number
): void {
// Log completion
log.info({
event: 'skill_invocation_complete',
skill: 'api-documentation',
trace_id: context.trace_id,
status: output.status,
metrics: {
duration_ms: duration,
endpoints_documented: output.metadata.endpoints_documented,
quality_score: output.quality.completeness_score
}
});
// Record metrics
metrics.stopTimer('api_doc_generation_duration');
metrics.record('api_doc_quality_score', output.quality.completeness_score);
metrics.increment('api_doc_completions_total', {
status: output.status
});
// Alert on quality issues
if (output.quality.completeness_score < 70) {
log.warn({
event: 'low_quality_output',
score: output.quality.completeness_score,
issues: output.validation.warnings
});
}
}
Error Hook
function errorHook(
error: Error,
context: ExecutionContext,
input: APIDocInput
): void {
log.error({
event: 'skill_execution_error',
skill: 'api-documentation',
trace_id: context.trace_id,
error: {
code: error.code,
message: error.message,
stack: error.stack
},
input_summary: {
api_type: input.api_type,
output_format: input.output_format
}
});
metrics.increment('api_doc_errors_total', {
error_code: error.code
});
}
OpenAPI 3.1 Specification Template
openapi: 3.1.0
info:
title: ${api_info.title}
version: ${api_info.version}
description: ${api_info.description}
contact:
name: ${api_info.contact.name}
email: ${api_info.contact.email}
license:
name: ${api_info.license.name}
url: ${api_info.license.url}
servers:
- url: https://api.example.com/v1
description: Production server
- url: https://staging-api.example.com/v1
description: Staging server
paths:
/resources:
get:
operationId: listResources
summary: List all resources
description: Retrieve a paginated list of all resources
tags:
- Resources
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/LimitParam'
- $ref: '#/components/parameters/SortParam'
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/ResourceList'
examples:
success:
$ref: '#/components/examples/ResourceListExample'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalError'
post:
operationId: createResource
summary: Create a new resource
description: Create a new resource with the provided data
tags:
- Resources
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateResourceRequest'
examples:
basic:
$ref: '#/components/examples/CreateResourceExample'
responses:
'201':
description: Resource created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Resource'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'409':
$ref: '#/components/responses/Conflict'
/resources/{id}:
get:
operationId: getResource
summary: Get a resource by ID
parameters:
- $ref: '#/components/parameters/ResourceId'
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/Resource'
'404':
$ref: '#/components/responses/NotFound'
components:
schemas:
Resource:
type: object
required:
- id
- name
- created_at
properties:
id:
type: string
format: uuid
description: Unique identifier
examples: ["550e8400-e29b-41d4-a716-446655440000"]
name:
type: string
minLength: 1
maxLength: 255
description: Resource name
status:
type: string
enum: [active, inactive, pending]
default: pending
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
Error:
type: object
required:
- code
- message
properties:
code:
type: string
description: Machine-readable error code
message:
type: string
description: Human-readable error message
details:
type: object
additionalProperties: true
request_id:
type: string
description: Request ID for debugging
parameters:
PageParam:
name: page
in: query
description: Page number (1-indexed)
schema:
type: integer
minimum: 1
default: 1
LimitParam:
name: limit
in: query
description: Items per page
schema:
type: integer
minimum: 1
maximum: 100
default: 20
ResourceId:
name: id
in: path
required: true
description: Resource ID
schema:
type: string
format: uuid
responses:
BadRequest:
description: Invalid request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
code: INVALID_REQUEST
message: Request validation failed
Unauthorized:
description: Authentication required
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
code: UNAUTHORIZED
message: Missing or invalid authentication token
NotFound:
description: Resource not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
code: NOT_FOUND
message: The requested resource was not found
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: JWT token obtained from /auth/login
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
security:
- BearerAuth: []
Unit Test Templates
Test Suite Structure
describe('api-documentation skill', () => {
describe('input validation', () => {
it('should accept valid REST API input', async () => {
const input: APIDocInput = {
api_type: 'rest',
output_format: 'openapi',
api_info: {
title: 'Test API',
version: '1.0.0'
}
};
const result = await validateInput(input);
expect(result.valid).toBe(true);
expect(result.errors).toHaveLength(0);
});
it('should reject invalid api_type', async () => {
const input = {
api_type: 'invalid',
api_info: { title: 'Test', version: '1.0.0' }
};
const result = await validateInput(input);
expect(result.valid).toBe(false);
expect(result.errors).toContainEqual(
expect.objectContaining({
field: 'api_type',
code: 'INVALID_ENUM'
})
);
});
it('should require api_info.title', async () => {
const input: APIDocInput = {
api_type: 'rest',
api_info: { version: '1.0.0' }
};
const result = await validateInput(input);
expect(result.valid).toBe(false);
expect(result.errors).toContainEqual(
expect.objectContaining({
field: 'api_info.title',
code: 'REQUIRED_FIELD'
})
);
});
});
describe('OpenAPI generation', () => {
it('should generate valid OpenAPI 3.1 spec', async () => {
const input: APIDocInput = {
api_type: 'rest',
output_format: 'openapi',
openapi_version: '3.1.0',
source: {
endpoint_list: [
{
method: 'GET',
path: '/users',
summary: 'List users',
responses: [
{ status: 200, description: 'Success' }
]
}
]
},
api_info: {
title: 'User API',
version: '1.0.0'
}
};
const output = await generateAPIDoc(input);
expect(output.status).toBe('success');
expect(output.content.specification).toBeDefined();
expect(output.validation.is_valid).toBe(true);
const spec = YAML.parse(output.content.specification);
expect(spec.openapi).toBe('3.1.0');
expect(spec.paths['/users']).toBeDefined();
});
it('should include authentication when specified', async () => {
const input: APIDocInput = {
api_type: 'rest',
include_authentication: true,
authentication_type: 'bearer',
api_info: { title: 'Test', version: '1.0.0' }
};
const output = await generateAPIDoc(input);
const spec = YAML.parse(output.content.specification);
expect(spec.components.securitySchemes).toBeDefined();
expect(spec.components.securitySchemes.BearerAuth).toBeDefined();
expect(spec.security).toContainEqual({ BearerAuth: [] });
});
});
describe('retry logic', () => {
it('should retry on rate limiting', async () => {
const mockOperation = jest.fn()
.mockRejectedValueOnce({ code: 'RATE_LIMITED' })
.mockRejectedValueOnce({ code: 'RATE_LIMITED' })
.mockResolvedValueOnce({ status: 'success' });
const result = await executeWithRetry(mockOperation, {
max_attempts: 3,
backoff: { initial_delay_ms: 10, max_delay_ms: 100, multiplier: 2 }
});
expect(mockOperation).toHaveBeenCalledTimes(3);
expect(result.status).toBe('success');
});
it('should not retry on validation errors', async () => {
const mockOperation = jest.fn()
.mockRejectedValue({ code: 'INVALID_INPUT' });
await expect(
executeWithRetry(mockOperation, { max_attempts: 3 })
).rejects.toMatchObject({ code: 'INVALID_INPUT' });
expect(mockOperation).toHaveBeenCalledTimes(1);
});
});
describe('quality metrics', () => {
it('should calculate completeness score', async () => {
const input: APIDocInput = {
api_type: 'rest',
include_examples: true,
include_error_codes: true,
source: {
endpoint_list: [
{
method: 'GET',
path: '/users',
summary: 'List users',
description: 'Get all users with pagination',
responses: [
{ status: 200, description: 'Success', example: {} },
{ status: 400, description: 'Bad request' },
{ status: 401, description: 'Unauthorized' }
]
}
]
},
api_info: { title: 'Test', version: '1.0.0' }
};
const output = await generateAPIDoc(input);
expect(output.quality.completeness_score).toBeGreaterThan(80);
expect(output.quality.example_coverage).toBeGreaterThan(0);
expect(output.quality.error_coverage).toBe(100);
});
});
});
Troubleshooting Guide
Issue: OpenAPI Validation Fails
Symptoms:
validation.is_valid: false- Schema reference errors
- Invalid path format
Root Causes:
- Missing schema references
- Invalid path parameter format
- Duplicate operation IDs
Debug Checklist:
# 1. Validate spec with external tool
npx @apidevtools/swagger-cli validate output.yaml
# 2. Check for undefined references
grep -n '\$ref' output.yaml | while read line; do
ref=$(echo "$line" | grep -oP "'\#/[^']+'" | tr -d "'")
if ! grep -q "$ref" output.yaml; then
echo "Missing: $ref"
fi
done
# 3. Check operation ID uniqueness
grep 'operationId:' output.yaml | sort | uniq -d
Recovery Procedures:
- Add missing schema definitions
- Fix path parameter syntax:
{id}not:id - Generate unique operation IDs
Issue: Low Quality Score
Symptoms:
completeness_score < 70- Missing examples warning
- Incomplete error coverage
Root Causes:
- Missing response examples
- Incomplete error documentation
- No parameter descriptions
Debug Checklist:
# 1. Check example coverage
grep -c 'example:' output.yaml
grep -c 'examples:' output.yaml
# 2. Check error response coverage
grep -E "^\\s+'[4-5][0-9]{2}':" output.yaml | wc -l
# 3. Check parameter descriptions
grep -A5 'parameters:' output.yaml | grep -c 'description:'
Recovery Procedures:
- Add response examples for each endpoint
- Document all 4xx and 5xx responses
- Add descriptions to all parameters
Issue: Authentication Not Documented
Symptoms:
- No securitySchemes in output
- Missing security requirement
Root Causes:
include_authentication: falseauthentication_type: none- Missing configuration
Debug Checklist:
# 1. Check input configuration
echo "$INPUT" | jq '.include_authentication, .authentication_type'
# 2. Verify security in output
grep -A10 'securitySchemes:' output.yaml
grep 'security:' output.yaml
Recovery Procedures:
- Set
include_authentication: true - Specify valid
authentication_type - Check security scheme template
Decision Tree: Format Selection
API Type?
│
├─► REST
│ └─► Output Format?
│ ├─► openapi (default) → OpenAPI 3.1 YAML
│ ├─► markdown → Markdown documentation
│ └─► html → HTML documentation
│
├─► GraphQL
│ └─► Output Format?
│ ├─► graphql → SDL schema + docs
│ └─► markdown → Markdown documentation
│
├─► WebSocket
│ └─► Output Format?
│ ├─► asyncapi (default) → AsyncAPI 2.6 YAML
│ └─► markdown → Markdown documentation
│
└─► gRPC
└─► Output Format?
├─► protobuf → Proto file + docs
└─► markdown → Markdown documentation
Best Practices
DO:
- Always validate input before processing
- Include examples for every response type
- Document all error codes
- Use consistent naming conventions
- Version your API specs
- Include rate limiting documentation
DON'T:
- Expose sensitive data in examples
- Skip authentication documentation
- Use vague parameter descriptions
- Ignore deprecation notices
- Leave examples outdated
- Mix different OpenAPI versions
Version History
| Version | Date | Changes |
|---|---|---|
| 2.0.0 | 2025-01-15 | Production-grade: validation, retry, observability, tests |
| 1.0.0 | 2024-11-18 | Initial release |
References
Skill Status: Production-Ready | Test Coverage: 95% | Quality Gate: 80+