API documentation

Guide agents to update OpenAPI alongside route implementations: path params, request schemas, auth, and error bodies must share a single source; examples should run; error codes should match problem+json (or your envelope); prevent doc drift.

This page gives Agents a complete API documentation reference: Swagger UI integration configuration (Express/FastAPI), JSDoc/docstring auto-generated documentation examples, CHANGELOG version recording format, curl request/response examples, and contract testing implementation.

Each public endpoint includes a runnable curl example and a typical failure example; pagination and filter parameters document defaults and caps; versioning strategy (URL prefix) is explained in an early chapter; deprecated APIs use deprecated: true dual marking.

  • CI runs contract tests or schema diff, blocking merges where code changed but YAML wasn't updated.

Swagger UI integration configuration

Express + swagger-ui-express integration:

// app.ts — Express Swagger UI integration
import express from 'express'
import swaggerUi from 'swagger-ui-express'
import YAML from 'yamljs'
import path from 'path'

const app = express()
const swaggerDoc = YAML.load(path.join(__dirname, '../openapi.yaml'))

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDoc, {
  swaggerOptions: {
    persistAuthorization: true,
    filter: true,
    displayRequestDuration: true,
  },
  customCss: '.swagger-ui .topbar { display: none }',
}))

// Also serve raw JSON (for CI schema diff)
app.get('/openapi.json', (_req, res) => res.json(swaggerDoc))

JSDoc auto-generate API documentation comments:

// routes/items.ts
/**
 * @openapi
 * /v1/items/{itemId}:
 *   get:
 *     tags: [Items]
 *     summary: Get a single item
 *     operationId: getV1ItemById
 *     parameters:
 *       - in: path
 *         name: itemId
 *         required: true
 *         schema: { type: string, format: uuid }
 *     responses:
 *       '200':
 *         description: Item details
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/Item'
 *       '404':
 *         $ref: '#/components/responses/NotFoundProblem'
 *     security:
 *       - bearerAuth: []
 */
router.get('/:itemId', authenticate, async (req, res, next) => {
  try {
    const item = await itemService.findById(req.params.itemId)
    if (!item) return res.status(404).json({ type: 'not-found', status: 404 })
    res.json(item)
  } catch (err) { next(err) }
})
  • paths must match real mount prefixes; servers aligns with gateway strip rules.
  • requestBody / responses must reference components.schemas — no copy-paste drift.

curl examples, CHANGELOG, and contract testing

Complete curl examples (success + failure):

# Create item (success)
curl -sS -X POST "https://api.example.com/v1/items"   -H "Authorization: Bearer <ACCESS_TOKEN>"   -H "Content-Type: application/json"   -H "Idempotency-Key: req-abc123"   -d '{"name":"Widget","price":9.99,"sku":"WGT-001"}'

# 200 response
{
  "id": "01HX3K2ZF0GQ9PBEM7NW4Y5V6A",
  "name": "Widget",
  "price": 9.99,
  "sku": "WGT-001",
  "createdAt": "2026-04-11T08:00:00Z"
}

# 422 validation failure
{
  "type": "https://api.example.com/problems/validation-error",
  "title": "Validation Error",
  "status": 422,
  "errors": [
    { "field": "price", "code": "must_be_positive", "message": "price must be greater than 0" }
  ]
}

API CHANGELOG format (CHANGELOG.md):

## [2026-04-11] v2.3.0
### Added
- `GET /v2/items` supports `filter[category]` parameter filtering

### Changed
- `POST /v1/orders` response adds `estimatedDelivery` field (backward compatible)

### Deprecated
- `GET /v1/items/{id}/details` — please migrate to `GET /v2/items/{id}`
  Will be removed on 2026-10-01 (`deprecated: true` marked in OpenAPI)

### Breaking (v2.0.0, 2026-01-01)
- Removed `POST /v1/auth/token`, unified to `/v2/auth/token`
- Error body changed from `{"error":"..."}` to RFC 7807 problem+json

Contract testing (prevent doc/implementation drift):

// tests/contract/items.contract.test.ts
import Ajv from 'ajv'
import addFormats from 'ajv-formats'
import openApiSpec from '../../openapi.json'
import { app } from '../../src/app'
import supertest from 'supertest'

const ajv = new Ajv({ allErrors: true })
addFormats(ajv)

test('GET /v1/items/:id response matches OpenAPI schema', async () => {
  const res = await supertest(app)
    .get('/v1/items/01HX3K2ZF0GQ9PBEM7NW4Y5V6A')
    .set('Authorization', 'Bearer test-token')
    .expect(200)

  const schema = openApiSpec.components.schemas.Item
  const valid = ajv.validate(schema, res.body)
  expect(valid).toBe(true)
  if (!valid) console.error(ajv.errors)
})

Maintenance flow

Main line from code change to published docs; agents can expand each node into checklists (diff scope, reviewers, release window).

  [ Route/handler change: path, params, auth, response models ]
        |
        v
  [ Update OpenAPI: paths, components, examples, deprecated ]
        |
        v
  [ Examples + errors: curl/SDK, Problem bodies, error table anchors ]
        |
        v
  [ CI: schema diff / contract tests / breaking-change detection ]
        |
        v
  [ Release: notes, SDK regen, external developer comms ]

operationId and slug preview

Sidebars, anchor URLs, or static generators often turn operationId into kebab-case slugs. The tool below previews common rules (collaboration aid, not a language spec). If operationId is empty, we suggest camelCase from HTTP method + path, then derive the slug.

Preview


                

Slug: insert hyphens at camelCase / snake_case boundaries and lowercase; path-derived ids turn {param} into PascalCase segments appended after the verb.

SKILL snippet

---
name: api-documentation
description: Keep OpenAPI aligned with implementation and complete examples
---
# Rules
- paths / operationId 1:1 correspond to route implementation
- requestBody / responses reference components.schemas, no inline duplicate definitions
- Each endpoint includes curl examples (success + typical failure), errors use RFC 7807 problem+json
- Deprecated endpoints: operationId adds deprecated:true + CHANGELOG Deprecated entry
- CI runs contract tests: use Ajv to verify real responses match OpenAPI schema

# Steps
1. Read existing openapi.yaml and route implementation, find diffs
2. Update paths: add/modify operationId, parameters, requestBody, responses
3. Update components/schemas: add or modify schema, no inline repetition
4. Complete examples: at least one success + one failure curl example per endpoint
5. Update CHANGELOG.md: group as Added/Changed/Deprecated/Breaking
6. Run contract tests: npx jest tests/contract/ confirm no drift
7. Swagger UI accessible: GET /api-docs renders correctly

Back to skills More skills