Files
HealthLog/docs/api
Marc-André Bombeck 467e83d639 feat(encounters): one suggestion rule for every filing moment
A document arriving in the vault and a lab panel being committed both ask the
same question — does this belong to a visit? — and the answer has to be the
same question asked once. The rule lives in one module with one exported
window constant: the caller's live DONE or PLANNED visits within seven days of
an anchor date, resolved into a verdict rather than a list.

One candidate pre-selects, visibly, with one undo. Two or more offer a picker
with nothing pre-selected: pre-selecting the first of two is what teaches a
person to stop reading suggestions, and the return type has no field on that
arm a caller could mistake for a choice. Zero offers nothing.

Cancelled visits and no-shows are not candidates. Neither happened, so neither
produced the letter or the panel being filed, and admitting them would widen
the set with rows that are wrong most of the time — which pushes the honest
single match into the picker branch.

The verdict is served over HTTP because all three moments live in the browser.
Handing the client a windowed list and letting it decide would put the rule in
two places, and the copy in the browser is the one nobody re-reads.

- suggest-window.ts with the window as an exported constant, not a literal
- GET /api/encounters/suggest, profile domain, reviewed into the frozen literal
- OpenAPI regenerated
- fixtures are relative to the run instant; a fixed date slides out of a
  relative window and rots the test into a false green
2026-08-08 12:43:51 +02:00
..

HealthLog API Spec

openapi.yaml is the canonical OpenAPI 3.1 description for the HealthLog Next.js API. It is the source of truth for the native iOS client's DTO codegen.

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). Some warnings are intentional:

  • no-server-example.com — http://localhost:3000 is the dev server.
  • operation-2xx-response / operation-4xx-response — webhook + redirect endpoints intentionally omit one or the other.
  • no-unused-components — ReminderPhase is exposed for client codegen even though no path response references it directly.

Layout

  • info — title, version (mirrored from package.json), description.
  • servers — production https://healthlog.bombeck.io plus http://localhost:3000 for local development.
  • tags — domain groupings (Auth, Measurements, Medications, Mood, Dashboard, Insights, Achievements, DoctorReport, Integrations, User, Tokens, Notifications, Onboarding, Health, Settings, Analytics, Export, AuditLog, Monitoring, Webhooks, Ingest, Admin).
  • securitySchemes:
    • bearerAuth — long-lived hlk_* API tokens issued via POST /api/tokens. Required on /api/ingest/medication.
    • 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 all routes plus the underlying Prisma models (e.g. Measurement, Medication, MedicationIntakeEvent, MoodEntry, User, DashboardLayout, ApiEnvelope, ApiError).
  • paths — all routes from src/app/api/**/route.ts, including the /api/admin/* family which is tagged Admin and marked internal.

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. Mirror the change in openapi.yaml. Keep operationId camelCase and stable — the iOS codegen keys off it.
  3. Run npx @redocly/cli lint docs/api/openapi.yaml and resolve all errors. Warnings are tolerated when documented above.
  4. Bump info.version if the change is breaking for clients.