Skip to content

Hosting Docs UI (CloudFront / Pages)

Serve the Callspec Docs UI behind CloudFront, S3, or Cloudflare/Netlify Pages so it feels like a native product surface — without Fern.

Live data (callspec.json, OpenAPI, RPC, MCP) always comes from your API process. Only the UI shell and assets can move to a CDN.

PatternWhen to useCallspec work
A — Reverse proxySame origin as the API; simplestNone (document + infra)
B — Static exportUI on S3/Pages; API elsewhereexport-docs-ui + absolute URLs + CORS
C — Shared SPA distributionApex path like /docs next to marketingInfra behaviors (+ A or B)
code
Browser → CloudFront / Pages → api.example.com/v1/docs/

All /docs/*, callspec.json, RPC, and MCP hit the API (via CloudFront behavior, Caddy, or ALB). Relative specUrl / rpcBase from mountSpec keep working.

  • Pros: No export step; try-it and MCP stay colocated
  • Cons: HTML is not a pure edge object (cache GET /docs/ with a short TTL if you want)
  • Logfox fit: CloudFront behavior or path rewrite on the API host

No Callspec code changes. Use Express cache headers from mountCallspecUi (HTML no-cache, hashed JS/CSS immutable).

Pattern B — Static UI on CDN + API for JSON/RPC

Section titled “Pattern B — Static UI on CDN + API for JSON/RPC”
code
/docs/* → S3 + CloudFront (exported shell + assets)
/v1/* → API (callspec.json, openapi.json, RPC, MCP)
  1. Build Callspec (npm run build or install the published package).
  2. Export a deployable folder:
bash
callspec export-docs-ui --out ./docs-ui-dist \
--spec-url https://api.example.com/v1/callspec.json \
--rpc-base https://api.example.com/v1 \
--mcp-url https://api.example.com/v1/mcp \
--title "Acme API"
  1. Upload docs-ui-dist/ (aws s3 sync, Pages, Netlify).
  2. Enable CORS on the API for the docs origin (try-it browser fetches).
  3. Set CDN Cache-Control for hashed /assets/* to long-lived / immutable; keep index.html short TTL or no-cache.

Absolute specUrl, rpcBase, and mcp.url are plain strings — the browser fetch / URL APIs accept them as-is.

When --rpc-base is an http(s) URL and you omit --mcp-path / --mcp-url, export defaults mcpPath to {rpcBase}/mcp so MCP does not resolve against the CDN docs origin.

Pattern C — Same distribution as a marketing SPA

Section titled “Pattern C — Same distribution as a marketing SPA”

Extend your static frontend CloudFront (or Pages) map:

PathOrigin
/docs/*S3 (exported UI) or API reverse proxy (Pattern A)
/*Marketing / app SPA

Subpath on the apex (example.com/docs) is DNS + behaviors — not a separate docs. host.

  • Behavior /docs/assets/* → S3; cache policy honors origin Cache-Control (hashed JS/CSS are immutable)
  • Behavior /docs* → S3; index.html short TTL / no-cache
  • API remains a separate origin for /v1/* (or your mount)

Point /docs/* (and API paths you need) at the API origin — same idea as a Fern reverse proxy. Prefer Pattern A when you do not need a separate static bucket.

  • Allow the docs origin on API CORS
  • Allow methods used by try-it (POST, OPTIONS)
  • Allow Authorization if routes use bearer auth
  • MCP over HTTP may need the same origin allowlist depending on the client