Files
HealthLog/docs/api
Marc-André Bombeck 2357807706 feat(import): bring the account's own mood vocabulary along
A full backup carries the account's own mood tags, rated factors and
tag groups, and the JSON import left them out: a mood entry imported
into another account landed without every link to a tag the person had
made.

Before the entries are written, every key they or the file name is
resolved once. A catalogue tag or one the account owns is used as it
is; an own tag whose label the file states differently is kept, never
overwritten, and named in keptMoodTagKeys (keptMoodCategoryKeys for a
group). A tag the file defines and the account lacks is created in its
group, the group created the same way, within the account's caps of 50
tags and 12 groups. A key another account of the server holds comes
over under a key derived from the account and the file's key, so a
second import of the same file finds the copy instead of minting
another. The links are written through that resolution, own rated
factors included, inside the entry's transaction; anything unresolved
is still dropped and named. The import card names kept keys in all
nine locales.

The field-coverage guard now holds the backup's customMoodTags and
customMoodTagCategories to the import schema. The round trip moves a
backup to another server (the same seeded catalogue, none of the
account's rows), imports it into a fresh account and compares the
re-export, own tags, group and rated factor included; a second case
imports into another account of the same server, and a third keeps a
renamed own tag.
2026-10-23 22:41:36 +02:00
..

HealthLog API Spec

openapi.yaml is the OpenAPI 3.1 description for the HealthLog Next.js API and the source of truth for the native iOS client's DTO codegen. The file is generated: pnpm openapi:generate emits it from the Zod registry under src/lib/openapi/ (registry.ts plus the route modules in src/lib/openapi/routes/). Do not edit the YAML by hand; the next generation overwrites it, and CI (openapi:check) fails the build when the committed YAML drifts from the registry.

The MCP server is documented separately — it is a distinct contract over JSON-RPC, not part of the REST OpenAPI spec:

Preview locally

Open an interactive Redoc preview in your browser:

npx @redocly/cli preview docs/api/openapi.yaml

Generate static HTML for sharing:

npx @redocly/cli build-docs docs/api/openapi.yaml --output docs/api/index.html

Validate

npx @redocly/cli lint docs/api/openapi.yaml

The spec is expected to be lint-clean (errors = 0). Warnings such as no-server-example.com for the localhost dev server are tolerated; re-run the lint to see what it produces against the current file before treating a warning as a regression.

Layout

  • info — title, description, and version mirrored automatically from package.json at generation time. Never bump it by hand.
  • servers — https://healthlog.example.com as the self-hosted placeholder plus http://localhost:3000 for local development.
  • tags — domain groupings; the current set lives at the top of the generated file (Auth, Measurements, Medications, Mood, Analytics, Insights, Consent, Dashboard, Notifications, Devices, Export, Sync, Cycle, MeasurementReminders, Labs, Illness, Documents, Records, Admin, Meta, Retired).
  • securitySchemes:
    • bearerAuth — hlk_* API tokens. A scope grants what it names and nothing adjacent, so where a token comes from decides what it reaches:

      • a completed password / passkey / refresh exchange mints the cookie-equivalent ["*"] token the native client carries;
      • a medication's own API-endpoint toggle mints medication:ingest plus medication:<id>:ingest, the pair /api/ingest/medication gates on;
      • POST /api/mcp/tokens mints health:read (optionally plus health:write), audience-bound to /mcp and refused on REST writes;
      • POST /api/tokens/measurements mints measurements:write, accepted by POST /api/measurements and POST /api/measurements/batch only, on the holder's own record only. The readings it writes carry source: EXTERNAL and are linked to the token; whether the HealthLog app writes them into Apple Health is the token's mirrorToAppleHealth setting (off by default, changed with PATCH /api/tokens/{id}), resolved onto every measurement row as appleHealthMirror;
      • POST /api/tokens/workouts mints workouts:write, accepted by POST /api/workouts/batch only, on the holder's own record only; the workouts it writes carry source: EXTERNAL;
      • POST /api/tokens/documents mints documents:write, accepted by POST /api/documents/inbound and GET /api/documents/inbound/source only.

      POST /api/tokens is not among them — that generic mint was removed in v1.30.17. The path still lists (GET) and revokes (DELETE /api/tokens/{id}) every token above, whichever surface issued it.

    • sessionCookie — HttpOnly healthlog_session cookie set by the password and passkey login flows. Used by the web app and any iOS client running inside a web view.

  • components.schemas — DTOs for the documented routes plus the underlying Prisma models (e.g. Measurement, Medication, MedicationIntakeEvent, MoodEntry, User, DashboardLayout, ApiEnvelope, ApiError).
  • paths — the client-facing REST surface. Not every src/app/api/**/route.ts is in the contract: most /api/admin/* routes, the OAuth connect/callback legs, and the webhook surfaces are not documented here. The Retired tag lists removed paths that answer 410 Gone.

Conventions

  • Successful responses use the envelope { data, error: null }.
  • Error responses use the envelope { data: null, error: '<message>' }.
  • Pagination uses limit + offset query parameters and a meta: { total, limit, offset } block on the response.
  • All timestamps are ISO 8601 / RFC 3339 (format: date-time) in UTC.
  • IDs are CUIDs (opaque strings).

Updating the spec

When adding or changing routes:

  1. Update the relevant route handler under src/app/api/**/route.ts.
  2. Change the Zod schemas and their .meta() registrations under src/lib/openapi/ (registry entry plus the matching module in src/lib/openapi/routes/). Keep operationId camelCase and stable; the iOS codegen keys off it.
  3. Run pnpm openapi:generate and commit the regenerated openapi.yaml alongside the schema change. CI's openapi:check fails on drift.
  4. Never edit the YAML directly and never bump info.version by hand; the version follows package.json at generation time.