Skip to main content

API Documentation Skill

API Documentation Skill

When to Use This Skill

Use this skill when implementing api documentation patterns in your codebase.

How to Use This Skill

  1. Review the patterns and examples below
  2. Apply the relevant patterns to your implementation
  3. Follow the best practices outlined in this skill

Comprehensive API documentation patterns for creating discoverable, interactive, and maintainable API references.

OpenAPI 3.1 Specification

Basic Structure

# openapi.yaml
openapi: 3.1.0
info:
title: User Service API
version: 1.0.0
description: |
User management API for authentication, profiles, and permissions.

## Authentication
All endpoints require Bearer token authentication.

## Rate Limiting
- Standard: 100 requests/minute
- Authenticated: 1000 requests/minute
contact:
name: API Support
email: api@example.com
url: https://developer.example.com
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0

servers:
- url: https://api.example.com/v1
description: Production
- url: https://staging-api.example.com/v1
description: Staging
- url: http://localhost:3000/v1
description: Development

tags:
- name: Users
description: User management operations
- name: Auth
description: Authentication endpoints
- name: Admin
description: Administrative operations

paths:
/users:
get:
summary: List all users
description: Retrieve paginated list of users with optional filtering
operationId: listUsers
tags: [Users]
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/LimitParam'
- name: status
in: query
schema:
type: string
enum: [active, inactive, pending]
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/UserList'
'401':
$ref: '#/components/responses/Unauthorized'
post:
summary: Create a user
operationId: createUser
tags: [Users]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUser'
examples:
basic:
summary: Basic user
value:
email: user@example.com
name: John Doe
admin:
summary: Admin user
value:
email: admin@example.com
name: Admin User
role: admin
responses:
'201':
description: User created
content:
application/json:
schema:
$ref: '#/components/schemas/User'

/users/{userId}:
get:
summary: Get user by ID
operationId: getUser
tags: [Users]
parameters:
- $ref: '#/components/parameters/UserIdParam'
responses:
'200':
description: User found
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
$ref: '#/components/responses/NotFound'

components:
schemas:
User:
type: object
required: [id, email, name]
properties:
id:
type: string
format: uuid
readOnly: true
email:
type: string
format: email
name:
type: string
minLength: 1
maxLength: 100
role:
type: string
enum: [user, admin, moderator]
default: user
createdAt:
type: string
format: date-time
readOnly: true
example:
id: "123e4567-e89b-12d3-a456-426614174000"
email: "user@example.com"
name: "John Doe"
role: "user"
createdAt: "2024-01-15T10:30:00Z"

CreateUser:
type: object
required: [email, name]
properties:
email:
type: string
format: email
name:
type: string
role:
type: string
enum: [user, admin]

UserList:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
pagination:
$ref: '#/components/schemas/Pagination'

Pagination:
type: object
properties:
total:
type: integer
page:
type: integer
limit:
type: integer
hasMore:
type: boolean

Error:
type: object
required: [code, message]
properties:
code:
type: string
message:
type: string
details:
type: object

parameters:
UserIdParam:
name: userId
in: path
required: true
schema:
type: string
format: uuid

PageParam:
name: page
in: query
schema:
type: integer
minimum: 1
default: 1

LimitParam:
name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20

responses:
Unauthorized:
description: Authentication required
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
code: UNAUTHORIZED
message: Invalid or missing authentication token

NotFound:
description: Resource not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'

securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key

security:
- BearerAuth: []

Redoc Integration

Static HTML Generation

# Install Redoc CLI
npm install -g @redocly/cli

# Generate static HTML
redocly build-docs openapi.yaml -o docs/api.html

# Preview locally
redocly preview-docs openapi.yaml

React Component

// ApiDocs.tsx
import { RedocStandalone } from 'redoc'

export function ApiDocs() {
return (
<RedocStandalone
specUrl="/api/openapi.yaml"
options={{
theme: {
colors: {
primary: { main: '#3B82F6' }
},
typography: {
fontSize: '15px',
fontFamily: 'Inter, sans-serif',
headings: {
fontFamily: 'Inter, sans-serif'
}
},
sidebar: {
width: '260px'
}
},
hideDownloadButton: false,
expandResponses: '200,201',
requiredPropsFirst: true,
sortPropsAlphabetically: true,
pathInMiddlePanel: true,
jsonSampleExpandLevel: 2
}}
/>
)
}

Redoc Configuration

# redocly.yaml
extends:
- recommended

theme:
openapi:
theme:
colors:
primary:
main: '#3B82F6'
typography:
fontSize: '15px'
fontFamily: 'Inter, sans-serif'
sidebar:
backgroundColor: '#F8FAFC'
rightPanel:
backgroundColor: '#1E293B'

apis:
main:
root: openapi.yaml

rules:
no-unused-components: warn
operation-operationId: error
operation-description: warn
tag-description: warn

Swagger UI Integration

Express.js Setup

// swagger.ts
import swaggerUi from 'swagger-ui-express'
import YAML from 'yamljs'
import { Express } from 'express'

export function setupSwagger(app: Express) {
const swaggerDocument = YAML.load('./openapi.yaml')

const options: swaggerUi.SwaggerUiOptions = {
explorer: true,
customCss: '.swagger-ui .topbar { display: none }',
customSiteTitle: 'API Documentation',
swaggerOptions: {
persistAuthorization: true,
displayRequestDuration: true,
filter: true,
tryItOutEnabled: true
}
}

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, options))

// Serve raw OpenAPI spec
app.get('/api-docs.json', (req, res) => {
res.json(swaggerDocument)
})
}

FastAPI (Python)

# main.py
from fastapi import FastAPI
from fastapi.openapi.docs import get_swagger_ui_html, get_redoc_html
from fastapi.openapi.utils import get_openapi

app = FastAPI(
title="User Service API",
version="1.0.0",
description="User management API",
docs_url=None, # Disable default
redoc_url=None
)

# Custom Swagger UI
@app.get("/docs", include_in_schema=False)
async def custom_swagger_ui():
return get_swagger_ui_html(
openapi_url="/openapi.json",
title=f"{app.title} - Swagger UI",
swagger_js_url="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js",
swagger_css_url="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css",
)

# Custom Redoc
@app.get("/redoc", include_in_schema=False)
async def custom_redoc():
return get_redoc_html(
openapi_url="/openapi.json",
title=f"{app.title} - ReDoc",
)

# Custom OpenAPI schema
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema

openapi_schema = get_openapi(
title=app.title,
version=app.version,
description=app.description,
routes=app.routes,
)

# Add security schemes
openapi_schema["components"]["securitySchemes"] = {
"BearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
}

app.openapi_schema = openapi_schema
return app.openapi_schema

app.openapi = custom_openapi

Stoplight Studio

Project Structure

api-project/
├── reference/
│ ├── openapi.yaml # Main spec
│ └── models/
│ ├── User.yaml
│ ├── Error.yaml
│ └── Pagination.yaml
├── docs/
│ ├── getting-started.md
│ ├── authentication.md
│ └── rate-limiting.md
└── .stoplight.json

Stoplight Configuration

// .stoplight.json
{
"projectName": "User Service API",
"formats": ["oas3.1"],
"linting": {
"ruleset": ".spectral.yaml"
}
}

Model References

# reference/models/User.yaml
type: object
title: User
description: Represents a user in the system
required:
- id
- email
- name
properties:
id:
type: string
format: uuid
readOnly: true
email:
type: string
format: email
name:
type: string
examples:
- id: "123e4567-e89b-12d3-a456-426614174000"
email: "user@example.com"
name: "John Doe"

API Versioning in Docs

URL-Based Versioning

# openapi-v1.yaml
openapi: 3.1.0
info:
title: User Service API
version: 1.0.0

servers:
- url: https://api.example.com/v1

# openapi-v2.yaml
openapi: 3.1.0
info:
title: User Service API
version: 2.0.0

servers:
- url: https://api.example.com/v2

Multi-Version Documentation

// version-switcher.tsx
import { useState } from 'react'
import { RedocStandalone } from 'redoc'

const API_VERSIONS = [
{ version: 'v2', url: '/api/v2/openapi.yaml', label: 'v2 (Latest)' },
{ version: 'v1', url: '/api/v1/openapi.yaml', label: 'v1 (Deprecated)' }
]

export function ApiDocs() {
const [version, setVersion] = useState('v2')
const currentSpec = API_VERSIONS.find(v => v.version === version)

return (
<div>
<select value={version} onChange={(e) => setVersion(e.target.value)}>
{API_VERSIONS.map(v => (
<option key={v.version} value={v.version}>{v.label}</option>
))}
</select>
<RedocStandalone specUrl={currentSpec?.url} />
</div>
)
}

Linting with Spectral

Configuration

# .spectral.yaml
extends:
- spectral:oas

rules:
# Naming conventions
operation-operationId-valid-in-url: true
paths-kebab-case: true

# Documentation requirements
operation-description: error
operation-tags: error
info-description: error

# Security
operation-security-defined: error

# Custom rules
response-must-have-example:
description: Responses must have examples
severity: warn
given: "$.paths..responses.*.content.*.schema"
then:
field: example
function: truthy

use-standard-error-format:
description: Error responses must use standard format
severity: error
given: "$.paths..responses[?(@property >= '400')].content.*.schema"
then:
function: schema
functionOptions:
schema:
type: object
required: [code, message]

CI Integration

# .github/workflows/api-lint.yml
name: API Lint

on: [push, pull_request]

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Lint OpenAPI
uses: stoplightio/spectral-action@latest
with:
file_glob: 'openapi.yaml'
spectral_ruleset: '.spectral.yaml'

Automated Doc Generation

TypeScript to OpenAPI

// Using tsoa
// src/controllers/UserController.ts
import { Controller, Get, Post, Body, Route, Tags, Security, Response } from 'tsoa'

@Route('users')
@Tags('Users')
export class UserController extends Controller {
/**
* Retrieves all users with pagination
* @param page Page number
* @param limit Items per page
*/
@Get()
@Security('bearerAuth')
public async getUsers(
@Query() page: number = 1,
@Query() limit: number = 20
): Promise<UserListResponse> {
// Implementation
}

/**
* Creates a new user
* @param body User creation data
*/
@Post()
@Response<ErrorResponse>(400, 'Validation error')
@Response<ErrorResponse>(409, 'User already exists')
public async createUser(
@Body() body: CreateUserRequest
): Promise<User> {
// Implementation
}
}

Generate Spec

# tsoa.json
{
"entryFile": "src/app.ts",
"noImplicitAdditionalProperties": "throw-on-extras",
"controllerPathGlobs": ["src/controllers/**/*.ts"],
"spec": {
"outputDirectory": "docs",
"specVersion": 3,
"securityDefinitions": {
"bearerAuth": {
"type": "http",
"scheme": "bearer"
}
}
}
}

# Generate
npx tsoa spec

Usage Examples

Setup OpenAPI Documentation

Apply api-documentation skill to create OpenAPI 3.1 spec with Redoc for REST API

Add Interactive Explorer

Apply api-documentation skill to integrate Swagger UI with authentication persistence

Implement Version Switching

Apply api-documentation skill to setup multi-version API docs with deprecation notices

Success Output

When successful, this skill MUST output:

✅ SKILL COMPLETE: api-documentation

Completed:
- [x] OpenAPI 3.1 specification created with all endpoints documented
- [x] Redoc or Swagger UI integration configured and tested
- [x] Interactive examples with authentication working
- [x] API versioning strategy implemented
- [x] Spectral linting passing with no errors

Outputs:
- openapi.yaml or openapi.json
- API documentation HTML (docs/api.html or /api-docs endpoint)
- .spectral.yaml linting configuration
- CI/CD integration for automatic spec validation

Completion Checklist

Before marking this skill as complete, verify:

  • OpenAPI specification validates against OpenAPI 3.1 schema
  • All endpoints have operation IDs, summaries, and descriptions
  • Request/response schemas defined with examples
  • Error responses documented (4xx, 5xx status codes)
  • Authentication schemes specified (Bearer, API Key, OAuth)
  • Interactive documentation UI accessible and functional
  • API versioning strategy documented and implemented
  • Spectral linting rules configured and passing
  • CI/CD pipeline validates spec on every commit

Failure Indicators

This skill has FAILED if:

  • ❌ OpenAPI spec fails validation or has syntax errors
  • ❌ Missing endpoint documentation (incomplete coverage)
  • ❌ No examples provided for request/response schemas
  • ❌ Authentication not documented or incorrect
  • ❌ Interactive UI (Redoc/Swagger) not accessible or broken
  • ❌ API versioning not implemented or inconsistent
  • ❌ Spectral linting not configured or has errors
  • ❌ Documentation not auto-generated from code annotations (if using tsoa/FastAPI)

When NOT to Use

Do NOT use this skill when:

  • API has fewer than 3 endpoints (use simple README.md instead)
  • API is internal-only and not exposed to external consumers
  • Documentation needs are non-interactive (use markdown-based docs)
  • GraphQL API (use GraphQL-specific documentation tools like GraphiQL)
  • WebSocket-only API (requires different documentation approach)
  • Prototyping phase before API design is stable
  • Legacy API without OpenAPI support (requires migration work first)

Use these alternatives instead:

  • Simple APIs: README.md with curl examples
  • GraphQL: graphql-documentation-patterns skill
  • Internal APIs: internal-api-documentation skill

Anti-Patterns (Avoid)

Anti-PatternProblemSolution
Manually writing OpenAPI specSpecs drift from actual codeUse code-first tools (tsoa, FastAPI auto-generation)
No request/response examplesDevelopers can't understand usageInclude realistic examples in every schema
Missing error documentationUnclear how errors are handledDocument all 4xx/5xx responses with examples
Version-less API docsBreaking changes break clientsImplement URL-based or header-based versioning
No CI validationSpecs become outdatedAdd Spectral linting to CI/CD pipeline
Over-documenting internal endpointsClutter for external consumersUse x-internal: true to hide internal endpoints
No interactive playgroundDevelopers can't test easilyAlways include Swagger UI or Redoc

Principles

This skill embodies:

  • #2 Progressive Disclosure - Start with minimal spec, progressively add schemas, examples, and advanced features
  • #5 Eliminate Ambiguity - Clear endpoint descriptions, explicit request/response contracts
  • #6 Clear, Understandable, Explainable - Interactive documentation makes API self-documenting
  • #7 Verification Protocol - Spectral linting enforces standards automatically
  • #9 Avoid Reinventing the Wheel - Use OpenAPI 3.1 standard, established tools (Redoc, Swagger)

Full Standard: CODITECT-STANDARD-AUTOMATION.md