Skip to content

Platform API

The Platform API (services/api) serves the hub. Games never call it — they use the Game Contract and the hub calls the API on their behalf.

Conventions

TopicRule
Base pathEverything under /api. The hub reaches it on its own origin (Vite proxy in dev, Caddy in docker/prod).
FormatJSON, camelCase, numbers are numbers (JsonNumberHandling.Strict).
ErrorsRFC 9457 problem details (application/problem+json) with a machine-readable code; validation errors carry codes per field.
DescriptionOpenAPI 3.1, generated at build time to services/api/openapi/tohe-api.json and committed.
ClientsThe hub’s types are generated from that file (pnpm --filter @tohe/hub generate:api). Never hand-write request/response types.
AuthCookie session tohe.session on the hub origin (__Host-tohe.session when the hub is served over HTTPS — ADR 0021); account and profile management behind the parent gate. See Identity and ADR 0011.
HealthGET /api/health → Healthy when the database is reachable.
TelemetryPOST /api/telemetry is anonymous (the hub’s batches, ADR 0015); GET /api/telemetry/report needs the operator token in X-Tohe-Operator. See Device telemetry.
Interactive docsGET /api/docs (Scalar) on a running API where Api:Reference:Enabled is on: pnpm dev and compose, not UAT or production (Configuration).

Changing the API

  1. Change the endpoint in its module (Modules/<Name>/), with a test in tests/Tohe.Api.Tests.
  2. pnpm --filter @tohe/api build regenerates openapi/tohe-api.json.
  3. pnpm --filter @tohe/hub generate:api regenerates the hub’s types; fix the hub until pnpm typecheck passes.
  4. Commit all three together. CI fails if any generated file is stale.

Breaking an endpoint the deployed hub uses is not allowed while an old hub may still be cached on phones: add the new shape next to the old one, ship the hub, then remove the old shape.

Reference

The generated reference lives at Platform API (generated).