Skip to main content

How We Document APIs

This document explains our code-first approach to API documentation. We maintain a single source of truth (the API manifest) and auto-generate Mintlify reference tables from it.

Overview

Our API documentation workflow follows these principles:
  1. Code-first: The manifest in packages/api-spec/src/manifest.ts is the source of truth
  2. Auto-generated: Reference tables are generated from the manifest
  3. Scope-safe: Guardrails ensure no SuperAdmin endpoints leak into tenant docs
  4. Lightweight: Regex-based scanning, no runtime code changes

Workflow

Step 1: Scan Routes

Discover all API routes in the codebase:
pnpm api:scan
This scans apps/scm/app/api/**/route.ts files and generates:
  • packages/.generated/api-routes.json - Complete route inventory
  • packages/.generated/manifest-stubs.ts - Suggested manifest entries
What it does:
  • Finds all route handlers
  • Detects HTTP methods (GET, POST, etc.)
  • Identifies authentication patterns
  • Extracts Zod schema names
  • Classifies routes as tenant-facing vs internal-only

Step 2: Generate Stubs

Create manifest stub suggestions:
pnpm api:stubs
This reads the scan results and generates:
  • packages/.generated/api-manifest.stubs.ts - Ready-to-copy manifest entries
What it does:
  • Converts paths to stable IDs
  • Infers tags from path prefixes
  • Detects auth types
  • Sets default stability (beta for safety)
  • Marks internal-only routes

Step 3: Refine Manifest

Copy relevant stubs to packages/api-spec/src/manifest.ts and:
  1. Fill in summaries: Replace “TODO: Add summary” with clear descriptions
  2. Add descriptions: Provide detailed endpoint documentation
  3. Add examples: Include request/response examples
  4. Set permissions: Document required permissions
  5. Adjust stability: Mark stable endpoints as stable (default is beta)
  6. Add query params: Document query parameters
  7. Add path params: Document dynamic route segments
Example:
{
  id: 'forecast.events.create',
  method: 'POST',
  path: '/api/forecast/events',
  summary: 'Create an external event',
  description: 'Create a new external event (promotion, holiday, etc.) to drive forecast adjustments',
  tags: ['forecasting'],
  audience: 'tenant',
  auth: 'session',
  stability: 'stable',
  permissions: ['forecast.write'],
  requestSchema: 'CreateEventSchema',
  queryParams: [
    { name: 'from', type: 'string', required: false, description: 'Start date (ISO string)' },
    { name: 'to', type: 'string', required: false, description: 'End date (ISO string)' },
  ],
  examples: [
    {
      title: 'Create promotion event',
      request: {
        body: {
          name: 'Summer Sale 2024',
          type: 'PROMOTION',
          startsAt: '2024-06-01T00:00:00Z',
          endsAt: '2024-06-30T23:59:59Z',
        },
      },
      response: {
        status: 201,
        body: {
          id: 'event_789',
          name: 'Summer Sale 2024',
        },
      },
    },
  ],
}

Step 4: Validate

Check the manifest against guardrails:
pnpm api:validate
Validations:
  • ✅ No SuperAdmin paths or references
  • ✅ All tenant routes have proper auth
  • ✅ All IDs are unique
  • ✅ All paths start with /api/
  • ✅ All routes have summaries and tags

Step 5: Generate Docs

Update Mintlify reference tables:
pnpm api:docs
This updates the delimited blocks in:
  • docs-site/api-reference/forecasting.mdx
  • docs-site/api-reference/inventory.mdx
  • docs-site/api-reference/alerts.mdx
  • docs-site/api-reference/transfers.mdx
  • docs-site/api-reference/catalog.mdx
What it does:
  • Groups routes by tag
  • Sorts by path, then method
  • Generates markdown tables
  • Includes permissions when available
  • Marks beta endpoints with ⚠️
  • Only updates delimited blocks (preserves other content)

CI Guard

Before committing, run the CI guard:
pnpm api:guard
Checks:
  • ✅ Manifest doesn’t contain excluded paths (SuperAdmin)
  • ✅ Generated docs blocks are present in all target files
  • ✅ Docs blocks are properly formatted
This should be run in CI to prevent regressions.

Scope Rules

✅ Include

  • Tenant-facing APIs in apps/scm/app/api/**
  • APIs consumed by external integrators
  • Tenant-admin configuration APIs (/api/admin/*)

❌ Exclude

  • SuperAdmin endpoints (apps/admin/**)
  • Tenant provisioning APIs
  • Entitlement assignment APIs
  • Billing admin operations
  • Internal cron endpoints (unless tenant-configurable)

⚠️ When in Doubt

Mark ambiguous endpoints as internalOnly: true until confirmed tenant-facing.

File Structure

packages/api-spec/
├── src/
│   ├── types.ts          # Type definitions
│   ├── manifest.ts       # API route specifications (source of truth)
│   └── index.ts          # Package exports
├── scripts/
│   ├── scan-routes.ts    # Route scanner
│   ├── build-manifest.ts # Manifest stub generator
│   ├── validate-manifest.ts # Manifest validator
│   ├── generate-mintlify.ts # Docs generator
│   └── ci-guard.ts       # CI guard
└── package.json

packages/.generated/
├── api-routes.json       # Scan results
├── manifest-stubs.ts     # Generated stubs
└── api-manifest.stubs.ts # Refined stubs

docs-site/api-reference/
├── forecasting.mdx       # Contains generated table block
├── inventory.mdx         # Contains generated table block
├── alerts.mdx            # Contains generated table block
├── transfers.mdx         # Contains generated table block
└── catalog.mdx           # Contains generated table block

Delimited Blocks

Generated tables are wrapped in delimited blocks:
{/* BEGIN:API_TABLE tag=forecasting */}
{/* this block is generated; do not edit by hand */}
| Method | Path | Summary | Auth | Stability | Permissions |
| ...    | ...  | ...     | ...  | ...       | ...         |
{/* END:API_TABLE tag=forecasting */}
Important:
  • Do NOT edit content between these markers
  • The generator will overwrite it
  • All other content in the file is preserved

Best Practices

  1. Keep it lightweight: Use regex-based scanning, don’t refactor runtime code
  2. Prefer correctness: Mark ambiguous endpoints as internalOnly: true
  3. Scope safety first: Better to exclude than include SuperAdmin endpoints
  4. Update regularly: Run api:scan when adding new routes
  5. Validate before commit: Run api:validate and api:guard

Troubleshooting

”Route not in docs”

  1. Check if route is in manifest: packages/api-spec/src/manifest.ts
  2. Verify route has correct tags matching target file
  3. Run pnpm api:docs to regenerate tables
  4. Check if route is marked internalOnly: true

”CI guard failing”

  1. Check for SuperAdmin paths: pnpm api:validate
  2. Verify docs blocks exist: Check target files for delimited blocks
  3. Regenerate docs: pnpm api:docs

”Missing permissions column”

Permissions column is only shown when routes have permissions defined in manifest. Add permissions to routes that need them.