ArchitectureAPI Contract (OpenAPI)

API Contract (OpenAPI)

Overview

The OpenAPI 3.1 specification at /openapi.yaml is the single source of truth for all API types and endpoints.

Bi-Directional Codegen

TypeScript (Frontend)

cd packages/api-client
pnpm generate

Generates TypeScript types and a type-safe openapi-fetch client.

Go (Backend)

The Go handlers are manually implemented to match the contract, but request/response types can be validated against the OpenAPI spec.

Development Workflow

  1. Modify /openapi.yaml with new endpoints, schemas, request/response bodies
  2. Run codegen to update TypeScript types
  3. Implement Go handlers matching the contract
  4. Build Next.js Server Actions proxying the new endpoints
  5. Test end-to-end

Key Rules

  • Never hand-write API types or fetchers
  • Every feature starts with an OpenAPI contract change
  • The contract must be reviewed and approved before implementation begins

Exposure and the public API

Every operation is internal unless openapi.yaml says otherwise:

  • x-api-exposure: beta | stable opens an operation to access tokens. Absent means internal.
  • x-api-since: "YYYY-MM-DD" is required on an exposed operation. x-api-sunset (a date) marks one for retirement.
  • x-api-sensitivity: personal_contact | personal_diversity | financial_rate | secret sits on a response property. Tokens without the matching scope get null (contact, diversity), rates are always null, and secrets are removed.

The x-* keys are a closed list per location. A misspelled key fails pnpm api:codegen and CI.

pnpm api:codegen runs apigen, which writes the public spec (openapi.public.yaml), the route table the API enforces (backend/pkg/apimap/apimap.generated.go), the stable-shape manifest (api.frozen.txt), the API reference and llms.txt. CI regenerates them and fails on any difference.

Gates

GateFails when
G1a generated output is out of date
G2an exposed operation has no permission key with a token scope, no 2xx schema, a non-token security scheme, a financial, destructive or external risk, or sits on a contract-truth ratchet
G3a stable operation’s shape breaks callers
G4an exposed response has an unclassified personal-looking field or an open object
G5an exposed write has no cross-org test
G6an exposed write could skip the Idempotency-Key check
G7an exposed operation names an entity with no ontology card
G10an exposed operation is not served, or the server serves a route the contract does not declare (beyond the list)