mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
9862 lines
341 KiB
YAML
9862 lines
341 KiB
YAML
openapi: 3.1.0
|
||
info:
|
||
title: HealthLog API
|
||
version: 1.13.2
|
||
description: >-
|
||
Self-hosted personal-health-tracking PWA — public API surface for the iOS native client and external ingest.
|
||
|
||
|
||
## Date-time contract
|
||
|
||
|
||
Every field marked `format: date-time` is an ISO-8601 instant. The schema pattern accepts both `Z` (UTC) and
|
||
`±HH:MM` (offset) suffixes; clients must accept either. Server responses since v1.4.25 emit timestamps with the
|
||
requesting user's display-timezone offset when a user context exists (exports, doctor-report PDF).
|
||
Background-job-authored payloads (backups, admin tools) emit `Z`. Native iOS clients should always parse via a
|
||
Foundation ISO-8601 formatter with the `withInternetDateTime` + `withTimeZone` options enabled.
|
||
license:
|
||
name: AGPL-3.0
|
||
url: https://github.com/MBombeck/HealthLog/blob/main/LICENSE
|
||
contact:
|
||
name: HealthLog
|
||
url: https://healthlog.dev
|
||
servers:
|
||
- url: https://healthlog.bombeck.io
|
||
description: Production
|
||
- url: http://localhost:3000
|
||
description: Local dev
|
||
tags:
|
||
- name: Auth
|
||
- name: Measurements
|
||
- name: Medications
|
||
- name: Mood
|
||
- name: Analytics
|
||
- name: Insights
|
||
- name: Dashboard
|
||
- name: Notifications
|
||
- name: Devices
|
||
- name: Export
|
||
- name: Sync
|
||
- name: Admin
|
||
- name: Meta
|
||
paths:
|
||
/api/meta/capabilities:
|
||
get:
|
||
tags:
|
||
- Meta
|
||
summary: Live server capability / id-vocabulary discovery
|
||
description: Returns the server's REAL id vocabularies (derived-metric ids, vitals-baseline types, layout tile-ids,
|
||
metric-status ids, the HealthKit ingest mapping, the FHIR coding constants) plus the running API contract
|
||
version. Every list is derived server-side from the canonical registry it documents, so a client can gate its UI
|
||
/ decoder against what the server actually ships rather than a hand-maintained copy. Auth via cookie or Bearer
|
||
(not admin).
|
||
responses:
|
||
"200":
|
||
description: Capability snapshot.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CapabilitiesEnvelope"
|
||
"401": &a1
|
||
description: Authentication required or invalid credentials.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"422": &a2
|
||
description: Request validation failed.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"429": &a3
|
||
description: Rate limit exceeded.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
/api/export/health-record:
|
||
post:
|
||
tags:
|
||
- Export
|
||
summary: Generate a health-record export (PDF / FHIR / package)
|
||
description: "v1.7.0 flagship export. Returns the doctor-handover artefact in the requested `format`: `pdf` →
|
||
application/pdf, `fhir` → application/fhir+json (HL7 FHIR R4 document Bundle), `package` → application/zip (PDF
|
||
+ FHIR + README). Auth via cookie or Bearer; shared `export:<userId>` rate bucket (10/h). Strict validation:
|
||
unknown keys 422."
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
format:
|
||
type: string
|
||
enum:
|
||
- pdf
|
||
- fhir
|
||
- package
|
||
range:
|
||
type: object
|
||
properties:
|
||
startDate:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
endDate:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
days:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 365
|
||
additionalProperties: false
|
||
sections:
|
||
type: object
|
||
properties:
|
||
vitals:
|
||
type: object
|
||
properties:
|
||
weight:
|
||
type: boolean
|
||
bp:
|
||
type: boolean
|
||
pulse:
|
||
type: boolean
|
||
oxygenSaturation:
|
||
type: boolean
|
||
bodyFat:
|
||
type: boolean
|
||
bodyComposition:
|
||
type: boolean
|
||
cardioFitness:
|
||
type: object
|
||
properties:
|
||
restingHeartRate:
|
||
type: boolean
|
||
hrv:
|
||
type: boolean
|
||
vo2max:
|
||
type: boolean
|
||
activity:
|
||
type: object
|
||
properties:
|
||
steps:
|
||
type: boolean
|
||
distance:
|
||
type: boolean
|
||
energy:
|
||
type: boolean
|
||
sleep:
|
||
type: boolean
|
||
glucose:
|
||
type: boolean
|
||
medications:
|
||
type: object
|
||
properties:
|
||
list:
|
||
type: boolean
|
||
compliance:
|
||
type: boolean
|
||
glp1:
|
||
type: boolean
|
||
sideEffects:
|
||
type: boolean
|
||
mood:
|
||
type: boolean
|
||
bmi:
|
||
type: boolean
|
||
locale:
|
||
type: string
|
||
enum:
|
||
- de
|
||
- en
|
||
- fr
|
||
- es
|
||
- it
|
||
- pl
|
||
practiceName:
|
||
type: string
|
||
maxLength: 120
|
||
includeCharts:
|
||
type: boolean
|
||
includeAiSummary:
|
||
type: boolean
|
||
germanAtc:
|
||
type: boolean
|
||
required:
|
||
- format
|
||
additionalProperties: false
|
||
responses:
|
||
"200":
|
||
description: "Export generated. Content-Type varies by `format`: application/pdf, application/fhir+json, or
|
||
application/zip."
|
||
content:
|
||
application/pdf:
|
||
schema:
|
||
type: string
|
||
format: binary
|
||
application/fhir+json:
|
||
schema:
|
||
type: string
|
||
format: binary
|
||
application/zip:
|
||
schema:
|
||
type: string
|
||
format: binary
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/share-links:
|
||
post:
|
||
tags:
|
||
- Export
|
||
summary: Create a clinician share link (v1.11.0)
|
||
description: "Owner-only. Mints an `hls_` token (192-bit), stores only its HMAC hash, and returns the raw token EXACTLY
|
||
ONCE in the response. Every scope column (window, sections, FHIR resource types, API toggle) is frozen
|
||
write-once. `expiresAt` is required and capped at 90 days. Auth via cookie or Bearer; rate-limited
|
||
(`share-link:<userId>`, 20/h). Strict: unknown keys 422."
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
label:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 120
|
||
rangeStart:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
rangeEnd:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
sections:
|
||
type: object
|
||
properties:
|
||
vitals:
|
||
type: object
|
||
properties:
|
||
weight:
|
||
type: boolean
|
||
bp:
|
||
type: boolean
|
||
pulse:
|
||
type: boolean
|
||
oxygenSaturation:
|
||
type: boolean
|
||
bodyFat:
|
||
type: boolean
|
||
bodyComposition:
|
||
type: boolean
|
||
cardioFitness:
|
||
type: object
|
||
properties:
|
||
restingHeartRate:
|
||
type: boolean
|
||
hrv:
|
||
type: boolean
|
||
vo2max:
|
||
type: boolean
|
||
activity:
|
||
type: object
|
||
properties:
|
||
steps:
|
||
type: boolean
|
||
distance:
|
||
type: boolean
|
||
energy:
|
||
type: boolean
|
||
sleep:
|
||
type: boolean
|
||
glucose:
|
||
type: boolean
|
||
medications:
|
||
type: object
|
||
properties:
|
||
list:
|
||
type: boolean
|
||
compliance:
|
||
type: boolean
|
||
glp1:
|
||
type: boolean
|
||
sideEffects:
|
||
type: boolean
|
||
mood:
|
||
type: boolean
|
||
bmi:
|
||
type: boolean
|
||
resourceTypes:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- Patient
|
||
- Observation
|
||
- MedicationStatement
|
||
- MedicationAdministration
|
||
allowFhirApi:
|
||
type: boolean
|
||
expiresAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
required:
|
||
- label
|
||
- rangeStart
|
||
- expiresAt
|
||
additionalProperties: false
|
||
responses:
|
||
"201":
|
||
description: Share link created. `token` carries the raw `hls_` value and is unrecoverable after this response.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ShareLinkCreated"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
get:
|
||
tags:
|
||
- Export
|
||
summary: List own clinician share links (v1.11.0)
|
||
description: Owner-only. Returns the caller's own share links (never the raw token — it is unrecoverable after
|
||
creation). Auth via cookie or Bearer.
|
||
responses:
|
||
"200":
|
||
description: Share links owned by the caller.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ShareLinkList"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/share-links/{id}:
|
||
delete:
|
||
tags:
|
||
- Export
|
||
summary: Revoke a clinician share link (v1.11.0)
|
||
description: Owner-only. Sets `revokedAt` on the caller's own link. A cross-user or unknown id is sealed as 404. Auth
|
||
via cookie or Bearer; rate-limited.
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
schema:
|
||
type: string
|
||
required: true
|
||
responses:
|
||
"200":
|
||
description: Link revoked.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ShareLinkRevoked"
|
||
"401": *a1
|
||
"404":
|
||
description: Link not found (or owned by another user).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/fhir/metadata:
|
||
get:
|
||
tags:
|
||
- FHIR
|
||
summary: FHIR R4 CapabilityStatement (v1.11.0)
|
||
description: "Read-only FHIR R4 capability statement for the REST face. Declares the served resource types (Patient,
|
||
Observation, MedicationStatement, MedicationAdministration), the `$everything` operation, and the
|
||
`application/fhir+json` format. Auth: `fhir:read` scope (cookie sessions also pass)."
|
||
responses:
|
||
"200":
|
||
description: CapabilityStatement (application/fhir+json).
|
||
content:
|
||
application/fhir+json:
|
||
schema:
|
||
type: string
|
||
format: binary
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/fhir/Patient:
|
||
get:
|
||
tags:
|
||
- FHIR
|
||
summary: FHIR R4 Patient search (v1.11.0)
|
||
description: "Read-only `searchset` Bundle of the caller's own Patient resource. Auth: `fhir:read` scope. Offset paging
|
||
via `_count` (clamped ≤200) / `_offset`. `userId` is narrowed from auth."
|
||
parameters:
|
||
- in: query
|
||
name: _count
|
||
schema:
|
||
type: number
|
||
- in: query
|
||
name: _offset
|
||
schema:
|
||
type: number
|
||
responses:
|
||
"200":
|
||
description: searchset Bundle (application/fhir+json).
|
||
content:
|
||
application/fhir+json:
|
||
schema:
|
||
type: string
|
||
format: binary
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/fhir/Observation:
|
||
get:
|
||
tags:
|
||
- FHIR
|
||
summary: FHIR R4 Observation search (v1.11.0)
|
||
description: "Read-only `searchset` Bundle of the caller's own Observations (vitals / activity / lab / survey). Auth:
|
||
`fhir:read` scope. Offset paging via `_count` (clamped ≤200) / `_offset`."
|
||
parameters:
|
||
- in: query
|
||
name: _count
|
||
schema:
|
||
type: number
|
||
- in: query
|
||
name: _offset
|
||
schema:
|
||
type: number
|
||
responses:
|
||
"200":
|
||
description: searchset Bundle (application/fhir+json).
|
||
content:
|
||
application/fhir+json:
|
||
schema:
|
||
type: string
|
||
format: binary
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/fhir/MedicationStatement:
|
||
get:
|
||
tags:
|
||
- FHIR
|
||
summary: FHIR R4 MedicationStatement search (v1.11.0)
|
||
description: "Read-only `searchset` Bundle of the caller's own active-medication statements. Auth: `fhir:read` scope.
|
||
Offset paging via `_count` (≤200) / `_offset`."
|
||
parameters:
|
||
- in: query
|
||
name: _count
|
||
schema:
|
||
type: number
|
||
- in: query
|
||
name: _offset
|
||
schema:
|
||
type: number
|
||
responses:
|
||
"200":
|
||
description: searchset Bundle (application/fhir+json).
|
||
content:
|
||
application/fhir+json:
|
||
schema:
|
||
type: string
|
||
format: binary
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/fhir/MedicationAdministration:
|
||
get:
|
||
tags:
|
||
- FHIR
|
||
summary: FHIR R4 MedicationAdministration search (v1.11.0)
|
||
description: "Read-only `searchset` Bundle of the caller's own acted intakes (completed / not-done). Auth: `fhir:read`
|
||
scope. Offset paging via `_count` (≤200) / `_offset`."
|
||
parameters:
|
||
- in: query
|
||
name: _count
|
||
schema:
|
||
type: number
|
||
- in: query
|
||
name: _offset
|
||
schema:
|
||
type: number
|
||
responses:
|
||
"200":
|
||
description: searchset Bundle (application/fhir+json).
|
||
content:
|
||
application/fhir+json:
|
||
schema:
|
||
type: string
|
||
format: binary
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/fhir/$everything:
|
||
get:
|
||
tags:
|
||
- FHIR
|
||
summary: FHIR R4 $everything (v1.11.0)
|
||
description: "Read-only `$everything` operation: every resource in the caller's own record (Patient, Coverage,
|
||
Observations, MedicationStatements, MedicationAdministrations) in one `searchset` Bundle. Auth: `fhir:read`
|
||
scope. Offset paging via `_count` (≤200) / `_offset`."
|
||
parameters:
|
||
- in: query
|
||
name: _count
|
||
schema:
|
||
type: number
|
||
- in: query
|
||
name: _offset
|
||
schema:
|
||
type: number
|
||
responses:
|
||
"200":
|
||
description: searchset Bundle (application/fhir+json).
|
||
content:
|
||
application/fhir+json:
|
||
schema:
|
||
type: string
|
||
format: binary
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/auth/login:
|
||
post:
|
||
tags:
|
||
- Auth
|
||
summary: Email-or-username login (password)
|
||
description: "Browser callers receive a session cookie. Native callers (X-Client-Type: native or HealthLog-iOS UA
|
||
prefix) additionally receive a paired access + refresh token."
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
email:
|
||
type: string
|
||
minLength: 1
|
||
password:
|
||
type: string
|
||
minLength: 1
|
||
required:
|
||
- email
|
||
- password
|
||
responses:
|
||
"200":
|
||
description: Login succeeded.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/LoginResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/auth/passkey/login-verify:
|
||
post:
|
||
tags:
|
||
- Auth
|
||
summary: Passkey assertion verification
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PasskeyLoginVerifyRequest"
|
||
responses:
|
||
"200":
|
||
description: Assertion verified — session + optional bearer issued.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PasskeyLoginVerifyResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/auth/refresh:
|
||
post:
|
||
tags:
|
||
- Auth
|
||
summary: Rotate refresh token (one-time use)
|
||
description: >-
|
||
Reuse of a consumed refresh token revokes every refresh token still active for the originating device
|
||
(per-device blast radius from v1.4.23). Legacy tokens issued before v1.4.23 with a null deviceId fall back to
|
||
revoke-all-for-user.
|
||
|
||
|
||
On a 401, `meta.errorCode` is a stable machine code so the client can branch terminal re-auth from a transient
|
||
blip without parsing the prose `error`: `auth.refresh.reuse` (a consumed token was replayed — device family
|
||
revoked, re-pair required), `auth.refresh.revoked` (family revoked out-of-band — re-pair required),
|
||
`auth.refresh.invalid` (not found / expired — drop the token and re-authenticate).
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/RefreshTokenRequest"
|
||
responses:
|
||
"200":
|
||
description: Rotation succeeded — new pair issued.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/RefreshResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/sync/state:
|
||
get:
|
||
tags:
|
||
- Sync
|
||
summary: Sync handshake + window metadata
|
||
description: Cheap 'should I sync?' summary. Returns the previous `lastSyncedAt` checkpoint and advances it server-side
|
||
on each call. The `sync` block carries the incremental-delta window + tombstone retention so the client reads
|
||
them rather than hardcoding.
|
||
responses:
|
||
"200":
|
||
description: Sync state summary.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/SyncStateEnvelope"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/sync/changes:
|
||
get:
|
||
tags:
|
||
- Sync
|
||
summary: Measurements delta feed
|
||
description: "Incremental catch-up after the first-pair backfill (never a replacement for it). Pages over an opaque
|
||
keyset cursor; each page carries `tombstones` (soft-deleted rows, keyed on `externalId`) and `upserts` (live
|
||
rows). Apply tombstones before upserts within a page. `cursorExpired: true` forces a clean re-init."
|
||
parameters:
|
||
- in: query
|
||
name: cursor
|
||
schema:
|
||
description: Opaque multi-domain keyset cursor from the previous page. Treat as fully opaque — echo, never parse. Omit
|
||
for the initial sync.
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 2048
|
||
description: Opaque multi-domain keyset cursor from the previous page. Treat as fully opaque — echo, never parse. Omit
|
||
for the initial sync.
|
||
- in: query
|
||
name: limit
|
||
schema:
|
||
description: Page size, default 200, hard cap 500.
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 500
|
||
description: Page size, default 200, hard cap 500.
|
||
responses:
|
||
"200":
|
||
description: Delta page.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/SyncChangesEnvelope"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/measurements:
|
||
get:
|
||
tags:
|
||
- Measurements
|
||
summary: List measurements
|
||
description: Filter by type + date range. Response is paged via offset/limit; default limit 100, hard cap 500.
|
||
parameters:
|
||
- in: query
|
||
name: type
|
||
schema:
|
||
type: string
|
||
enum:
|
||
- WEIGHT
|
||
- BLOOD_PRESSURE_SYS
|
||
- BLOOD_PRESSURE_DIA
|
||
- PULSE
|
||
- BODY_FAT
|
||
- SLEEP_DURATION
|
||
- ACTIVITY_STEPS
|
||
- BLOOD_GLUCOSE
|
||
- TOTAL_BODY_WATER
|
||
- BONE_MASS
|
||
- OXYGEN_SATURATION
|
||
- HEART_RATE_VARIABILITY
|
||
- RESTING_HEART_RATE
|
||
- ACTIVE_ENERGY_BURNED
|
||
- FLIGHTS_CLIMBED
|
||
- WALKING_RUNNING_DISTANCE
|
||
- VO2_MAX
|
||
- BODY_TEMPERATURE
|
||
- FAT_FREE_MASS
|
||
- FAT_MASS
|
||
- MUSCLE_MASS
|
||
- SKIN_TEMPERATURE
|
||
- PULSE_WAVE_VELOCITY
|
||
- VASCULAR_AGE
|
||
- VISCERAL_FAT
|
||
- AUDIO_EXPOSURE_ENV
|
||
- AUDIO_EXPOSURE_HEADPHONE
|
||
- TIME_IN_DAYLIGHT
|
||
- WALKING_STEADINESS
|
||
- AUDIO_EXPOSURE_EVENT
|
||
- RESPIRATORY_RATE
|
||
- BODY_MASS_INDEX
|
||
- LEAN_BODY_MASS
|
||
- WALKING_HEART_RATE_AVERAGE
|
||
- WALKING_ASYMMETRY
|
||
- WALKING_DOUBLE_SUPPORT
|
||
- WALKING_STEP_LENGTH
|
||
- WALKING_SPEED
|
||
- CARDIO_RECOVERY
|
||
- WRIST_TEMPERATURE
|
||
- FALL_COUNT
|
||
- SIX_MINUTE_WALK_DISTANCE
|
||
- STAIR_ASCENT_SPEED
|
||
- STAIR_DESCENT_SPEED
|
||
- BREATHING_DISTURBANCES
|
||
- IRREGULAR_RHYTHM_NOTIFICATION
|
||
- HIGH_HEART_RATE_EVENT
|
||
- LOW_HEART_RATE_EVENT
|
||
- WALKING_STEADINESS_EVENT
|
||
- BREATHING_DISTURBANCE_EVENT
|
||
- RECOVERY_SCORE
|
||
- STRESS_SCORE
|
||
- STRAIN_SCORE
|
||
- HRV_RMSSD
|
||
- DAY_STRAIN
|
||
- WORKOUT_STRAIN
|
||
- SLEEP_PERFORMANCE
|
||
- SLEEP_EFFICIENCY
|
||
- SLEEP_CONSISTENCY
|
||
- SLEEP_NEED
|
||
- ENERGY_EXPENDITURE_KJ
|
||
- AVERAGE_HEART_RATE
|
||
- MAX_HEART_RATE
|
||
- SLEEP_DISTURBANCE_COUNT
|
||
- in: query
|
||
name: from
|
||
schema:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- in: query
|
||
name: to
|
||
schema:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- in: query
|
||
name: limit
|
||
schema:
|
||
default: 100
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 5000
|
||
- in: query
|
||
name: offset
|
||
schema:
|
||
default: 0
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
- in: query
|
||
name: sortBy
|
||
schema:
|
||
default: measuredAt
|
||
type: string
|
||
enum:
|
||
- type
|
||
- value
|
||
- measuredAt
|
||
- source
|
||
- in: query
|
||
name: sortDir
|
||
schema:
|
||
default: desc
|
||
type: string
|
||
enum:
|
||
- asc
|
||
- desc
|
||
- in: query
|
||
name: aggregate
|
||
schema:
|
||
type: string
|
||
enum:
|
||
- raw
|
||
- daily
|
||
- weekly
|
||
- monthly
|
||
- in: query
|
||
name: source
|
||
schema:
|
||
type: string
|
||
enum:
|
||
- rollup
|
||
- in: query
|
||
name: groupBy
|
||
schema:
|
||
type: string
|
||
enum:
|
||
- day
|
||
- in: query
|
||
name: dayKey
|
||
schema:
|
||
type: string
|
||
pattern: ^\d{4}-\d{2}-\d{2}$
|
||
responses:
|
||
"200":
|
||
description: Measurement page.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ListMeasurementsResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
post:
|
||
tags:
|
||
- Measurements
|
||
summary: Create one measurement
|
||
description: Single ingest. Use `/api/measurements/batch` for Apple Health upload streams.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
type:
|
||
type: string
|
||
enum:
|
||
- WEIGHT
|
||
- BLOOD_PRESSURE_SYS
|
||
- BLOOD_PRESSURE_DIA
|
||
- PULSE
|
||
- BODY_FAT
|
||
- SLEEP_DURATION
|
||
- ACTIVITY_STEPS
|
||
- BLOOD_GLUCOSE
|
||
- TOTAL_BODY_WATER
|
||
- BONE_MASS
|
||
- OXYGEN_SATURATION
|
||
- HEART_RATE_VARIABILITY
|
||
- RESTING_HEART_RATE
|
||
- ACTIVE_ENERGY_BURNED
|
||
- FLIGHTS_CLIMBED
|
||
- WALKING_RUNNING_DISTANCE
|
||
- VO2_MAX
|
||
- BODY_TEMPERATURE
|
||
- FAT_FREE_MASS
|
||
- FAT_MASS
|
||
- MUSCLE_MASS
|
||
- SKIN_TEMPERATURE
|
||
- PULSE_WAVE_VELOCITY
|
||
- VASCULAR_AGE
|
||
- VISCERAL_FAT
|
||
- AUDIO_EXPOSURE_ENV
|
||
- AUDIO_EXPOSURE_HEADPHONE
|
||
- TIME_IN_DAYLIGHT
|
||
- WALKING_STEADINESS
|
||
- AUDIO_EXPOSURE_EVENT
|
||
- RESPIRATORY_RATE
|
||
- BODY_MASS_INDEX
|
||
- LEAN_BODY_MASS
|
||
- WALKING_HEART_RATE_AVERAGE
|
||
- WALKING_ASYMMETRY
|
||
- WALKING_DOUBLE_SUPPORT
|
||
- WALKING_STEP_LENGTH
|
||
- WALKING_SPEED
|
||
- CARDIO_RECOVERY
|
||
- WRIST_TEMPERATURE
|
||
- FALL_COUNT
|
||
- SIX_MINUTE_WALK_DISTANCE
|
||
- STAIR_ASCENT_SPEED
|
||
- STAIR_DESCENT_SPEED
|
||
- BREATHING_DISTURBANCES
|
||
- IRREGULAR_RHYTHM_NOTIFICATION
|
||
- HIGH_HEART_RATE_EVENT
|
||
- LOW_HEART_RATE_EVENT
|
||
- WALKING_STEADINESS_EVENT
|
||
- BREATHING_DISTURBANCE_EVENT
|
||
- RECOVERY_SCORE
|
||
- STRESS_SCORE
|
||
- STRAIN_SCORE
|
||
- HRV_RMSSD
|
||
- DAY_STRAIN
|
||
- WORKOUT_STRAIN
|
||
- SLEEP_PERFORMANCE
|
||
- SLEEP_EFFICIENCY
|
||
- SLEEP_CONSISTENCY
|
||
- SLEEP_NEED
|
||
- ENERGY_EXPENDITURE_KJ
|
||
- AVERAGE_HEART_RATE
|
||
- MAX_HEART_RATE
|
||
- SLEEP_DISTURBANCE_COUNT
|
||
value:
|
||
type: number
|
||
measuredAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
notes:
|
||
type: string
|
||
maxLength: 200
|
||
source:
|
||
default: MANUAL
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- APPLE_HEALTH
|
||
glucoseContext:
|
||
type: string
|
||
enum:
|
||
- FASTING
|
||
- POSTPRANDIAL
|
||
- RANDOM
|
||
- BEDTIME
|
||
deviceType:
|
||
anyOf:
|
||
- type: string
|
||
minLength: 1
|
||
maxLength: 32
|
||
- type: "null"
|
||
required:
|
||
- type
|
||
- value
|
||
- measuredAt
|
||
responses:
|
||
"201":
|
||
description: Created.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CreateMeasurementResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/measurements/batch:
|
||
post:
|
||
tags:
|
||
- Measurements
|
||
summary: Apple Health batch ingest
|
||
description: Up to 500 HealthKit entries per call. Idempotent via the `Idempotency-Key` header (replay window 24h).
|
||
Per-entry status lets the iOS client advance its sync cursor accurately. v1.4.25 W8c adds an optional
|
||
`deviceType` per entry — feed it from `HKDevice.model` so the cross-source canonical picker can break
|
||
Apple-Watch-vs-iPhone ties; null/absent stays backward-compatible with v1.4.23 clients.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AppleHealthBatchRequest"
|
||
responses:
|
||
"200":
|
||
description: Batch processed.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BatchMeasurementsResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/measurements/by-external-ids:
|
||
delete:
|
||
tags:
|
||
- Measurements
|
||
summary: Delete measurements by external ID (iOS deletion-sync)
|
||
description: Removes the user's measurement rows whose externalId is in the request list. Rows owned by another user are
|
||
silently skipped (cross-user 404 guard). Up to 500 externalIds per call.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MeasurementsDeleteByExternalIdsRequest"
|
||
responses:
|
||
"200":
|
||
description: Delete batch processed.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MeasurementsDeleteByExternalIdsResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/sleep/night:
|
||
get:
|
||
tags:
|
||
- Measurements
|
||
summary: Reconstructed sleep night (hypnogram source, v1.11.5)
|
||
description: "Returns one night's reconstructed sleep session for the phase-progression (hypnogram) view: the canonical
|
||
source's stage segments (each with an absolute start/end), the per-stage breakdown, asleep/in-bed/awake totals,
|
||
the mid-sleep awakenings count, and same-wake-day naps surfaced separately. Session-clustered, keyed by the
|
||
local wake day, collapsed to one source via the sleep priority ladder so two sources never overlay. `date`
|
||
omitted returns the most recent night. A read-only view over existing per-stage SLEEP_DURATION rows — no schema,
|
||
no new measurement type."
|
||
parameters:
|
||
- in: query
|
||
name: date
|
||
schema:
|
||
description: Wake-day key (YYYY-MM-DD). Omit for the most recent night.
|
||
type: string
|
||
pattern: ^\d{4}-\d{2}-\d{2}$
|
||
description: Wake-day key (YYYY-MM-DD). Omit for the most recent night.
|
||
responses:
|
||
"200":
|
||
description: Reconstructed night.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/SleepNightResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/workouts:
|
||
get:
|
||
tags:
|
||
- Measurements
|
||
summary: List workouts (v1.4.32)
|
||
description: "Paginated workout list with cross-source canonical-row dedup. The picker reads the per-user
|
||
source-priority ladder and buckets by `(startedAt 5 min slot, sportType)` — twin workouts (Apple Watch +
|
||
Withings ScanWatch) collapse to a single canonical row per cluster. Field names mirror the iOS handoff contract:
|
||
`distanceM`, `activeEnergyKcal`, `avgHr`, `maxHr`."
|
||
parameters:
|
||
- in: query
|
||
name: limit
|
||
schema:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 200
|
||
- in: query
|
||
name: offset
|
||
schema:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
- in: query
|
||
name: since
|
||
schema:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- in: query
|
||
name: until
|
||
schema:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- in: query
|
||
name: sportType
|
||
schema:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: Canonical workout page.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ListWorkoutsResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/workouts/{id}:
|
||
get:
|
||
tags:
|
||
- Measurements
|
||
summary: Workout detail (v1.4.32)
|
||
description: Single-workout envelope. Owns the optional `WorkoutRoute` GeoJSON geometry + `canonicalId` pointer that
|
||
resolves to the cluster winner so deep-links into non-canonical twin rows can redirect cleanly. Cross-user rows
|
||
surface as 404 (existence channel sealed).
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
schema:
|
||
type: string
|
||
required: true
|
||
responses:
|
||
"200":
|
||
description: Workout detail.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/GetWorkoutResponse"
|
||
"401": *a1
|
||
"404":
|
||
description: Workout not found (or owned by another user).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/workouts/batch:
|
||
post:
|
||
tags:
|
||
- Measurements
|
||
summary: Typed workout batch ingest (v1.4.25 W16b)
|
||
description: Server-side ingest endpoint for HKWorkout records (iOS) and Withings activity rows (server-to-server). Up
|
||
to 100 workouts per call; nested route geometry (GeoJSON LineString) is capped at 20 000 points and stored in a
|
||
1:1 `WorkoutRoute` row keyed by `workoutId`. Idempotent via the `Idempotency-Key` header (replay window 24h).
|
||
Per-entry status (`inserted | duplicate | skipped`) lets the iOS sync cursor checkpoint accurately. The request
|
||
body ceiling is 5 MB enforced at the HTTP layer — clients above the ceiling receive a 413 with
|
||
`workout.batch.payload_too_large`.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
workouts:
|
||
minItems: 1
|
||
maxItems: 100
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
sportType:
|
||
type: string
|
||
enum:
|
||
- walking
|
||
- running
|
||
- cycling
|
||
- hiking
|
||
- swimming
|
||
- rowing
|
||
- elliptical
|
||
- stairClimber
|
||
- yoga
|
||
- mindAndBody
|
||
- strength
|
||
- hiit
|
||
- dance
|
||
- golf
|
||
- tennis
|
||
- basketball
|
||
- soccer
|
||
- crossTraining
|
||
- mixedCardio
|
||
- other
|
||
startedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
endedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
totalEnergyKcal:
|
||
type: number
|
||
minimum: 0
|
||
maximum: 50000
|
||
totalDistanceM:
|
||
type: number
|
||
minimum: 0
|
||
maximum: 1000000
|
||
avgHeartRate:
|
||
type: integer
|
||
minimum: 20
|
||
maximum: 300
|
||
maxHeartRate:
|
||
type: integer
|
||
minimum: 20
|
||
maximum: 300
|
||
minHeartRate:
|
||
type: integer
|
||
minimum: 20
|
||
maximum: 300
|
||
stepCount:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 200000
|
||
elevationM:
|
||
type: number
|
||
minimum: -500
|
||
maximum: 10000
|
||
pauseDurationSec:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 86400
|
||
source:
|
||
default: MANUAL
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
externalId:
|
||
type: string
|
||
maxLength: 128
|
||
externalSourceVersion:
|
||
type: string
|
||
maxLength: 64
|
||
metadata:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties: {}
|
||
route:
|
||
type: object
|
||
properties:
|
||
geometry:
|
||
type: object
|
||
properties:
|
||
type:
|
||
type: string
|
||
const: LineString
|
||
coordinates:
|
||
minItems: 2
|
||
maxItems: 20000
|
||
type: array
|
||
items:
|
||
type: array
|
||
prefixItems:
|
||
- type: number
|
||
minimum: -180
|
||
maximum: 180
|
||
- type: number
|
||
minimum: -90
|
||
maximum: 90
|
||
items:
|
||
type: number
|
||
required:
|
||
- type
|
||
- coordinates
|
||
sampleTimestamps:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
t:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
speedMs:
|
||
type: number
|
||
minimum: 0
|
||
maximum: 50
|
||
hr:
|
||
type: integer
|
||
minimum: 20
|
||
maximum: 300
|
||
required:
|
||
- t
|
||
required:
|
||
- geometry
|
||
samples:
|
||
minItems: 1
|
||
maxItems: 30000
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
t:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
hr:
|
||
type: integer
|
||
minimum: 20
|
||
maximum: 300
|
||
speedMs:
|
||
type: number
|
||
minimum: 0
|
||
maximum: 50
|
||
power:
|
||
type: number
|
||
minimum: 0
|
||
maximum: 3000
|
||
cadence:
|
||
type: number
|
||
minimum: 0
|
||
maximum: 400
|
||
required:
|
||
- t
|
||
required:
|
||
- sportType
|
||
- startedAt
|
||
- endedAt
|
||
required:
|
||
- workouts
|
||
responses:
|
||
"200":
|
||
description: Batch processed.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BatchWorkoutsResponse"
|
||
"400":
|
||
description: Batch validation failed (over-cap, schema reject, or oversized route).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"401": *a1
|
||
"413":
|
||
description: Request body exceeds the 5 MB ceiling.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/devices:
|
||
post:
|
||
tags:
|
||
- Devices
|
||
summary: Register native device + APNs token
|
||
description: Idempotent upsert by `token`. APNs token + environment are paired — supplying one without the other returns
|
||
422. Cross-user re-registration of either identifier returns 409.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/DeviceRegisterRequest"
|
||
responses:
|
||
"201":
|
||
description: Device registered or refreshed.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/DeviceRegisterResponse"
|
||
"401": *a1
|
||
"409":
|
||
description: Device or APNs token already registered to another user.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/medications:
|
||
get:
|
||
tags:
|
||
- Medications
|
||
summary: List medications for the calling user
|
||
description: Returns every medication owned by the caller (active + paused), ordered by `createdAt DESC`. Each row
|
||
carries its nested `schedules`, the joined clinical `category`, the latest non-skipped `lastTakenAt`, and the
|
||
count of intake events scheduled for today (`todayEventCount`). The response is cached server-side for 60 s per
|
||
user; writes flush the cache.
|
||
responses:
|
||
"200":
|
||
description: Medication list.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ListMedicationsResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
post:
|
||
tags:
|
||
- Medications
|
||
summary: Create a medication with at least one schedule
|
||
description: Validates the body against `CreateMedicationRequest`, applies the v1.5 cross-field invariants (one-shot
|
||
consistency, recurring default `FREQ=DAILY`, `timesOfDay` dual-write), and creates the medication + its
|
||
schedules in a single Prisma write. Audits as `medication.create`.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CreateMedicationRequest"
|
||
responses:
|
||
"201":
|
||
description: Created medication with its schedules.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CreateMedicationResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/medications/{id}:
|
||
get:
|
||
tags:
|
||
- Medications
|
||
summary: Fetch a single medication
|
||
description: Returns the medication + its schedules + the joined `category`. Cross-user rows surface as 404 (existence
|
||
channel sealed).
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
schema:
|
||
type: string
|
||
required: true
|
||
responses:
|
||
"200":
|
||
description: Medication detail.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/GetMedicationResponse"
|
||
"401": *a1
|
||
"404":
|
||
description: Medication not found (or owned by another user).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"422": *a2
|
||
"429": *a3
|
||
put:
|
||
tags:
|
||
- Medications
|
||
summary: Replace a medication (partial fields)
|
||
description: Every field on the body is optional; omitted fields are left untouched. Supplying `schedules` REPLACES the
|
||
medication's full schedule list (the route deletes existing rows before re-creating). Flipping `active` to false
|
||
stamps `pausedAt`; flipping back to true clears it. v1.5 invariants on the `schedules` array match `POST
|
||
/api/medications`. Audits as `medication.update`.
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
schema:
|
||
type: string
|
||
required: true
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/UpdateMedicationRequest"
|
||
responses:
|
||
"200":
|
||
description: Updated medication.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/UpdateMedicationResponse"
|
||
"401": *a1
|
||
"404":
|
||
description: Medication not found (or owned by another user).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"422": *a2
|
||
"429": *a3
|
||
delete:
|
||
tags:
|
||
- Medications
|
||
summary: Delete a medication
|
||
description: Cascades to the medication's schedules, intake events, dose changes, inventory rows, and side-effect logs.
|
||
Revokes every API token scoped to `medication:<id>:ingest`. Audits as `medication.delete`.
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
schema:
|
||
type: string
|
||
required: true
|
||
responses:
|
||
"200":
|
||
description: Deletion succeeded.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/DeleteMedicationResponse"
|
||
"401": *a1
|
||
"404":
|
||
description: Medication not found (or owned by another user).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/medications/{id}/intake:
|
||
post:
|
||
tags:
|
||
- Medications
|
||
summary: Log an intake event for a medication
|
||
description: Records a taken or skipped dose. Idempotent via the `Idempotency-Key` header AND the optional
|
||
`idempotencyKey` body field (the route walks both paths); a re-post inside the 60 s server-side dedup window
|
||
returns the original event. Non-skipped intakes auto-decrement pen inventory (best-effort), refresh the per-day
|
||
compliance rollup, and — for `oneShot:true` medications — flip `active` to false.
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
schema:
|
||
type: string
|
||
required: true
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MedicationIntakeRequest"
|
||
responses:
|
||
"200":
|
||
description: Idempotent replay — the original event is returned without creating a new row.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ReplayMedicationIntakeResponse"
|
||
"201":
|
||
description: Intake event created.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CreateMedicationIntakeResponse"
|
||
"401": *a1
|
||
"404":
|
||
description: Medication not found (or owned by another user).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/medications/extract:
|
||
post:
|
||
tags:
|
||
- Medications
|
||
summary: Extract scheduling fields from a free-text medication description
|
||
description: Runs the user's free-text description through the Coach provider chain and returns a citation-guarded
|
||
partial payload the wizard merges onto whatever the user already typed. `name` and `dose` are dropped when not
|
||
substring-matched in the original text so the wizard cannot land a hallucinated brand or dose. `cadenceKind` /
|
||
`doseUnit` / `weekdays` are closed enums; numeric fields are clamped. Rate-limited 10 requests / 5 minutes /
|
||
user, gated against the daily Coach token budget.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MedicationExtractRequest"
|
||
responses:
|
||
"200":
|
||
description: Citation-guarded partial extraction.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MedicationExtractResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
"502":
|
||
description: Upstream provider returned an empty, unparseable, or off-schema reply.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"503":
|
||
description: No AI provider configured for the calling user (or operator).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
/api/medications/{id}/cadence:
|
||
get:
|
||
tags:
|
||
- Medications
|
||
summary: Cadence + compliance read for a medication
|
||
description: Returns the expected-vs-actual dose timeline for the requested window plus the four compliance chip values
|
||
that drive the detail-page section. Pure computation — no writes. Day boundaries are resolved in the user's IANA
|
||
timezone so a Tokyo user and a Berlin user see the same chips for the same medication. The `days` query
|
||
parameter caps at 180.
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
schema:
|
||
type: string
|
||
required: true
|
||
- in: query
|
||
name: days
|
||
schema:
|
||
description: Window size in days (default 30, max 180).
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 180
|
||
description: Window size in days (default 30, max 180).
|
||
responses:
|
||
"200":
|
||
description: Cadence response.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/GetMedicationCadenceResponse"
|
||
"401": *a1
|
||
"404":
|
||
description: Medication not found (or owned by another user).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/medications/{id}/compliance:
|
||
get:
|
||
tags:
|
||
- Medications
|
||
summary: Adherence read for a medication
|
||
description: Returns the 7- and 30-day adherence summaries, the per-day compliance grid for the history glyph track, and
|
||
the two-row display block. Pure computation — no writes. Day boundaries are resolved in the user's IANA
|
||
timezone, and the expected-dose denominator is cadence-aware (RRULE / rolling / one-shot / PRN / cyclic) and
|
||
clamped to the medication's `createdAt`. Read `compliance30` for the headline 30-day taken-vs-expected
|
||
percentage; build the per-day glyph track from `dailyCompliance` (draw a cell only where `due === true`).
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
schema:
|
||
type: string
|
||
required: true
|
||
responses:
|
||
"200":
|
||
description: Compliance response.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/GetMedicationComplianceResponse"
|
||
"401": *a1
|
||
"404":
|
||
description: Medication not found (or owned by another user).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/auth/me/coach-prefs:
|
||
get:
|
||
tags:
|
||
- Auth
|
||
summary: Read per-user Coach prompt-tuning preferences
|
||
description: Returns the persisted preferences with defaults filled in. Null persisted state resolves to the legacy
|
||
v1.4.22 defaults.
|
||
responses:
|
||
"200":
|
||
description: Resolved preferences.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/GetCoachPrefsResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
put:
|
||
tags:
|
||
- Auth
|
||
summary: Replace per-user Coach prompt-tuning preferences
|
||
description: Persists the supplied prefs. Body is validated against `CoachPrefs`; missing keys fall back to the
|
||
documented defaults.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
tone:
|
||
default: warm
|
||
type: string
|
||
enum:
|
||
- warm
|
||
- neutral
|
||
- concise
|
||
verbosity:
|
||
default: default
|
||
type: string
|
||
enum:
|
||
- brief
|
||
- default
|
||
- detailed
|
||
excludeMetrics:
|
||
default: []
|
||
maxItems: 11
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- bp
|
||
- weight
|
||
- pulse
|
||
- mood
|
||
- compliance
|
||
- hrv
|
||
- sleep
|
||
- resting_hr
|
||
- steps
|
||
- medications
|
||
- anthropometrics
|
||
showEvidenceByDefault:
|
||
default: false
|
||
type: boolean
|
||
defaultWindow:
|
||
default: allTime
|
||
type: string
|
||
enum:
|
||
- last7days
|
||
- last30days
|
||
- last90days
|
||
- allTime
|
||
dataClusters:
|
||
maxItems: 10
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- cardio
|
||
- body
|
||
- activity
|
||
- workouts
|
||
- sleep
|
||
- mood
|
||
- glucose
|
||
- medication
|
||
- mobility
|
||
- environment
|
||
responses:
|
||
"200":
|
||
description: Saved preferences echoed back.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PutCoachPrefsResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/auth/me/disable-coach:
|
||
get:
|
||
tags:
|
||
- Auth
|
||
summary: Read per-user Coach opt-out flag
|
||
description: Returns the user's per-account Coach opt-out flag. Default `false` (Coach visible). Powers the Settings →
|
||
Insights "Hide Coach" Switch and the layout FAB short-circuit.
|
||
responses:
|
||
"200":
|
||
description: Resolved flag.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/GetDisableCoachResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
patch:
|
||
tags:
|
||
- Auth
|
||
summary: Toggle per-user Coach opt-out flag
|
||
description: Flips the per-account Coach opt-out flag. Idempotent — the DB write fires even when the value matches so
|
||
the audit-log row mirrors the API call. Rate-limit 60/min per user.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/DisableCoachFlag"
|
||
responses:
|
||
"200":
|
||
description: Resolved next-state echoed back for optimistic-update consumers.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PatchDisableCoachResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/auth/me/source-priority:
|
||
get:
|
||
tags:
|
||
- Auth
|
||
summary: Read per-user source priority
|
||
description: Returns the per-metric-class source priority used by the analytics aggregator. Missing keys fall back to
|
||
`DEFAULT_SOURCE_PRIORITY`. Null persisted state resolves to the documented defaults.
|
||
responses:
|
||
"200":
|
||
description: Resolved priority.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/GetSourcePriorityResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
put:
|
||
tags:
|
||
- Auth
|
||
summary: Replace per-user source priority
|
||
description: Persists the supplied priority. Body is validated against `SourcePriority`; missing keys read as
|
||
`DEFAULT_SOURCE_PRIORITY` at the call site.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
steps:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
activeEnergy:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
walkingRunningDistance:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
flightsClimbed:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
sleep:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
weight:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bloodPressure:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
pulse:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bodyFat:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bodyTemperature:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
spo2:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
hrv:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
restingHeartRate:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
vo2Max:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
skinTemperature:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
respiratoryRate:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
recovery:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
metricPriority:
|
||
type: object
|
||
properties:
|
||
steps:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
activeEnergy:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
walkingRunningDistance:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
flightsClimbed:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
sleep:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
weight:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bloodPressure:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
pulse:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bodyFat:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bodyTemperature:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
spo2:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
hrv:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
restingHeartRate:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
vo2Max:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
skinTemperature:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
respiratoryRate:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
recovery:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
deviceTypePriority:
|
||
type: object
|
||
properties:
|
||
default:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- watch
|
||
- band
|
||
- ring
|
||
- phone
|
||
- scale
|
||
- other
|
||
- unknown
|
||
additionalProperties:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- watch
|
||
- band
|
||
- ring
|
||
- phone
|
||
- scale
|
||
- other
|
||
- unknown
|
||
responses:
|
||
"200":
|
||
description: Saved priority (defaulted) echoed back.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PutSourcePriorityResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/user/avatar:
|
||
post:
|
||
tags:
|
||
- Auth
|
||
summary: Upload the calling user's avatar
|
||
description: "Multipart upload (field `file`). Accepts image/jpeg, image/png, image/webp; rejects anything else at the
|
||
magic-byte sniff (the multipart Content-Type header is informational only). Hard caps: 2 MiB body, 2048×2048
|
||
dimensions. Replaces the v1.4.22 Gravatar leak by storing the bytes on the User row + serving them from
|
||
same-origin."
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
multipart/form-data:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
file:
|
||
type: string
|
||
description: Binary image payload (JPEG / PNG / WebP). Hard-capped at 2 MiB and 2048×2048.
|
||
required:
|
||
- file
|
||
responses:
|
||
"201":
|
||
description: Avatar saved. `avatarUrl` is the same value the /me payload returns; the `?v=` suffix busts client caches
|
||
on re-upload.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/UploadAvatarResponse"
|
||
"401": *a1
|
||
"413":
|
||
description: Upload exceeds the 2 MiB byte limit or the 2048×2048 dimension limit.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"415":
|
||
description: Image format is not one of JPEG / PNG / WebP (magic-byte sniff).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"422": *a2
|
||
"429": *a3
|
||
delete:
|
||
tags:
|
||
- Auth
|
||
summary: Clear the calling user's avatar
|
||
description: Resets the avatar columns to null. Idempotent — a delete on an already-empty row returns 204 with no
|
||
audit-log row.
|
||
responses:
|
||
"204":
|
||
description: Avatar cleared (or already empty).
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/user/avatar/{id}:
|
||
get:
|
||
tags:
|
||
- Auth
|
||
summary: Serve a user's avatar bytes
|
||
description: Owner-scoped. The `{id}` path segment must match the calling user's id — cross-user reads return 403.
|
||
Response body is the raw image bytes with the persisted `Content-Type`; the /me payload appends
|
||
`?v={updatedAtMs}` so clients can cache aggressively.
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
schema:
|
||
type: string
|
||
description: User id (must match the calling user).
|
||
required: true
|
||
description: User id (must match the calling user).
|
||
responses:
|
||
"200":
|
||
description: Avatar bytes.
|
||
content:
|
||
image/jpeg:
|
||
schema:
|
||
type: string
|
||
description: Raw JPEG bytes.
|
||
image/png:
|
||
schema:
|
||
type: string
|
||
description: Raw PNG bytes.
|
||
image/webp:
|
||
schema:
|
||
type: string
|
||
description: Raw WebP bytes.
|
||
"401": *a1
|
||
"403":
|
||
description: Caller is not the avatar's owner.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"404":
|
||
description: The user has no uploaded avatar.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/user/profile:
|
||
get:
|
||
tags:
|
||
- Auth
|
||
summary: Read the calling user's profile
|
||
description: Flattened profile fields for the native client. Aliased over the same data exposed by /api/auth/me. The
|
||
KVNR is decrypted server-side; the IKNR (v1.8.6) is returned plaintext.
|
||
responses:
|
||
"200":
|
||
description: The calling user's profile.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/GetProfileResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
patch:
|
||
tags:
|
||
- Auth
|
||
summary: Update the calling user's profile
|
||
description: Partial profile update — every field is optional. An explicit null (or empty string) clears a field. A
|
||
non-empty `insurerIkNumber` must be exactly 9 digits (422 otherwise); a non-empty `insuranceNumber` (KVNR) must
|
||
pass the mod-10 check. `userId` is never accepted from the body.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ProfileUpdateRequest"
|
||
responses:
|
||
"200":
|
||
description: Saved profile echoed back.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PatchProfileResponse"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/insights/layout:
|
||
get:
|
||
tags:
|
||
- Insights
|
||
summary: Read the calling user's Insights tile layout
|
||
description: Returns the per-user Insights tile layout (visibility + order). Falls back to the default layout when the
|
||
user has not customised it. Mirrors the dashboard-widgets contract.
|
||
responses:
|
||
"200":
|
||
description: The resolved layout (custom or default).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InsightsLayout"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
put:
|
||
tags:
|
||
- Insights
|
||
summary: Replace the calling user's Insights tile layout
|
||
description: Persists the full tile layout. The normalised layout is returned. Invalid bodies return the multi-issue 422
|
||
envelope, matching the dashboard-widgets route.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InsightsLayoutBody"
|
||
responses:
|
||
"200":
|
||
description: Layout saved; the normalised layout is echoed back.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InsightsLayoutSaved"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
delete:
|
||
tags:
|
||
- Insights
|
||
summary: Reset the calling user's Insights tile layout
|
||
description: Clears the persisted layout and returns the default layout. Idempotent.
|
||
responses:
|
||
"200":
|
||
description: Layout reset; the default layout is returned.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InsightsLayoutReset"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/insights/coach/facts:
|
||
get:
|
||
tags:
|
||
- Insights
|
||
summary: List the caller's durable Coach facts
|
||
description: v1.11.1 — returns the active facts the Coach has extracted about the caller (highest-confidence then newest
|
||
first), each decrypted on the fly. The GDPR 'what do you know about me' surface. Coach-gated
|
||
(`requireAssistantSurface("coach")`). Auth via cookie or Bearer; the owner is always narrowed from the session,
|
||
never the body. Undecryptable rows are omitted rather than failing the read.
|
||
responses:
|
||
"200":
|
||
description: The caller's active facts.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CoachFactsList"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
delete:
|
||
tags:
|
||
- Insights
|
||
summary: Forget all of the caller's Coach facts
|
||
description: "v1.11.1 — bulk 'forget what you know about me': soft-deletes every active fact for the caller and returns
|
||
the count cleared. Idempotent (a second call clears 0). Coach-gated. Auth via cookie or Bearer."
|
||
responses:
|
||
"200":
|
||
description: All active facts cleared; the count is returned.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CoachFactsCleared"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/insights/coach/facts/{id}:
|
||
delete:
|
||
tags:
|
||
- Insights
|
||
summary: Forget one Coach fact
|
||
description: "v1.11.1 — soft-deletes a single fact owned by the caller. An unknown / cross-user / already-deleted id is
|
||
an idempotent no-op returning `{ deleted: false }`, never revealing whether the id exists under another account.
|
||
Coach-gated. Auth via cookie or Bearer."
|
||
responses:
|
||
"200":
|
||
description: "The fact was soft-deleted (`deleted: true`) or the id matched nothing the caller owns (`deleted: false`)."
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CoachFactDeleted"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/insights/chat/messages/{id}/feedback:
|
||
post:
|
||
tags:
|
||
- Insights
|
||
summary: Rate a Coach assistant message
|
||
description: Persists a helpful/unhelpful rating for a single Coach reply. Reuses the v1.4.16 RecommendationFeedback
|
||
table via the polymorphic `targetType` column. The aggregator buckets ratings by (promptVersion, tone,
|
||
verbosity).
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CoachMessageFeedbackRequest"
|
||
responses:
|
||
"201":
|
||
description: Feedback saved.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CoachMessageFeedbackResponse"
|
||
"401": *a1
|
||
"404":
|
||
description: Message not found or not owned by the caller.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"409":
|
||
description: Caller has already rated this message text.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/admin/notifications/diagnostic:
|
||
get:
|
||
tags:
|
||
- Admin
|
||
summary: Admin notification diagnostic snapshot
|
||
description: "Admin-only. Returns what the dispatcher would see when targeting the calling admin's account: registered
|
||
devices (APNs tokens masked to prefix + suffix), per-channel enabled + configPresent flags, and recent push
|
||
attempts (currently always `[]` pending the v1.4.48+ PushAttempt table). Cookie auth only — never Bearer."
|
||
responses:
|
||
"200":
|
||
description: Diagnostic snapshot.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AdminDiagnosticResponse"
|
||
"401": *a1
|
||
"403":
|
||
description: Caller is not an admin.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorEnvelope"
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/dashboard/snapshot:
|
||
get:
|
||
tags:
|
||
- Dashboard
|
||
summary: Unified dashboard first-paint snapshot
|
||
description: "Assembles every above-the-fold tile field in one round-trip from the rollup / mood / widget helpers plus a
|
||
read-only lift of the pre-generated daily briefing. Two-phase: `tiles` always present, `extras` nullable on a
|
||
rollup-coverage miss. No LLM is reachable from this path. Cookie or Bearer auth."
|
||
responses:
|
||
"200":
|
||
description: Dashboard snapshot.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/DashboardSnapshotResponseEnvelope"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/insights/comprehensive:
|
||
get:
|
||
tags:
|
||
- Insights
|
||
summary: Comprehensive AI insights bundle
|
||
description: Full Insights surface — daily briefing, recommendations with rationale, optional weekly report + storyboard
|
||
annotations. Strict-schema validated server-side.
|
||
responses:
|
||
"200":
|
||
description: Insights bundle.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InsightsComprehensiveResponseEnvelope"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/insights/pregenerate:
|
||
post:
|
||
tags:
|
||
- Insights
|
||
summary: Warm all AI assessments for the calling user
|
||
description: v1.8.7.1 — enqueue a full warm of every AI assessment for the authenticated user (comprehensive insight +
|
||
the seven specialised status cards + every data-bearing generic metric assessment) in the active locale, so the
|
||
read-only status GETs serve cached text instantly. Returns immediately; the generation runs out of band on the
|
||
worker. Empty metrics and provider-less accounts never trigger an LLM call. Short anti-spam bucket
|
||
(`insights-warm:<userId>`, one warm per 3 minutes) → 429 on a tight loop. Auth via cookie or Bearer; `userId` is
|
||
taken from the session, never the body.
|
||
requestBody:
|
||
required: false
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InsightsPregenerateRequest"
|
||
responses:
|
||
"200":
|
||
description: Warm accepted and enqueued. The work runs on the worker; poll the read-only status routes for the text.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InsightsPregenerateResponseEnvelope"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/analytics/range:
|
||
get:
|
||
tags:
|
||
- Analytics
|
||
summary: Single-metric period-over-period range delta
|
||
description: v1.9.0 — returns the current-window aggregate, the previous comparable window, and the composed delta for
|
||
ONE metric type over a `7d` / `30d` / `90d` / `1y` range. Single-type by construction (the metric page is
|
||
single-metric), so the read is one rollup-tier call covering the trailing 2N days sliced into the two halves —
|
||
no per-type fan-out. Additive route; the `/api/analytics` envelope is unchanged. Auth via cookie or Bearer.
|
||
parameters:
|
||
- in: query
|
||
name: type
|
||
schema:
|
||
type: string
|
||
enum:
|
||
- WEIGHT
|
||
- BLOOD_PRESSURE_SYS
|
||
- BLOOD_PRESSURE_DIA
|
||
- PULSE
|
||
- BODY_FAT
|
||
- SLEEP_DURATION
|
||
- ACTIVITY_STEPS
|
||
- BLOOD_GLUCOSE
|
||
- TOTAL_BODY_WATER
|
||
- BONE_MASS
|
||
- OXYGEN_SATURATION
|
||
- HEART_RATE_VARIABILITY
|
||
- RESTING_HEART_RATE
|
||
- ACTIVE_ENERGY_BURNED
|
||
- FLIGHTS_CLIMBED
|
||
- WALKING_RUNNING_DISTANCE
|
||
- VO2_MAX
|
||
- BODY_TEMPERATURE
|
||
- FAT_FREE_MASS
|
||
- FAT_MASS
|
||
- MUSCLE_MASS
|
||
- SKIN_TEMPERATURE
|
||
- PULSE_WAVE_VELOCITY
|
||
- VASCULAR_AGE
|
||
- VISCERAL_FAT
|
||
- AUDIO_EXPOSURE_ENV
|
||
- AUDIO_EXPOSURE_HEADPHONE
|
||
- TIME_IN_DAYLIGHT
|
||
- WALKING_STEADINESS
|
||
- AUDIO_EXPOSURE_EVENT
|
||
- RESPIRATORY_RATE
|
||
- BODY_MASS_INDEX
|
||
- LEAN_BODY_MASS
|
||
- WALKING_HEART_RATE_AVERAGE
|
||
- WALKING_ASYMMETRY
|
||
- WALKING_DOUBLE_SUPPORT
|
||
- WALKING_STEP_LENGTH
|
||
- WALKING_SPEED
|
||
- CARDIO_RECOVERY
|
||
- WRIST_TEMPERATURE
|
||
- FALL_COUNT
|
||
- SIX_MINUTE_WALK_DISTANCE
|
||
- STAIR_ASCENT_SPEED
|
||
- STAIR_DESCENT_SPEED
|
||
- BREATHING_DISTURBANCES
|
||
- IRREGULAR_RHYTHM_NOTIFICATION
|
||
- HIGH_HEART_RATE_EVENT
|
||
- LOW_HEART_RATE_EVENT
|
||
- WALKING_STEADINESS_EVENT
|
||
- BREATHING_DISTURBANCE_EVENT
|
||
- RECOVERY_SCORE
|
||
- STRESS_SCORE
|
||
- STRAIN_SCORE
|
||
- HRV_RMSSD
|
||
- DAY_STRAIN
|
||
- WORKOUT_STRAIN
|
||
- SLEEP_PERFORMANCE
|
||
- SLEEP_EFFICIENCY
|
||
- SLEEP_CONSISTENCY
|
||
- SLEEP_NEED
|
||
- ENERGY_EXPENDITURE_KJ
|
||
- AVERAGE_HEART_RATE
|
||
- MAX_HEART_RATE
|
||
- SLEEP_DISTURBANCE_COUNT
|
||
description: "The measurement type to read (single metric — no fan-out). Closed enum: an unknown type 422s."
|
||
required: true
|
||
description: "The measurement type to read (single metric — no fan-out). Closed enum: an unknown type 422s."
|
||
- in: query
|
||
name: range
|
||
schema:
|
||
type: string
|
||
enum:
|
||
- 7d
|
||
- 30d
|
||
- 90d
|
||
- 1y
|
||
description: "Trailing window: `7d` / `30d` / `90d` / `1y`. The previous comparable window is the equally-sized span
|
||
immediately before it."
|
||
required: true
|
||
description: "Trailing window: `7d` / `30d` / `90d` / `1y`. The previous comparable window is the equally-sized span
|
||
immediately before it."
|
||
responses:
|
||
"200":
|
||
description: Current + previous window aggregates and the delta.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AnalyticsRangeResponseEnvelope"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/insights/blood-pressure-status:
|
||
get:
|
||
tags:
|
||
- Insights
|
||
summary: Blood-pressure assessment
|
||
description: "Data-driven plain-language assessment of the user's recent blood-pressure readings. Read-only: a cache
|
||
miss warms a generation out of band and serves the last-good text meanwhile (stale-while-revalidate). Auth via
|
||
cookie or Bearer."
|
||
parameters:
|
||
- in: query
|
||
name: locale
|
||
schema:
|
||
description: Optional UI-locale override; defaults to the session locale.
|
||
type: string
|
||
enum:
|
||
- de
|
||
- en
|
||
description: Optional UI-locale override; defaults to the session locale.
|
||
responses:
|
||
"200":
|
||
description: Assessment envelope (fresh, cached, or preparing).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BloodPressureStatusResponseEnvelope"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/insights/pulse-status:
|
||
get:
|
||
tags:
|
||
- Insights
|
||
summary: Pulse assessment
|
||
description: "Data-driven plain-language assessment of the user's recent resting-pulse readings. Read-only: a cache miss
|
||
warms a generation out of band and serves the last-good text meanwhile (stale-while-revalidate). Auth via cookie
|
||
or Bearer."
|
||
parameters:
|
||
- in: query
|
||
name: locale
|
||
schema:
|
||
description: Optional UI-locale override; defaults to the session locale.
|
||
type: string
|
||
enum:
|
||
- de
|
||
- en
|
||
description: Optional UI-locale override; defaults to the session locale.
|
||
responses:
|
||
"200":
|
||
description: Assessment envelope (fresh, cached, or preparing).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PulseStatusResponseEnvelope"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/insights/weight-status:
|
||
get:
|
||
tags:
|
||
- Insights
|
||
summary: Weight assessment
|
||
description: "Data-driven plain-language assessment of the user's recent weight trend. Read-only: a cache miss warms a
|
||
generation out of band and serves the last-good text meanwhile (stale-while-revalidate). Auth via cookie or
|
||
Bearer."
|
||
parameters:
|
||
- in: query
|
||
name: locale
|
||
schema:
|
||
description: Optional UI-locale override; defaults to the session locale.
|
||
type: string
|
||
enum:
|
||
- de
|
||
- en
|
||
description: Optional UI-locale override; defaults to the session locale.
|
||
responses:
|
||
"200":
|
||
description: Assessment envelope (fresh, cached, or preparing).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/WeightStatusResponseEnvelope"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/insights/bmi-status:
|
||
get:
|
||
tags:
|
||
- Insights
|
||
summary: BMI assessment
|
||
description: "Data-driven plain-language assessment of the user's body-mass index. Read-only: a cache miss warms a
|
||
generation out of band and serves the last-good text meanwhile (stale-while-revalidate). Auth via cookie or
|
||
Bearer."
|
||
parameters:
|
||
- in: query
|
||
name: locale
|
||
schema:
|
||
description: Optional UI-locale override; defaults to the session locale.
|
||
type: string
|
||
enum:
|
||
- de
|
||
- en
|
||
description: Optional UI-locale override; defaults to the session locale.
|
||
responses:
|
||
"200":
|
||
description: Assessment envelope (fresh, cached, or preparing).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BmiStatusResponseEnvelope"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/insights/mood-status:
|
||
get:
|
||
tags:
|
||
- Insights
|
||
summary: Mood assessment
|
||
description: "Data-driven plain-language assessment of the user's recent mood entries. Read-only: a cache miss warms a
|
||
generation out of band and serves the last-good text meanwhile (stale-while-revalidate). Auth via cookie or
|
||
Bearer."
|
||
parameters:
|
||
- in: query
|
||
name: locale
|
||
schema:
|
||
description: Optional UI-locale override; defaults to the session locale.
|
||
type: string
|
||
enum:
|
||
- de
|
||
- en
|
||
description: Optional UI-locale override; defaults to the session locale.
|
||
responses:
|
||
"200":
|
||
description: Assessment envelope (fresh, cached, or preparing).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MoodStatusResponseEnvelope"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/insights/medication-compliance-status:
|
||
get:
|
||
tags:
|
||
- Insights
|
||
summary: Medication-compliance assessment
|
||
description: "Data-driven plain-language assessment of the user's medication compliance — an overall `summary` plus a
|
||
per-medication note array. Read-only: a cache miss warms a generation out of band and serves the last-good
|
||
envelope meanwhile (stale-while-revalidate). Auth via cookie or Bearer."
|
||
parameters:
|
||
- in: query
|
||
name: locale
|
||
schema:
|
||
description: Optional UI-locale override; defaults to the session locale.
|
||
type: string
|
||
enum:
|
||
- de
|
||
- en
|
||
description: Optional UI-locale override; defaults to the session locale.
|
||
responses:
|
||
"200":
|
||
description: Compliance assessment envelope (fresh, cached, or preparing).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MedicationComplianceStatusResponseEnvelope"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/insights/metric-status:
|
||
get:
|
||
tags:
|
||
- Insights
|
||
summary: Generic per-HealthKit-metric assessment
|
||
description: "v1.8.7.1 — data-driven plain-language assessment for any registered HealthKit metric (resting heart rate,
|
||
sleep, glucose, body composition, gait, audio exposure, …). One generic route covering ~30 metric pages via
|
||
archetype prompt templates + per-metric metadata. Read-only: a cache miss warms a generation out of band and
|
||
serves the last-good text meanwhile (stale-while-revalidate). An unknown `metric` 422s against the closed
|
||
registry enum. Auth via cookie or Bearer."
|
||
parameters:
|
||
- in: query
|
||
name: metric
|
||
schema:
|
||
type: string
|
||
enum:
|
||
- RESTING_HEART_RATE
|
||
- HEART_RATE_VARIABILITY
|
||
- OXYGEN_SATURATION
|
||
- RESPIRATORY_RATE
|
||
- BODY_TEMPERATURE
|
||
- SKIN_TEMPERATURE
|
||
- BLOOD_GLUCOSE
|
||
- WALKING_HEART_RATE_AVERAGE
|
||
- PULSE_WAVE_VELOCITY
|
||
- VASCULAR_AGE
|
||
- STEPS
|
||
- ACTIVE_ENERGY
|
||
- FLIGHTS_CLIMBED
|
||
- WALKING_RUNNING_DISTANCE
|
||
- TIME_IN_DAYLIGHT
|
||
- VO2_MAX
|
||
- TOTAL_BODY_WATER
|
||
- BONE_MASS
|
||
- FAT_FREE_MASS
|
||
- FAT_MASS
|
||
- MUSCLE_MASS
|
||
- LEAN_BODY_MASS
|
||
- VISCERAL_FAT
|
||
- WALKING_STEADINESS
|
||
- WALKING_ASYMMETRY
|
||
- WALKING_DOUBLE_SUPPORT
|
||
- WALKING_STEP_LENGTH
|
||
- WALKING_SPEED
|
||
- AUDIO_EXPOSURE_ENV
|
||
- AUDIO_EXPOSURE_HEADPHONE
|
||
- AUDIO_EXPOSURE_EVENT
|
||
- SLEEP_DURATION
|
||
- CARDIO_RECOVERY
|
||
- WRIST_TEMPERATURE
|
||
- FALL_COUNT
|
||
- SIX_MINUTE_WALK_DISTANCE
|
||
- STAIR_ASCENT_SPEED
|
||
- STAIR_DESCENT_SPEED
|
||
- BREATHING_DISTURBANCES
|
||
description: "HealthKit metric id to assess (e.g. RESTING_HEART_RATE, SLEEP_DURATION). Closed enum: an unknown id 422s.
|
||
The seven specialised metrics are served by their own routes and are not accepted here."
|
||
required: true
|
||
description: "HealthKit metric id to assess (e.g. RESTING_HEART_RATE, SLEEP_DURATION). Closed enum: an unknown id 422s.
|
||
The seven specialised metrics are served by their own routes and are not accepted here."
|
||
- in: query
|
||
name: locale
|
||
schema:
|
||
description: Optional UI-locale override; defaults to the session locale.
|
||
type: string
|
||
enum:
|
||
- de
|
||
- en
|
||
description: Optional UI-locale override; defaults to the session locale.
|
||
responses:
|
||
"200":
|
||
description: Assessment envelope (fresh, cached, or preparing).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MetricStatusResponseEnvelope"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/insights/derived:
|
||
get:
|
||
tags:
|
||
- Insights
|
||
summary: Derived wellness metric (compute-once)
|
||
description: v1.10.0 — the compute-once `Derived<T>` value for any registered derived wellness metric (personal
|
||
typical-range vitals baseline, cardio-fitness band, vascular-age delta, sleep score, readiness,
|
||
coincident-deviation flag). One generic route over a closed registry enum; an unknown `metric` 422s. Pure
|
||
compute over the rollup tier with a per-type live fallback on a coverage miss — no LLM call, no narrative, no
|
||
cache table. Returns the flat `Derived<T>` union so the native client can decode one stable shape and combine
|
||
values across metrics. Auth via cookie or Bearer.
|
||
parameters:
|
||
- in: query
|
||
name: metric
|
||
schema:
|
||
type: string
|
||
enum:
|
||
- VITALS_BASELINE
|
||
- FITNESS_AGE
|
||
- VASCULAR_AGE_DELTA
|
||
- SLEEP_SCORE
|
||
- READINESS
|
||
- COINCIDENT_DEVIATION
|
||
- HRV_BALANCE
|
||
- BMI
|
||
- RECOVERY_SCORE
|
||
- STRESS_SCORE
|
||
- STRAIN_SCORE
|
||
- WRIST_TEMPERATURE_BASELINE
|
||
- STAIR_ASCENT_SPEED_BASELINE
|
||
- STAIR_DESCENT_SPEED_BASELINE
|
||
- SIX_MINUTE_WALK_BAND
|
||
- TRAJECTORY
|
||
description: "Derived-metric id to compute (e.g. VITALS_BASELINE, FITNESS_AGE, VASCULAR_AGE_DELTA, HRV_BALANCE, BMI,
|
||
READINESS). Closed enum: an unknown id 422s. Metrics whose compute has not yet landed return an
|
||
`insufficient` value with reason `not_implemented`."
|
||
required: true
|
||
description: "Derived-metric id to compute (e.g. VITALS_BASELINE, FITNESS_AGE, VASCULAR_AGE_DELTA, HRV_BALANCE, BMI,
|
||
READINESS). Closed enum: an unknown id 422s. Metrics whose compute has not yet landed return an
|
||
`insufficient` value with reason `not_implemented`."
|
||
- in: query
|
||
name: type
|
||
schema:
|
||
description: For VITALS_BASELINE only — the single vital to band (defaults to RESTING_HEART_RATE). Ignored by
|
||
composites. An unsupported value yields an `insufficient` value rather than a 422 so iOS metric
|
||
combinations stay forgiving.
|
||
type: string
|
||
enum:
|
||
- RESTING_HEART_RATE
|
||
- HEART_RATE_VARIABILITY
|
||
- RESPIRATORY_RATE
|
||
- OXYGEN_SATURATION
|
||
- BODY_TEMPERATURE
|
||
- SKIN_TEMPERATURE
|
||
- BLOOD_PRESSURE_SYS
|
||
- BLOOD_PRESSURE_DIA
|
||
- BLOOD_GLUCOSE
|
||
- PULSE
|
||
- WEIGHT
|
||
description: For VITALS_BASELINE only — the single vital to band (defaults to RESTING_HEART_RATE). Ignored by
|
||
composites. An unsupported value yields an `insufficient` value rather than a 422 so iOS metric combinations
|
||
stay forgiving.
|
||
responses:
|
||
"200":
|
||
description: The flat derived-metric value (ok or insufficient).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/DerivedMetricResponseEnvelope"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/insights/derived/batch:
|
||
get:
|
||
tags:
|
||
- Insights
|
||
summary: Derived wellness metrics (batched compute-once)
|
||
description: v1.10.0 — resolve several derived wellness metrics in ONE request. The `metrics` CSV names the metrics (a
|
||
`metric:type` token sub-targets a VITALS_BASELINE vital); the server fans out under a bounded limiter with the
|
||
profile loaded once and returns a map keyed by the per-request token. Collapses the Insights cold-mount fan-out
|
||
of 14+ independent single-metric requests — the pool-starvation class that surfaces as a hang-then-recover. The
|
||
single-metric route stays for the per-score detail pages. Auth via cookie or Bearer.
|
||
parameters:
|
||
- in: query
|
||
name: metrics
|
||
schema:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 1024
|
||
description: Comma-separated derived-metric tokens. Each is a `<DERIVED_METRIC_ID>` or
|
||
`<DERIVED_METRIC_ID>:<MeasurementType>` (the colon sub-targets a VITALS_BASELINE vital). An unknown id
|
||
422s; at most 24 tokens; duplicates collapse.
|
||
required: true
|
||
description: Comma-separated derived-metric tokens. Each is a `<DERIVED_METRIC_ID>` or
|
||
`<DERIVED_METRIC_ID>:<MeasurementType>` (the colon sub-targets a VITALS_BASELINE vital). An unknown id 422s;
|
||
at most 24 tokens; duplicates collapse.
|
||
responses:
|
||
"200":
|
||
description: The map of derived-metric values, keyed by token.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/DerivedBatchResponseEnvelope"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
/api/insights/correlations:
|
||
get:
|
||
tags:
|
||
- Insights
|
||
summary: Correlation discovery (FDR-controlled)
|
||
description: v1.10.0 — scans a curated behaviour × outcome matrix (daylight / mood / glucose / BP / steps × sleep / HRV
|
||
/ resting HR / weight), lag-joins each behaviour day to the next day's outcome, runs Pearson with the exact
|
||
Student-t p-value, and applies Benjamini-Hochberg FDR control across every tested pair. Only
|
||
statistically-defensible pairs surface, each carrying n, r, p, and the BH-adjusted q. Descriptive, never causal.
|
||
Gated by the operator `correlations` assistant surface. Auth via cookie or Bearer.
|
||
responses:
|
||
"200":
|
||
description: The discovered correlations + the tested-pair count.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CorrelationDiscoveryResponseEnvelope"
|
||
"401": *a1
|
||
"422": *a2
|
||
"429": *a3
|
||
components:
|
||
schemas:
|
||
PasskeyLoginVerifyRequest:
|
||
type: object
|
||
properties:
|
||
challengeId:
|
||
type: string
|
||
minLength: 1
|
||
description: Server-issued WebAuthn challenge id.
|
||
credential:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
rawId:
|
||
type: string
|
||
type:
|
||
type: string
|
||
const: public-key
|
||
response:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties: {}
|
||
authenticatorAttachment:
|
||
type: string
|
||
enum:
|
||
- platform
|
||
- cross-platform
|
||
clientExtensionResults:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties: {}
|
||
required:
|
||
- id
|
||
- rawId
|
||
- type
|
||
- response
|
||
description: SimpleWebAuthn-style assertion response payload.
|
||
required:
|
||
- challengeId
|
||
- credential
|
||
description: Passkey assertion verification. Same native-client token issuance as the password path.
|
||
RefreshTokenRequest:
|
||
type: object
|
||
properties:
|
||
refreshToken:
|
||
type: string
|
||
minLength: 1
|
||
description: Caller-presented refresh token (`hlr_<64hex>`).
|
||
revoke:
|
||
description: When true, revoke the supplied token instead of rotating.
|
||
type: boolean
|
||
required:
|
||
- refreshToken
|
||
description: Exchange a one-time-use refresh token for a fresh access + refresh pair.
|
||
AppleHealthBatchRequest:
|
||
type: object
|
||
properties:
|
||
entries:
|
||
minItems: 1
|
||
maxItems: 500
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/AppleHealthBatchEntry"
|
||
required:
|
||
- entries
|
||
description: Apple Health batch ingest. ≤500 entries per call; idempotent via `Idempotency-Key`.
|
||
AppleHealthBatchEntry:
|
||
type: object
|
||
properties:
|
||
hkIdentifier:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 120
|
||
description: HealthKit identifier (e.g. `HKQuantityTypeIdentifierBodyMass`).
|
||
value:
|
||
type: number
|
||
unit:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 60
|
||
startDate:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
endDate:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
sleepStage:
|
||
description: HKCategoryValueSleepAnalysis codepoint; only for sleep samples.
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 20
|
||
categoryValue:
|
||
description: v1.10.0 — `HKCategoryValue` codepoint for an EVENT-class category sample (irregular-rhythm, high/low-HR,
|
||
walking-steadiness, breathing-disturbance). Carries the device's own classification verdict / severity,
|
||
which the server resolves to a stored `rhythmClassification`. HealthLog stores ONLY the device's result — it
|
||
never re-classifies. Ignored for non-event identifiers.
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 20
|
||
externalId:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 120
|
||
description: HKSample.uuid string — the dedup key.
|
||
externalSourceVersion:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 120
|
||
source:
|
||
description: Source attribution for the row. Defaults to `APPLE_HEALTH` when omitted. Send `MANUAL` to tag rows the user
|
||
entered by hand on-device (e.g. the standalone adopt-on-pair backfill). Part of the `(userId, type, source,
|
||
externalId)` dedup key, so the same externalId under a different source is a distinct row. `WITHINGS` /
|
||
`IMPORT` are not accepted here.
|
||
type: string
|
||
enum:
|
||
- APPLE_HEALTH
|
||
- MANUAL
|
||
deviceType:
|
||
description: Device class mapped from `HKDevice.model`. Used by the analytics aggregator to break ties when the same
|
||
source contributed multiple devices for the same day. Omit (or send null) on legacy clients — the server
|
||
treats it as `unknown` and the picker falls through.
|
||
anyOf:
|
||
- type: string
|
||
enum:
|
||
- watch
|
||
- band
|
||
- ring
|
||
- phone
|
||
- scale
|
||
- other
|
||
- unknown
|
||
- type: "null"
|
||
required:
|
||
- hkIdentifier
|
||
- value
|
||
- unit
|
||
- startDate
|
||
- endDate
|
||
- externalId
|
||
MeasurementsDeleteByExternalIdsRequest:
|
||
type: object
|
||
properties:
|
||
externalIds:
|
||
minItems: 0
|
||
maxItems: 500
|
||
type: array
|
||
items:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 120
|
||
required:
|
||
- externalIds
|
||
description: iOS deletion-sync. Up to 500 externalIds per call; matching rows owned by other users are silently skipped
|
||
(cross-user 404 guard).
|
||
DeviceRegisterRequest:
|
||
type: object
|
||
properties:
|
||
token:
|
||
type: string
|
||
minLength: 8
|
||
maxLength: 512
|
||
pattern: ^[A-Za-z0-9+/=._:-]+$
|
||
description: Generic device identifier (legacy; APNs token below).
|
||
bundleId:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 128
|
||
locale:
|
||
type: string
|
||
minLength: 2
|
||
maxLength: 16
|
||
appVersion:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 32
|
||
model:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 64
|
||
apnsToken:
|
||
description: Hex-encoded APNs device token. Must be paired with `apnsEnvironment`.
|
||
type: string
|
||
minLength: 8
|
||
maxLength: 256
|
||
pattern: ^[A-Fa-f0-9]+$
|
||
apnsEnvironment:
|
||
description: Gateway the iOS client received `apnsToken` from. Server never auto-detects.
|
||
type: string
|
||
enum:
|
||
- sandbox
|
||
- production
|
||
medicationDelivery:
|
||
description: v1.7.0 per-device medication-delivery override. NULL / omitted = inherit the user-level roaming default.
|
||
"server" forces server APNs for this device; "client" forces local. Stored + echoed; cron suppression stays
|
||
user-level.
|
||
anyOf:
|
||
- type: string
|
||
enum:
|
||
- server
|
||
- client
|
||
- type: "null"
|
||
required:
|
||
- token
|
||
- bundleId
|
||
description: Native device registration. Re-registering an APNs token belonging to another user returns 409
|
||
(cross-user-hijack guard).
|
||
CreateMedicationRequest:
|
||
type: object
|
||
properties:
|
||
name:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 100
|
||
dose:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 50
|
||
category:
|
||
type: string
|
||
enum:
|
||
- BLOOD_PRESSURE
|
||
- VITAMIN
|
||
- SUPPLEMENT
|
||
- PAIN_RELIEF
|
||
- ALLERGY
|
||
- DIGESTIVE
|
||
- THYROID
|
||
- HORMONE
|
||
- SKIN
|
||
- SLEEP_AID
|
||
- DIABETES
|
||
- ANTIBIOTIC
|
||
- OTHER
|
||
treatmentClass:
|
||
type: string
|
||
enum:
|
||
- GENERIC
|
||
- GLP1
|
||
dosesPerUnit:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 100
|
||
deliveryForm:
|
||
type: string
|
||
enum:
|
||
- ORAL
|
||
- INJECTION
|
||
- OTHER
|
||
trackInjectionSites:
|
||
description: Per-medication injection-site tracking opt-in. Default false. Only meaningful for an INJECTION delivery
|
||
form; when true the client prompts (skippably) for the site after a taken dose.
|
||
type: boolean
|
||
allowedInjectionSites:
|
||
description: Per-medication allowed / preferred injection sites. Empty array = no per-medication restriction (every site
|
||
offered). The effective pickable set is this list minus the user's global exclusion.
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- ABDOMEN_LEFT
|
||
- ABDOMEN_RIGHT
|
||
- ABDOMEN_UPPER_LEFT
|
||
- ABDOMEN_UPPER_RIGHT
|
||
- THIGH_LEFT
|
||
- THIGH_RIGHT
|
||
- UPPER_ARM_LEFT
|
||
- UPPER_ARM_RIGHT
|
||
notificationsEnabled:
|
||
type: boolean
|
||
liveActivityEnabled:
|
||
description: iOS Live Activity opt-in for this medication's reminders. Default false. The iOS client owns the
|
||
ActivityKit lifecycle; the server only stores + echoes the flag.
|
||
type: boolean
|
||
criticalAlarmEnabled:
|
||
description: iOS 26 AlarmKit critical-reminder opt-in. Default false. Critical alarms bypass the device mute switch /
|
||
Focus; the server stores the preference only and hangs no server-side behaviour off it.
|
||
type: boolean
|
||
atcCode:
|
||
description: Optional WHO ATC classification code (active-substance class, 7 chars, e.g. `A10BX10`).
|
||
User/clinician-asserted; never machine-guessed. Emitted on the FHIR `medicationCodeableConcept` under
|
||
`http://www.whocc.no/atc`. NULL clears it; absent leaves it untouched.
|
||
anyOf:
|
||
- type: string
|
||
pattern: ^[A-Z]\d{2}[A-Z]{2}\d{2}$
|
||
- type: "null"
|
||
rxNormCode:
|
||
description: Optional RxNorm RxCUI (numeric, US identifier, e.g. `2601723`). Secondary coding emitted under
|
||
`http://www.nlm.nih.gov/research/umls/rxnorm` alongside any ATC code, never instead of the free-text name.
|
||
NULL clears it; absent leaves it untouched.
|
||
anyOf:
|
||
- type: string
|
||
maxLength: 20
|
||
pattern: ^\d+$
|
||
- type: "null"
|
||
startsOn:
|
||
description: Date the medication course begins (ISO `YYYY-MM-DD`). Anchors RRULE BYDAY / BYMONTHDAY patterns and the
|
||
rolling-interval countdown's first window. NULL = active from creation (the legacy implicit behaviour).
|
||
Required when `oneShot` is true.
|
||
anyOf:
|
||
- type: string
|
||
format: date
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))$
|
||
- type: "null"
|
||
endsOn:
|
||
description: Date the medication course ends (ISO `YYYY-MM-DD`). NULL = no end date (chronic). When `oneShot` is true
|
||
the route normalises `endsOn` to equal `startsOn`.
|
||
anyOf:
|
||
- type: string
|
||
format: date
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))$
|
||
- type: "null"
|
||
oneShot:
|
||
description: Single-administration medication (e.g. flu shot, post-op single dose). When true the medication has at most
|
||
one schedule with no `rrule` / `rollingIntervalDays`, and `active` auto-flips to false once the dose is
|
||
logged (non-skipped).
|
||
type: boolean
|
||
schedules:
|
||
minItems: 1
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/MedicationScheduleInput"
|
||
required:
|
||
- name
|
||
- dose
|
||
- schedules
|
||
description: 'Create-medication body. The route enforces the v1.5 cross-field invariants on top of the per-schedule
|
||
`rrule_xor_rolling` Zod refine: a `oneShot:true` medication may carry at most one schedule and that schedule
|
||
must not declare a recurrence; `endsOn` is normalised to equal `startsOn` for one-shot doses; a recurring
|
||
schedule with no `rrule`, `rollingIntervalDays`, or legacy `daysOfWeek` defaults to `rrule = "FREQ=DAILY"`; and
|
||
`timesOfDay` is dual-written from `windowStart` when the caller omits it.'
|
||
MedicationScheduleInput:
|
||
type: object
|
||
properties:
|
||
windowStart:
|
||
type: string
|
||
pattern: ^([01]\d|2[0-3]):([0-5]\d)$
|
||
description: Legacy single-time-of-intake (HH:mm, user local). v1.5 keeps the field for backwards compatibility with
|
||
pre-wizard iOS clients; the new `timesOfDay` array supersedes it.
|
||
windowEnd:
|
||
type: string
|
||
pattern: ^([01]\d|2[0-3]):([0-5]\d)$
|
||
description: Legacy reminder-window upper bound (HH:mm). Used to derive the late-classification grace span when
|
||
`reminderGraceMinutes` is null.
|
||
label:
|
||
description: Optional human label (e.g. "Morning", "Evening").
|
||
type: string
|
||
maxLength: 50
|
||
dose:
|
||
description: Per-schedule dose override. NULL means the schedule inherits `Medication.dose`.
|
||
type: string
|
||
maxLength: 50
|
||
daysOfWeek:
|
||
description: Legacy day-of-week filter (0=Sunday..6=Saturday). v1.5 reads new writes through `rrule` first; this field
|
||
is preserved for pre-v1.5 rows and is the input the route serialises into the persisted `days_of_week`
|
||
string.
|
||
type: array
|
||
items:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 6
|
||
intervalWeeks:
|
||
description: Legacy multi-week stride (1..4). Bi-weekly + tri-weekly were broken in the pre-v1.5 reminder worker; new
|
||
writes encode the same intent via `rrule` (e.g. `FREQ=WEEKLY;INTERVAL=2;BYDAY=WE`).
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 4
|
||
timesOfDay:
|
||
description: v1.5 first-class points-in-time the dose is taken (HH:mm, user local). Up to 8 entries. Absent or empty
|
||
means the route stamps `[windowStart]` so the new engine always sees a populated array.
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
pattern: ^([01]\d|2[0-3]):([0-5]\d)$
|
||
reminderGraceMinutes:
|
||
description: Reminder grace window in minutes. Caps at 24h. NULL falls back to the legacy `windowEnd - windowStart` span.
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 1440
|
||
rrule:
|
||
description: "RFC 5545 RRULE string (subset). Use for daily / weekly-with-BYDAY / multi-week / monthly / yearly
|
||
cadences. Mutually exclusive with `rollingIntervalDays`. Examples: `FREQ=DAILY`,
|
||
`FREQ=WEEKLY;BYDAY=MO,WE,FR`, `FREQ=WEEKLY;INTERVAL=2;BYDAY=WE`, `FREQ=MONTHLY;BYMONTHDAY=1`,
|
||
`FREQ=YEARLY;BYMONTH=1;BYMONTHDAY=1`."
|
||
type: string
|
||
maxLength: 200
|
||
pattern: ^FREQ=(?:DAILY|WEEKLY|MONTHLY|YEARLY)(?:;(?:INTERVAL=\d{1,3}|BYDAY=(?:MO|TU|WE|TH|FR|SA|SU)(?:,(?:MO|TU|WE|TH|FR|SA|SU))*|BYMONTHDAY=-?\d{1,2}(?:,-?\d{1,2})*|BYMONTH=\d{1,2}(?:,\d{1,2})*|COUNT=\d{1,4}|UNTIL=\d{8}T\d{6}Z))*$
|
||
rollingIntervalDays:
|
||
description: Flexible-rolling interval in days, counted forward from the latest `MedicationIntakeEvent.takenAt` (the
|
||
dose re-anchors when logged). Mutually exclusive with `rrule`. Range 1..365.
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 365
|
||
scheduleType:
|
||
description: Schedule type. SCHEDULED (default) = rrule / rolling / legacy cadence. PRN = as-needed (never projected,
|
||
reminded, or counted in compliance expected; still loggable via the intake route). CYCLIC = N weeks on / M
|
||
weeks off, gating whichever inner cadence the rrule / legacy fields describe.
|
||
type: string
|
||
enum:
|
||
- SCHEDULED
|
||
- PRN
|
||
- CYCLIC
|
||
cyclicOnWeeks:
|
||
description: Cyclic "on" weeks (1..52). Required when `scheduleType` is CYCLIC; ignored otherwise.
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 52
|
||
cyclicOffWeeks:
|
||
description: Cyclic "off" weeks (0..52). Required when `scheduleType` is CYCLIC; ignored otherwise.
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 52
|
||
required:
|
||
- windowStart
|
||
- windowEnd
|
||
description: Single schedule entry on a medication. v1.5 introduces `timesOfDay`, `rrule`, `rollingIntervalDays`, and
|
||
`reminderGraceMinutes` as first-class fields; `windowStart`, `windowEnd`, `daysOfWeek`, and `intervalWeeks` are
|
||
preserved through the v1.5.x line for backwards compatibility. **`rrule` and `rollingIntervalDays` are mutually
|
||
exclusive** — supplying both fails 422 (`rrule_xor_rolling`). The DB enforces the same invariant via a CHECK
|
||
constraint.
|
||
UpdateMedicationRequest:
|
||
type: object
|
||
properties:
|
||
name:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 100
|
||
dose:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 50
|
||
category:
|
||
type: string
|
||
enum:
|
||
- BLOOD_PRESSURE
|
||
- VITAMIN
|
||
- SUPPLEMENT
|
||
- PAIN_RELIEF
|
||
- ALLERGY
|
||
- DIGESTIVE
|
||
- THYROID
|
||
- HORMONE
|
||
- SKIN
|
||
- SLEEP_AID
|
||
- DIABETES
|
||
- ANTIBIOTIC
|
||
- OTHER
|
||
treatmentClass:
|
||
type: string
|
||
enum:
|
||
- GENERIC
|
||
- GLP1
|
||
dosesPerUnit:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: 1
|
||
maximum: 100
|
||
- type: "null"
|
||
deliveryForm:
|
||
type: string
|
||
enum:
|
||
- ORAL
|
||
- INJECTION
|
||
- OTHER
|
||
trackInjectionSites:
|
||
description: Per-medication injection-site tracking opt-in. Only meaningful for an INJECTION delivery form. Set false to
|
||
deactivate tracking.
|
||
type: boolean
|
||
allowedInjectionSites:
|
||
description: Per-medication allowed / preferred injection sites. Empty array clears the restriction. The effective
|
||
pickable set subtracts the user's global exclusion (deny wins).
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- ABDOMEN_LEFT
|
||
- ABDOMEN_RIGHT
|
||
- ABDOMEN_UPPER_LEFT
|
||
- ABDOMEN_UPPER_RIGHT
|
||
- THIGH_LEFT
|
||
- THIGH_RIGHT
|
||
- UPPER_ARM_LEFT
|
||
- UPPER_ARM_RIGHT
|
||
active:
|
||
type: boolean
|
||
notificationsEnabled:
|
||
type: boolean
|
||
liveActivityEnabled:
|
||
description: iOS Live Activity opt-in for this medication's reminders. The iOS client owns the ActivityKit lifecycle;
|
||
the server only stores + echoes the flag.
|
||
type: boolean
|
||
criticalAlarmEnabled:
|
||
description: iOS 26 AlarmKit critical-reminder opt-in. Critical alarms bypass the device mute switch / Focus; the server
|
||
stores the preference only.
|
||
type: boolean
|
||
atcCode:
|
||
description: Optional WHO ATC classification code (active-substance class, 7 chars, e.g. `A10BX10`).
|
||
User/clinician-asserted; never machine-guessed. Emitted on the FHIR `medicationCodeableConcept` under
|
||
`http://www.whocc.no/atc`. NULL clears it; absent leaves it untouched.
|
||
anyOf:
|
||
- type: string
|
||
pattern: ^[A-Z]\d{2}[A-Z]{2}\d{2}$
|
||
- type: "null"
|
||
rxNormCode:
|
||
description: Optional RxNorm RxCUI (numeric, US identifier, e.g. `2601723`). Secondary coding emitted under
|
||
`http://www.nlm.nih.gov/research/umls/rxnorm` alongside any ATC code, never instead of the free-text name.
|
||
NULL clears it; absent leaves it untouched.
|
||
anyOf:
|
||
- type: string
|
||
maxLength: 20
|
||
pattern: ^\d+$
|
||
- type: "null"
|
||
startsOn:
|
||
description: Date the medication course begins (ISO `YYYY-MM-DD`). Anchors RRULE BYDAY / BYMONTHDAY patterns and the
|
||
rolling-interval countdown's first window. NULL = active from creation (the legacy implicit behaviour).
|
||
Required when `oneShot` is true.
|
||
anyOf:
|
||
- type: string
|
||
format: date
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))$
|
||
- type: "null"
|
||
endsOn:
|
||
description: Date the medication course ends (ISO `YYYY-MM-DD`). NULL = no end date (chronic). When `oneShot` is true
|
||
the route normalises `endsOn` to equal `startsOn`.
|
||
anyOf:
|
||
- type: string
|
||
format: date
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))$
|
||
- type: "null"
|
||
oneShot:
|
||
description: Single-administration medication (e.g. flu shot, post-op single dose). When true the medication has at most
|
||
one schedule with no `rrule` / `rollingIntervalDays`, and `active` auto-flips to false once the dose is
|
||
logged (non-skipped).
|
||
type: boolean
|
||
schedules:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/MedicationScheduleInput"
|
||
reminderGraceMinutes:
|
||
description: "Detail-page bridge: primary-schedule reminder-window in minutes. The route maps the value onto the primary
|
||
schedule's `reminderGraceMinutes` field; ignored when a full `schedules` array is also supplied."
|
||
anyOf:
|
||
- type: integer
|
||
minimum: 0
|
||
maximum: 1440
|
||
- type: "null"
|
||
description: Update-medication body. Every field is optional; omitted fields are left untouched. Supplying `schedules`
|
||
REPLACES the medication's full schedule list (the route deletes existing rows before re-creating). Flipping
|
||
`active` to false records the current timestamp on `pausedAt`; flipping back to true clears it. v1.5 invariants
|
||
on the `schedules` array match the create path.
|
||
MedicationIntakeRequest:
|
||
type: object
|
||
properties:
|
||
medicationId:
|
||
type: string
|
||
minLength: 1
|
||
description: Server-narrowed from the URL path. The route layer overwrites whatever the body supplies before Zod parsing
|
||
so a caller cannot log an intake against another medication.
|
||
scheduledFor:
|
||
description: Slot the dose belongs to. Defaults to `takenAt` (or `now()` when both are absent) so the compliance pairing
|
||
logic can pin the dose to a schedule slot.
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
takenAt:
|
||
description: When the dose was actually taken. NULL when `skipped` is true; defaults to `now()` for non-skipped intakes.
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
skipped:
|
||
default: false
|
||
description: True to log a skipped slot (no consumption, no inventory decrement, one-shot medications stay active).
|
||
type: boolean
|
||
idempotencyKey:
|
||
description: Caller-issued de-dup key. A second POST with the same key returns the original event without creating a new
|
||
row.
|
||
type: string
|
||
maxLength: 128
|
||
injectionSite:
|
||
description: Optional injection site for a taken dose. Honoured only when the medication is an INJECTION with
|
||
site-tracking enabled; validated against the medication's effective allowed set (per-medication allowed
|
||
sites minus the user's global exclusion). A disallowed site returns 422. Omit to record the dose without a
|
||
site.
|
||
type: string
|
||
enum:
|
||
- ABDOMEN_LEFT
|
||
- ABDOMEN_RIGHT
|
||
- ABDOMEN_UPPER_LEFT
|
||
- ABDOMEN_UPPER_RIGHT
|
||
- THIGH_LEFT
|
||
- THIGH_RIGHT
|
||
- UPPER_ARM_LEFT
|
||
- UPPER_ARM_RIGHT
|
||
required:
|
||
- medicationId
|
||
description: Per-medication intake log body. Idempotent via `idempotencyKey`; the server also dedupes by a 60-second
|
||
sliding window when the key is absent. Non-skipped intakes auto-decrement pen inventory (best-effort), refresh
|
||
the per-day compliance rollup, and — for one-shot medications — flip `active` to false. The optional
|
||
`injectionSite` is persisted only for an INJECTION medication with site-tracking enabled and is validated
|
||
against the medication's effective allowed set (422 on a disallowed value).
|
||
MedicationExtractRequest:
|
||
type: object
|
||
properties:
|
||
text:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 2000
|
||
description: Free-text medication description (any locale). Up to 2 000 characters. The model never echoes the text back
|
||
into another tenant — it is only used to produce the structured fields.
|
||
locale:
|
||
description: Optional UI locale hint for the model.
|
||
type: string
|
||
enum:
|
||
- en
|
||
- de
|
||
- es
|
||
- fr
|
||
- it
|
||
- pl
|
||
today:
|
||
description: "Optional override of the reference date used to resolve relative phrases (\"tomorrow\", \"next Monday\").
|
||
Format: `YYYY-MM-DD`. Defaults to the server's UTC day."
|
||
type: string
|
||
pattern: ^\d{4}-\d{2}-\d{2}$
|
||
required:
|
||
- text
|
||
description: Free-text medication description payload. The route runs the text through the Coach provider chain and
|
||
returns a partial structured payload the wizard merges. Rate-limited 10 requests / 5 minutes / user;
|
||
budget-gated against the daily Coach token ceiling.
|
||
DisableCoachFlag:
|
||
type: object
|
||
properties:
|
||
disableCoach:
|
||
type: boolean
|
||
required:
|
||
- disableCoach
|
||
description: Per-account Coach opt-out toggle (v1.4.47 W3). `true` hides the Coach FAB and short-circuits its API gates.
|
||
ProfileUpdateRequest:
|
||
type: object
|
||
properties:
|
||
email:
|
||
anyOf:
|
||
- type: string
|
||
format: email
|
||
pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
|
||
- type: "null"
|
||
heightCm:
|
||
anyOf:
|
||
- type: number
|
||
minimum: 50
|
||
maximum: 300
|
||
- type: "null"
|
||
dateOfBirth:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
gender:
|
||
anyOf:
|
||
- type: string
|
||
enum:
|
||
- MALE
|
||
- FEMALE
|
||
- type: "null"
|
||
displayName:
|
||
anyOf:
|
||
- type: string
|
||
minLength: 1
|
||
maxLength: 80
|
||
- type: "null"
|
||
locale:
|
||
anyOf:
|
||
- type: string
|
||
enum:
|
||
- de
|
||
- en
|
||
- type: "null"
|
||
timezone:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 64
|
||
moodReminderEnabled:
|
||
type: boolean
|
||
fullName:
|
||
anyOf:
|
||
- type: string
|
||
maxLength: 120
|
||
- type: "null"
|
||
insurerName:
|
||
anyOf:
|
||
- type: string
|
||
maxLength: 120
|
||
- type: "null"
|
||
insuranceNumber:
|
||
description: German KVNR. Empty/null clears it; mod-10 check enforced.
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
insurerIkNumber:
|
||
description: German insurer institution number (IKNR). Optional; empty/null clears it. A non-empty value must be exactly
|
||
9 digits (no checksum enforced).
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: Partial profile update. Every field is optional; an omitted field is left untouched, an explicit null (or
|
||
empty string) clears it. `userId` is never accepted — it is narrowed from the session/token.
|
||
InsightsLayoutBody:
|
||
type: object
|
||
properties:
|
||
version:
|
||
type: number
|
||
const: 1
|
||
tiles:
|
||
minItems: 1
|
||
maxItems: 50
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
enum:
|
||
- overview
|
||
- blood-pressure
|
||
- pulse
|
||
- oxygen
|
||
- body-temperature
|
||
- respiratory-rate
|
||
- weight
|
||
- bmi
|
||
- body-water
|
||
- bone-mass
|
||
- fat-free-mass
|
||
- fat-mass
|
||
- muscle-mass
|
||
- visceral-fat
|
||
- lean-body-mass
|
||
- steps
|
||
- active-energy
|
||
- workouts
|
||
- flights-climbed
|
||
- walking-distance
|
||
- walking-steadiness
|
||
- walking-heart-rate
|
||
- walking-asymmetry
|
||
- double-support-time
|
||
- step-length
|
||
- walking-speed
|
||
- cardio-fitness
|
||
- falls
|
||
- six-minute-walk
|
||
- stair-ascent-speed
|
||
- stair-descent-speed
|
||
- sleep
|
||
- breathing-disturbances
|
||
- resting-pulse
|
||
- hrv
|
||
- pulse-wave-velocity
|
||
- vascular-age
|
||
- cardio-recovery
|
||
- environmental-audio
|
||
- headphone-audio
|
||
- audio-events
|
||
- daylight
|
||
- blood-glucose
|
||
- skin-temperature
|
||
- wrist-temperature
|
||
- mood
|
||
- medications
|
||
- blutdruck
|
||
- puls
|
||
- sauerstoff
|
||
- koerpertemperatur
|
||
- gewicht
|
||
- aktive-energie
|
||
- schlaf
|
||
- ruhepuls
|
||
- stimmung
|
||
- medikamente
|
||
visible:
|
||
type: boolean
|
||
order:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 99
|
||
required:
|
||
- id
|
||
- visible
|
||
- order
|
||
required:
|
||
- version
|
||
- tiles
|
||
description: "Per-user Insights tile layout: an ordered list of tiles with a visibility flag. `version` is the layout
|
||
schema version. Tile ids are a closed enum: the canonical ids are English (matching the routed
|
||
`/insights/<slug>` sub-pages). The legacy German ids (blutdruck, puls, sauerstoff, koerpertemperatur, gewicht,
|
||
aktive-energie, schlaf, ruhepuls, stimmung, medikamente) remain accepted on input for backward compatibility and
|
||
are normalised to their English equivalents before persisting; GET responses always carry the canonical English
|
||
ids. The legacy ids are deprecated and will be removed in a future major version."
|
||
CoachMessageFeedbackRequest:
|
||
type: object
|
||
properties:
|
||
rating:
|
||
type: string
|
||
enum:
|
||
- helpful
|
||
- unhelpful
|
||
reason:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 200
|
||
required:
|
||
- rating
|
||
description: Per-message helpful/unhelpful feedback (v1.4.23 H7). Optional `reason` is free-form prose, capped at 200 chars.
|
||
InsightsPregenerateRequest:
|
||
type: object
|
||
properties: {}
|
||
description: No body fields. The user is taken from the session / Bearer and the locale from the session; the warm
|
||
covers every assessment for that user.
|
||
MedicationTreatmentClass:
|
||
type: string
|
||
enum:
|
||
- GENERIC
|
||
- GLP1
|
||
description: Prisma-level treatment-class discriminator. `GLP1` unlocks the GLP-1 specialist surfaces (injection-site
|
||
rotation, titration history, pen inventory, GLP-1-aware Coach).
|
||
MedicationSchedule:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
medicationId:
|
||
type: string
|
||
windowStart:
|
||
type: string
|
||
description: Legacy single-time-of-intake (HH:mm, user local). Preserved for backwards compatibility; the new
|
||
`timesOfDay` array supersedes it.
|
||
windowEnd:
|
||
type: string
|
||
description: Legacy reminder-window upper bound (HH:mm). Used to derive the late-classification grace span when
|
||
`reminderGraceMinutes` is null.
|
||
label:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
dose:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: Per-schedule dose override. NULL means the schedule inherits `Medication.dose`.
|
||
daysOfWeek:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: Legacy persisted recurrence encoding (`null` | `1,3,5` | `i2;1,3,5`). v1.5 readers consult `rrule` first;
|
||
the field is kept for pre-v1.5 rows. v1.6.0 drops the column.
|
||
timesOfDay:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: v1.5 first-class points-in-time the dose is taken (HH:mm, user local). Backfilled to `[windowStart]` for
|
||
every pre-v1.5 row.
|
||
reminderGraceMinutes:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
description: Reminder grace window in minutes. NULL falls back to the legacy `windowEnd - windowStart` span.
|
||
rrule:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: RFC 5545 RRULE string (subset). Used for calendar-anchored cadences. **Mutually exclusive with
|
||
`rollingIntervalDays`** — exactly one of the two is non-null on any v1.5+ schedule (or both are null on
|
||
legacy rows that haven't been touched since the migration).
|
||
rollingIntervalDays:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
description: Flexible-rolling interval in days, counted forward from the latest `MedicationIntakeEvent.takenAt`.
|
||
**Mutually exclusive with `rrule`.**
|
||
scheduleType:
|
||
type: string
|
||
enum:
|
||
- SCHEDULED
|
||
- PRN
|
||
- CYCLIC
|
||
description: v1.7.0 schedule-type discriminator. SCHEDULED = rrule / rolling / legacy cadence. PRN = as-needed (never
|
||
projected, reminded, or counted in compliance expected; still loggable via the intake route). CYCLIC = N
|
||
weeks on / M weeks off, gating whichever inner cadence the rrule / legacy fields describe.
|
||
cyclicOnWeeks:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
description: v1.7.0 cyclic "on" weeks. Only meaningful when `scheduleType` is CYCLIC; null otherwise.
|
||
cyclicOffWeeks:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
description: v1.7.0 cyclic "off" weeks. Only meaningful when `scheduleType` is CYCLIC; null otherwise.
|
||
required:
|
||
- id
|
||
- medicationId
|
||
- windowStart
|
||
- windowEnd
|
||
- label
|
||
- dose
|
||
- daysOfWeek
|
||
- timesOfDay
|
||
- reminderGraceMinutes
|
||
- rrule
|
||
- rollingIntervalDays
|
||
- scheduleType
|
||
- cyclicOnWeeks
|
||
- cyclicOffWeeks
|
||
description: Schedule entry attached to a medication. v1.5 promotes `timesOfDay` to first-class and introduces `rrule`
|
||
(calendar-anchored cadences) and `rollingIntervalDays` (flexible-rolling cadences). The two recurrence
|
||
primitives are mutually exclusive — enforced by the Zod refine on writes, the route layer, and a DB CHECK
|
||
constraint (`medication_schedules_rrule_xor_rolling`). v1.7.0 adds `scheduleType` (SCHEDULED / PRN / CYCLIC) and
|
||
the cyclic on/off-week fields.
|
||
CapabilitiesEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/CapabilitiesResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
CapabilitiesResponse:
|
||
type: object
|
||
properties:
|
||
apiContractVersion:
|
||
type: string
|
||
description: Running build version — mirrors GET /api/version `version`.
|
||
derivedMetricIds:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: Closed derived-metric id set (GET /api/insights/derived).
|
||
vitalsBaselineTypes:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: MeasurementTypes the typical-range baseline engine supports.
|
||
layoutTileIds:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: Canonical insights layout tile-id set (English ids).
|
||
metricStatusIds:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: Closed metric-status / assessment id set.
|
||
ingest:
|
||
type: object
|
||
properties:
|
||
quantityTypes:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
type:
|
||
type: string
|
||
description: HealthLog MeasurementType.
|
||
hk:
|
||
type: string
|
||
description: HealthKit identifier.
|
||
unit:
|
||
type: string
|
||
description: Canonical DB unit.
|
||
required:
|
||
- type
|
||
- hk
|
||
- unit
|
||
additionalProperties: false
|
||
description: Accepted HealthKit quantity-sample mappings.
|
||
eventTypes:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: MeasurementTypes for device-flagged EVENT-class HealthKit samples.
|
||
computedScores:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: Server-owned nightly composite score types.
|
||
writeAllowlist:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: MeasurementSources a client may attribute on a write (others are server-owned).
|
||
required:
|
||
- quantityTypes
|
||
- eventTypes
|
||
- computedScores
|
||
- writeAllowlist
|
||
additionalProperties: false
|
||
description: Ingest vocabularies the batch / single-write paths accept.
|
||
fhir:
|
||
type: object
|
||
properties:
|
||
atcSystem:
|
||
type: string
|
||
description: WHO ATC CodeSystem URI.
|
||
snomedRoute:
|
||
type: string
|
||
description: SNOMED CT CodeSystem URI.
|
||
germanAtcDefaultLocales:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: App locales that default the additive BfArM ATC coding on.
|
||
restBaseUrl:
|
||
type: string
|
||
description: Base path of the read-only FHIR R4 REST face.
|
||
readScope:
|
||
type: string
|
||
description: Bearer scope a narrow token needs to read the FHIR face.
|
||
resourceTypes:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: FHIR resource types the REST face serves (read + search).
|
||
operations:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: Whole-record operations exposed (e.g. $everything).
|
||
searchParams:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: Search parameters honoured uniformly across the search routes.
|
||
required:
|
||
- atcSystem
|
||
- snomedRoute
|
||
- germanAtcDefaultLocales
|
||
- restBaseUrl
|
||
- readScope
|
||
- resourceTypes
|
||
- operations
|
||
- searchParams
|
||
additionalProperties: false
|
||
description: FHIR coding constants + the read-only REST face descriptor (v1.11).
|
||
share:
|
||
type: object
|
||
properties:
|
||
supported:
|
||
type: boolean
|
||
description: Whether clinician share links are served.
|
||
maxDays:
|
||
type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
description: Maximum lifetime of a share link, in days. No never-expiring share.
|
||
resourceTypes:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: FHIR resource types a share link may be scoped to serve.
|
||
sections:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: Scopeable report sections a share link may toggle.
|
||
required:
|
||
- supported
|
||
- maxDays
|
||
- resourceTypes
|
||
- sections
|
||
additionalProperties: false
|
||
description: Clinician share-link surface descriptor (v1.11).
|
||
required:
|
||
- apiContractVersion
|
||
- derivedMetricIds
|
||
- vitalsBaselineTypes
|
||
- layoutTileIds
|
||
- metricStatusIds
|
||
- ingest
|
||
- fhir
|
||
- share
|
||
additionalProperties: false
|
||
description: Live id vocabularies + contract version. Every list is derived server-side from the canonical registry it
|
||
documents, so it cannot drift from the values the routes actually accept/emit.
|
||
ErrorEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: "null"
|
||
error:
|
||
type: string
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
errorCode:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
description: "Standard error response: data is null, error is human prose."
|
||
ShareLinkCreated:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
label:
|
||
type: string
|
||
rangeStart:
|
||
type: string
|
||
rangeEnd:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
resourceTypes:
|
||
type: array
|
||
items:
|
||
type: string
|
||
allowFhirApi:
|
||
type: boolean
|
||
expiresAt:
|
||
type: string
|
||
createdAt:
|
||
type: string
|
||
revokedAt:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
lastAccessAt:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
accessCount:
|
||
type: number
|
||
active:
|
||
type: boolean
|
||
token:
|
||
type: string
|
||
description: Raw `hls_` token — returned ONCE and unrecoverable thereafter.
|
||
required:
|
||
- id
|
||
- label
|
||
- rangeStart
|
||
- rangeEnd
|
||
- resourceTypes
|
||
- allowFhirApi
|
||
- expiresAt
|
||
- createdAt
|
||
- revokedAt
|
||
- lastAccessAt
|
||
- accessCount
|
||
- active
|
||
- token
|
||
additionalProperties: false
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
ShareLinkList:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
shareLinks:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
label:
|
||
type: string
|
||
rangeStart:
|
||
type: string
|
||
rangeEnd:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
resourceTypes:
|
||
type: array
|
||
items:
|
||
type: string
|
||
allowFhirApi:
|
||
type: boolean
|
||
expiresAt:
|
||
type: string
|
||
createdAt:
|
||
type: string
|
||
revokedAt:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
lastAccessAt:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
accessCount:
|
||
type: number
|
||
active:
|
||
type: boolean
|
||
required:
|
||
- id
|
||
- label
|
||
- rangeStart
|
||
- rangeEnd
|
||
- resourceTypes
|
||
- allowFhirApi
|
||
- expiresAt
|
||
- createdAt
|
||
- revokedAt
|
||
- lastAccessAt
|
||
- accessCount
|
||
- active
|
||
additionalProperties: false
|
||
required:
|
||
- shareLinks
|
||
additionalProperties: false
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
ShareLinkRevoked:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
revoked:
|
||
type: boolean
|
||
required:
|
||
- id
|
||
- revoked
|
||
additionalProperties: false
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
LoginResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/AccessRefreshBundle"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
AccessRefreshBundle:
|
||
type: object
|
||
properties:
|
||
user:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
username:
|
||
type: string
|
||
required:
|
||
- id
|
||
- username
|
||
additionalProperties: false
|
||
token:
|
||
type: string
|
||
description: Access token (`hlk_<64hex>`).
|
||
tokenExpiresAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
refreshToken:
|
||
description: Refresh token (`hlr_<64hex>`); only present for native-policy callers.
|
||
type: string
|
||
refreshTokenExpiresAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
required:
|
||
- token
|
||
- tokenExpiresAt
|
||
additionalProperties: false
|
||
description: Native-client token bundle returned by login + refresh. Web cookie-only callers see only `user`.
|
||
PasskeyLoginVerifyResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/AccessRefreshBundle"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
RefreshResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/AccessRefreshBundle"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
SyncStateEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/SyncStateResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
SyncStateResponse:
|
||
type: object
|
||
properties:
|
||
userId:
|
||
type: string
|
||
timezone:
|
||
type: string
|
||
lastSyncedAt:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
serverNow:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
measurements:
|
||
type: object
|
||
properties:
|
||
lastUpdatedAt:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
liveCount:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
tombstonedCount:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
required:
|
||
- lastUpdatedAt
|
||
- liveCount
|
||
- tombstonedCount
|
||
additionalProperties: false
|
||
mood:
|
||
type: object
|
||
properties:
|
||
lastUpdatedAt:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
liveCount:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
tombstonedCount:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
required:
|
||
- lastUpdatedAt
|
||
- liveCount
|
||
- tombstonedCount
|
||
additionalProperties: false
|
||
intakes:
|
||
type: object
|
||
properties:
|
||
lastUpdatedAt:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
liveCount:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
tombstonedCount:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
required:
|
||
- lastUpdatedAt
|
||
- liveCount
|
||
- tombstonedCount
|
||
additionalProperties: false
|
||
sync:
|
||
type: object
|
||
properties:
|
||
incrementalWindowDays:
|
||
type: integer
|
||
exclusiveMinimum: 0
|
||
maximum: 9007199254740991
|
||
description: Days an incremental delta stays valid; tracks the native refresh-token lifetime. Beyond it a device
|
||
re-pairs with a full backfill. iOS derives its window from this rather than hardcoding 60.
|
||
tombstoneRetentionDays:
|
||
type: integer
|
||
exclusiveMinimum: 0
|
||
maximum: 9007199254740991
|
||
description: Horizon past which tombstones may be pruned. A cursor older than this gets `cursorExpired` on
|
||
`/api/sync/changes`.
|
||
required:
|
||
- incrementalWindowDays
|
||
- tombstoneRetentionDays
|
||
additionalProperties: false
|
||
description: Sync-window metadata the client reads instead of hardcoding.
|
||
required:
|
||
- userId
|
||
- timezone
|
||
- lastSyncedAt
|
||
- serverNow
|
||
- measurements
|
||
- mood
|
||
- intakes
|
||
- sync
|
||
additionalProperties: false
|
||
description: iOS SyncMode handshake. Each GET also advances the server-side `lastSyncedAt` checkpoint and returns the
|
||
previous value. The cheap 'should I sync?' summary; the durable delta cursor lives on `/api/sync/changes`.
|
||
SyncChangesEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/SyncChangesResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
SyncChangesResponse:
|
||
type: object
|
||
properties:
|
||
serverNow:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
cursor:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: Opaque cursor to echo into the next request.
|
||
hasMore:
|
||
type: boolean
|
||
description: False once the client is caught up as of `serverNow`.
|
||
cursorExpired:
|
||
type: boolean
|
||
description: True when the supplied cursor predates tombstone retention — drop the cursor and do a clean initial sync.
|
||
changes:
|
||
type: object
|
||
properties:
|
||
measurements:
|
||
type: object
|
||
properties:
|
||
upserts:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/SyncMeasurementUpsert"
|
||
tombstones:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/SyncMeasurementTombstone"
|
||
required:
|
||
- upserts
|
||
- tombstones
|
||
additionalProperties: false
|
||
mood:
|
||
type: object
|
||
properties:
|
||
upserts:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/SyncMoodUpsert"
|
||
tombstones:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/SyncMoodTombstone"
|
||
required:
|
||
- upserts
|
||
- tombstones
|
||
additionalProperties: false
|
||
intakes:
|
||
type: object
|
||
properties:
|
||
upserts:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/SyncIntakeUpsert"
|
||
tombstones:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/SyncIntakeTombstone"
|
||
required:
|
||
- upserts
|
||
- tombstones
|
||
additionalProperties: false
|
||
required:
|
||
- measurements
|
||
- mood
|
||
- intakes
|
||
additionalProperties: false
|
||
required:
|
||
- serverNow
|
||
- cursor
|
||
- hasMore
|
||
- cursorExpired
|
||
- changes
|
||
additionalProperties: false
|
||
description: "Multi-domain delta page (v1.7.0): measurements + mood + intakes. One opaque multi-domain keyset cursor;
|
||
tombstones apply before upserts within each domain. Tombstone identity: measurements key on externalId, mood +
|
||
intakes on server id. The iOS consumer is measurements-only this cycle; mood + intakes are forward-prep."
|
||
SyncMeasurementUpsert:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
type:
|
||
type: string
|
||
enum:
|
||
- WEIGHT
|
||
- BLOOD_PRESSURE_SYS
|
||
- BLOOD_PRESSURE_DIA
|
||
- PULSE
|
||
- BODY_FAT
|
||
- SLEEP_DURATION
|
||
- ACTIVITY_STEPS
|
||
- BLOOD_GLUCOSE
|
||
- TOTAL_BODY_WATER
|
||
- BONE_MASS
|
||
- OXYGEN_SATURATION
|
||
- HEART_RATE_VARIABILITY
|
||
- RESTING_HEART_RATE
|
||
- ACTIVE_ENERGY_BURNED
|
||
- FLIGHTS_CLIMBED
|
||
- WALKING_RUNNING_DISTANCE
|
||
- VO2_MAX
|
||
- BODY_TEMPERATURE
|
||
- FAT_FREE_MASS
|
||
- FAT_MASS
|
||
- MUSCLE_MASS
|
||
- SKIN_TEMPERATURE
|
||
- PULSE_WAVE_VELOCITY
|
||
- VASCULAR_AGE
|
||
- VISCERAL_FAT
|
||
- AUDIO_EXPOSURE_ENV
|
||
- AUDIO_EXPOSURE_HEADPHONE
|
||
- TIME_IN_DAYLIGHT
|
||
- WALKING_STEADINESS
|
||
- AUDIO_EXPOSURE_EVENT
|
||
- RESPIRATORY_RATE
|
||
- BODY_MASS_INDEX
|
||
- LEAN_BODY_MASS
|
||
- WALKING_HEART_RATE_AVERAGE
|
||
- WALKING_ASYMMETRY
|
||
- WALKING_DOUBLE_SUPPORT
|
||
- WALKING_STEP_LENGTH
|
||
- WALKING_SPEED
|
||
- CARDIO_RECOVERY
|
||
- WRIST_TEMPERATURE
|
||
- FALL_COUNT
|
||
- SIX_MINUTE_WALK_DISTANCE
|
||
- STAIR_ASCENT_SPEED
|
||
- STAIR_DESCENT_SPEED
|
||
- BREATHING_DISTURBANCES
|
||
- IRREGULAR_RHYTHM_NOTIFICATION
|
||
- HIGH_HEART_RATE_EVENT
|
||
- LOW_HEART_RATE_EVENT
|
||
- WALKING_STEADINESS_EVENT
|
||
- BREATHING_DISTURBANCE_EVENT
|
||
- RECOVERY_SCORE
|
||
- STRESS_SCORE
|
||
- STRAIN_SCORE
|
||
- HRV_RMSSD
|
||
- DAY_STRAIN
|
||
- WORKOUT_STRAIN
|
||
- SLEEP_PERFORMANCE
|
||
- SLEEP_EFFICIENCY
|
||
- SLEEP_CONSISTENCY
|
||
- SLEEP_NEED
|
||
- ENERGY_EXPENDITURE_KJ
|
||
- AVERAGE_HEART_RATE
|
||
- MAX_HEART_RATE
|
||
- SLEEP_DISTURBANCE_COUNT
|
||
value:
|
||
type: number
|
||
unit:
|
||
type: string
|
||
measuredAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
source:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
notes:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
externalId:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: Cross-device dedup key (UUID string or `stats:<id>:<date>`).
|
||
syncVersion:
|
||
type: integer
|
||
exclusiveMinimum: 0
|
||
maximum: 9007199254740991
|
||
description: LWW reconciliation counter; echo to keep the mirror monotonic.
|
||
updatedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
required:
|
||
- id
|
||
- type
|
||
- value
|
||
- unit
|
||
- measuredAt
|
||
- source
|
||
- externalId
|
||
- syncVersion
|
||
- updatedAt
|
||
additionalProperties: false
|
||
SyncMeasurementTombstone:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
externalId:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: The identity key the client dedups on for measurements.
|
||
syncVersion:
|
||
type: integer
|
||
exclusiveMinimum: 0
|
||
maximum: 9007199254740991
|
||
deletedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
updatedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
required:
|
||
- id
|
||
- externalId
|
||
- syncVersion
|
||
- deletedAt
|
||
- updatedAt
|
||
additionalProperties: false
|
||
description: A soft-deleted measurement. Apply tombstones BEFORE upserts within a page to avoid resurrecting a row.
|
||
SyncMoodUpsert:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
date:
|
||
type: string
|
||
description: YYYY-MM-DD anchored to the row's `tz`.
|
||
mood:
|
||
type: string
|
||
score:
|
||
type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
tags:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: JSON array of tag keys, or null.
|
||
note:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
moodLoggedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
source:
|
||
type: string
|
||
syncVersion:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
description: LWW reconciliation counter; mood is last-writer-wins by it.
|
||
updatedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
required:
|
||
- id
|
||
- date
|
||
- mood
|
||
- score
|
||
- tags
|
||
- note
|
||
- moodLoggedAt
|
||
- source
|
||
- syncVersion
|
||
- updatedAt
|
||
additionalProperties: false
|
||
SyncMoodTombstone:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
description: Server id — the identity key the client dedups on for mood.
|
||
syncVersion:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
deletedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
updatedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
required:
|
||
- id
|
||
- syncVersion
|
||
- deletedAt
|
||
- updatedAt
|
||
additionalProperties: false
|
||
description: A soft-deleted mood entry, keyed on server `id`. Apply before upserts within the domain page.
|
||
SyncIntakeUpsert:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
medicationId:
|
||
type: string
|
||
scheduledFor:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
takenAt:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
skipped:
|
||
type: boolean
|
||
source:
|
||
type: string
|
||
syncVersion:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
description: Reconciliation counter. An intake is immutable; a correction is a tombstone + re-insert.
|
||
updatedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
required:
|
||
- id
|
||
- medicationId
|
||
- scheduledFor
|
||
- takenAt
|
||
- skipped
|
||
- source
|
||
- syncVersion
|
||
- updatedAt
|
||
additionalProperties: false
|
||
SyncIntakeTombstone:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
description: Server id — the identity key the client dedups on for intakes.
|
||
syncVersion:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
deletedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
updatedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
required:
|
||
- id
|
||
- syncVersion
|
||
- deletedAt
|
||
- updatedAt
|
||
additionalProperties: false
|
||
description: A soft-deleted medication intake, keyed on server `id`. Apply before upserts within the domain page.
|
||
ListMeasurementsResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
measurements:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/MeasurementResource"
|
||
total:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
required:
|
||
- measurements
|
||
- total
|
||
additionalProperties: false
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
MeasurementResource:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
type:
|
||
type: string
|
||
enum:
|
||
- WEIGHT
|
||
- BLOOD_PRESSURE_SYS
|
||
- BLOOD_PRESSURE_DIA
|
||
- PULSE
|
||
- BODY_FAT
|
||
- SLEEP_DURATION
|
||
- ACTIVITY_STEPS
|
||
- BLOOD_GLUCOSE
|
||
- TOTAL_BODY_WATER
|
||
- BONE_MASS
|
||
- OXYGEN_SATURATION
|
||
- HEART_RATE_VARIABILITY
|
||
- RESTING_HEART_RATE
|
||
- ACTIVE_ENERGY_BURNED
|
||
- FLIGHTS_CLIMBED
|
||
- WALKING_RUNNING_DISTANCE
|
||
- VO2_MAX
|
||
- BODY_TEMPERATURE
|
||
- FAT_FREE_MASS
|
||
- FAT_MASS
|
||
- MUSCLE_MASS
|
||
- SKIN_TEMPERATURE
|
||
- PULSE_WAVE_VELOCITY
|
||
- VASCULAR_AGE
|
||
- VISCERAL_FAT
|
||
- AUDIO_EXPOSURE_ENV
|
||
- AUDIO_EXPOSURE_HEADPHONE
|
||
- TIME_IN_DAYLIGHT
|
||
- WALKING_STEADINESS
|
||
- AUDIO_EXPOSURE_EVENT
|
||
- RESPIRATORY_RATE
|
||
- BODY_MASS_INDEX
|
||
- LEAN_BODY_MASS
|
||
- WALKING_HEART_RATE_AVERAGE
|
||
- WALKING_ASYMMETRY
|
||
- WALKING_DOUBLE_SUPPORT
|
||
- WALKING_STEP_LENGTH
|
||
- WALKING_SPEED
|
||
- CARDIO_RECOVERY
|
||
- WRIST_TEMPERATURE
|
||
- FALL_COUNT
|
||
- SIX_MINUTE_WALK_DISTANCE
|
||
- STAIR_ASCENT_SPEED
|
||
- STAIR_DESCENT_SPEED
|
||
- BREATHING_DISTURBANCES
|
||
- IRREGULAR_RHYTHM_NOTIFICATION
|
||
- HIGH_HEART_RATE_EVENT
|
||
- LOW_HEART_RATE_EVENT
|
||
- WALKING_STEADINESS_EVENT
|
||
- BREATHING_DISTURBANCE_EVENT
|
||
- RECOVERY_SCORE
|
||
- STRESS_SCORE
|
||
- STRAIN_SCORE
|
||
- HRV_RMSSD
|
||
- DAY_STRAIN
|
||
- WORKOUT_STRAIN
|
||
- SLEEP_PERFORMANCE
|
||
- SLEEP_EFFICIENCY
|
||
- SLEEP_CONSISTENCY
|
||
- SLEEP_NEED
|
||
- ENERGY_EXPENDITURE_KJ
|
||
- AVERAGE_HEART_RATE
|
||
- MAX_HEART_RATE
|
||
- SLEEP_DISTURBANCE_COUNT
|
||
value:
|
||
type: number
|
||
unit:
|
||
type: string
|
||
measuredAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
source:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
notes:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
required:
|
||
- id
|
||
- type
|
||
- value
|
||
- unit
|
||
- measuredAt
|
||
- source
|
||
additionalProperties: false
|
||
description: Server-shaped measurement row returned by GET endpoints.
|
||
CreateMeasurementResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/MeasurementResource"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
BatchMeasurementsResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/AppleHealthBatchResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
AppleHealthBatchResponse:
|
||
type: object
|
||
properties:
|
||
inserted:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
duplicate:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
skipped:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
entries:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/AppleHealthBatchEntryResult"
|
||
required:
|
||
- inserted
|
||
- duplicate
|
||
- skipped
|
||
- entries
|
||
additionalProperties: false
|
||
AppleHealthBatchEntryResult:
|
||
type: object
|
||
properties:
|
||
index:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
status:
|
||
type: string
|
||
enum:
|
||
- inserted
|
||
- duplicate
|
||
- skipped
|
||
reason:
|
||
type: string
|
||
required:
|
||
- index
|
||
- status
|
||
additionalProperties: false
|
||
MeasurementsDeleteByExternalIdsResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/MeasurementsDeleteByExternalIdsResult"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
MeasurementsDeleteByExternalIdsResult:
|
||
type: object
|
||
properties:
|
||
deletedCount:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
required:
|
||
- deletedCount
|
||
additionalProperties: false
|
||
SleepNightResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/SleepNightResource"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
SleepNightResource:
|
||
type: object
|
||
properties:
|
||
night:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
main:
|
||
anyOf:
|
||
- type: object
|
||
properties:
|
||
night:
|
||
type: string
|
||
description: Wake-day key (YYYY-MM-DD).
|
||
source:
|
||
anyOf:
|
||
- type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
- type: "null"
|
||
start:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
end:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
asleepMinutes:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
inBedMinutes:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
awakeMinutes:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
awakenings:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
stages:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
enum:
|
||
- IN_BED
|
||
- AWAKE
|
||
- ASLEEP
|
||
- REM
|
||
- CORE
|
||
- DEEP
|
||
additionalProperties:
|
||
type: number
|
||
required:
|
||
- IN_BED
|
||
- AWAKE
|
||
- ASLEEP
|
||
- REM
|
||
- CORE
|
||
- DEEP
|
||
segments:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
stage:
|
||
anyOf:
|
||
- type: string
|
||
enum:
|
||
- IN_BED
|
||
- AWAKE
|
||
- ASLEEP
|
||
- REM
|
||
- CORE
|
||
- DEEP
|
||
- type: "null"
|
||
start:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
end:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
minutes:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
required:
|
||
- stage
|
||
- start
|
||
- end
|
||
- minutes
|
||
additionalProperties: false
|
||
required:
|
||
- night
|
||
- source
|
||
- start
|
||
- end
|
||
- asleepMinutes
|
||
- inBedMinutes
|
||
- awakeMinutes
|
||
- awakenings
|
||
- stages
|
||
- segments
|
||
additionalProperties: false
|
||
- type: "null"
|
||
naps:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
night:
|
||
type: string
|
||
description: Wake-day key (YYYY-MM-DD).
|
||
source:
|
||
anyOf:
|
||
- type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
- type: "null"
|
||
start:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
end:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
asleepMinutes:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
inBedMinutes:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
awakeMinutes:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
awakenings:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
stages:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
enum:
|
||
- IN_BED
|
||
- AWAKE
|
||
- ASLEEP
|
||
- REM
|
||
- CORE
|
||
- DEEP
|
||
additionalProperties:
|
||
type: number
|
||
required:
|
||
- IN_BED
|
||
- AWAKE
|
||
- ASLEEP
|
||
- REM
|
||
- CORE
|
||
- DEEP
|
||
segments:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
stage:
|
||
anyOf:
|
||
- type: string
|
||
enum:
|
||
- IN_BED
|
||
- AWAKE
|
||
- ASLEEP
|
||
- REM
|
||
- CORE
|
||
- DEEP
|
||
- type: "null"
|
||
start:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
end:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
minutes:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
required:
|
||
- stage
|
||
- start
|
||
- end
|
||
- minutes
|
||
additionalProperties: false
|
||
required:
|
||
- night
|
||
- source
|
||
- start
|
||
- end
|
||
- asleepMinutes
|
||
- inBedMinutes
|
||
- awakeMinutes
|
||
- awakenings
|
||
- stages
|
||
- segments
|
||
additionalProperties: false
|
||
required:
|
||
- night
|
||
- main
|
||
- naps
|
||
additionalProperties: false
|
||
description: "One reconstructed sleep night: the main session's hypnogram segments + breakdown, plus same-wake-day naps
|
||
surfaced separately."
|
||
ListWorkoutsResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/WorkoutListResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
WorkoutListResponse:
|
||
type: object
|
||
properties:
|
||
workouts:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/WorkoutListEntry"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
total:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
limit:
|
||
type: integer
|
||
exclusiveMinimum: 0
|
||
maximum: 9007199254740991
|
||
offset:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
droppedDuplicates:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
required:
|
||
- total
|
||
- limit
|
||
- offset
|
||
- droppedDuplicates
|
||
additionalProperties: false
|
||
required:
|
||
- workouts
|
||
- meta
|
||
additionalProperties: false
|
||
WorkoutListEntry:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
sportType:
|
||
type: string
|
||
startedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
endedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
durationSec:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
distanceM:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
activeEnergyKcal:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
avgHr:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
maxHr:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
source:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
externalId:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
required:
|
||
- id
|
||
- sportType
|
||
- startedAt
|
||
- endedAt
|
||
- durationSec
|
||
- distanceM
|
||
- activeEnergyKcal
|
||
- avgHr
|
||
- maxHr
|
||
- source
|
||
- externalId
|
||
additionalProperties: false
|
||
GetWorkoutResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/WorkoutDetailResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
WorkoutDetailResponse:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
sportType:
|
||
type: string
|
||
startedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
endedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
durationSec:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
distanceM:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
activeEnergyKcal:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
avgHr:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
maxHr:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
minHr:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
stepCount:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
elevationM:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
pauseDurationSec:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
source:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
externalId:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
metadata:
|
||
anyOf:
|
||
- {}
|
||
- type: "null"
|
||
route:
|
||
anyOf:
|
||
- $ref: "#/components/schemas/WorkoutRouteGeometry"
|
||
- type: "null"
|
||
samples:
|
||
anyOf:
|
||
- $ref: "#/components/schemas/WorkoutHrSeries"
|
||
- type: "null"
|
||
canonicalId:
|
||
type: string
|
||
required:
|
||
- id
|
||
- sportType
|
||
- startedAt
|
||
- endedAt
|
||
- durationSec
|
||
- distanceM
|
||
- activeEnergyKcal
|
||
- avgHr
|
||
- maxHr
|
||
- minHr
|
||
- stepCount
|
||
- elevationM
|
||
- pauseDurationSec
|
||
- source
|
||
- externalId
|
||
- metadata
|
||
- route
|
||
- samples
|
||
- canonicalId
|
||
additionalProperties: false
|
||
WorkoutRouteGeometry:
|
||
type: object
|
||
properties:
|
||
geometry: {}
|
||
sampleTimestamps:
|
||
anyOf:
|
||
- type: array
|
||
items:
|
||
type: string
|
||
- type: "null"
|
||
required:
|
||
- geometry
|
||
- sampleTimestamps
|
||
additionalProperties: false
|
||
WorkoutHrSeries:
|
||
type: object
|
||
properties:
|
||
sampleCount:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
samples: {}
|
||
required:
|
||
- sampleCount
|
||
- samples
|
||
additionalProperties: false
|
||
BatchWorkoutsResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/WorkoutBatchResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
WorkoutBatchResponse:
|
||
type: object
|
||
properties:
|
||
processed:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
inserted:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
duplicates:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
skipped:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
index:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
reason:
|
||
type: string
|
||
required:
|
||
- index
|
||
- reason
|
||
additionalProperties: false
|
||
entries:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/WorkoutBatchEntryResult"
|
||
required:
|
||
- processed
|
||
- inserted
|
||
- duplicates
|
||
- skipped
|
||
- entries
|
||
additionalProperties: false
|
||
WorkoutBatchEntryResult:
|
||
type: object
|
||
properties:
|
||
index:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
status:
|
||
type: string
|
||
enum:
|
||
- inserted
|
||
- duplicate
|
||
- skipped
|
||
reason:
|
||
type: string
|
||
required:
|
||
- index
|
||
- status
|
||
additionalProperties: false
|
||
DeviceRegisterResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
required:
|
||
- id
|
||
additionalProperties: false
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
ListMedicationsResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/MedicationListEntry"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
MedicationListEntry:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
name:
|
||
type: string
|
||
dose:
|
||
type: string
|
||
treatmentClass:
|
||
$ref: "#/components/schemas/MedicationTreatmentClass"
|
||
dosesPerUnit:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
description: Doses per pen / vial for inventory tracking. NULL = inventory tracking off.
|
||
active:
|
||
type: boolean
|
||
notificationsEnabled:
|
||
type: boolean
|
||
liveActivityEnabled:
|
||
type: boolean
|
||
description: v1.7.0 iOS Live Activity opt-in for this medication's reminders. Default false. The iOS client owns the
|
||
ActivityKit lifecycle; the server only stores + echoes the flag.
|
||
criticalAlarmEnabled:
|
||
type: boolean
|
||
description: v1.7.0 iOS 26 AlarmKit critical-reminder opt-in. Default false. Critical alarms bypass the device mute
|
||
switch / Focus; the server stores the preference only.
|
||
atcCode:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: v1.9.0 optional WHO ATC classification code (active-substance class, e.g. `A10BX10`).
|
||
User/clinician-asserted; never machine-guessed. Emitted on the FHIR `medicationCodeableConcept` under
|
||
`http://www.whocc.no/atc`. NULL = no code captured.
|
||
rxNormCode:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: v1.9.0 optional RxNorm RxCUI (numeric, US identifier, e.g. `2601723`). Secondary FHIR coding under
|
||
`http://www.nlm.nih.gov/research/umls/rxnorm`, alongside any ATC code. NULL = no code captured.
|
||
pausedAt:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
snoozedUntil:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
nextDueAt:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
description: v1.7.0 server-computed next due instant across all the medication's schedules (earliest
|
||
`nextOccurrenceAfter`). Read-only — computed, not stored. NULL when no schedule has an upcoming slot
|
||
(paused, one-shot in the past, `endsOn` crossed, every schedule PRN). The list GET is cached 60 s, so a 60 s
|
||
staleness is accepted.
|
||
startsOn:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
description: v1.5 course start (ISO date). Anchors RRULE BYDAY / BYMONTHDAY patterns and the rolling-interval
|
||
countdown's first window. NULL means active from creation.
|
||
endsOn:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
description: v1.5 course end (ISO date). NULL means chronic. Equals `startsOn` when `oneShot` is true.
|
||
oneShot:
|
||
type: boolean
|
||
description: v1.5 single-administration flag. When true the medication has at most one schedule (no `rrule` /
|
||
`rollingIntervalDays`), and `active` auto-flips to false once the dose is logged.
|
||
createdAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
updatedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
schedules:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/MedicationScheduleOutput"
|
||
category:
|
||
$ref: "#/components/schemas/MedicationCategory"
|
||
lastTakenAt:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
description: Latest non-skipped `MedicationIntakeEvent.takenAt` for the medication. Drives the rolling-cadence countdown
|
||
surface.
|
||
todayEventCount:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
description: Number of intake events scheduled for today (user-local day window).
|
||
required:
|
||
- id
|
||
- name
|
||
- dose
|
||
- treatmentClass
|
||
- dosesPerUnit
|
||
- active
|
||
- notificationsEnabled
|
||
- liveActivityEnabled
|
||
- criticalAlarmEnabled
|
||
- atcCode
|
||
- rxNormCode
|
||
- pausedAt
|
||
- snoozedUntil
|
||
- nextDueAt
|
||
- startsOn
|
||
- endsOn
|
||
- oneShot
|
||
- createdAt
|
||
- updatedAt
|
||
- schedules
|
||
- category
|
||
- lastTakenAt
|
||
- todayEventCount
|
||
additionalProperties: false
|
||
description: List-row variant of the medication resource enriched with the joined `category`, `lastTakenAt`, and
|
||
`todayEventCount` fields the dashboard + iOS client consume. The base medication fields (`id`, `name`, `dose`,
|
||
`treatmentClass`, `dosesPerUnit`, `active`, `notificationsEnabled`, `pausedAt`, `snoozedUntil`, `startsOn`,
|
||
`endsOn`, `oneShot`, `createdAt`, `updatedAt`, `schedules`) are inlined; see the `Medication` component for
|
||
their semantics.
|
||
MedicationCategory:
|
||
type: string
|
||
enum:
|
||
- BLOOD_PRESSURE
|
||
- VITAMIN
|
||
- SUPPLEMENT
|
||
- PAIN_RELIEF
|
||
- ALLERGY
|
||
- DIGESTIVE
|
||
- THYROID
|
||
- HORMONE
|
||
- SKIN
|
||
- SLEEP_AID
|
||
- DIABETES
|
||
- ANTIBIOTIC
|
||
- OTHER
|
||
description: Clinical taxonomy stored in the `medication_categories` side-table. Orthogonal to `MedicationTreatmentClass`.
|
||
CreateMedicationResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/MedicationDetail"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
MedicationDetail:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
name:
|
||
type: string
|
||
dose:
|
||
type: string
|
||
treatmentClass:
|
||
$ref: "#/components/schemas/MedicationTreatmentClass"
|
||
dosesPerUnit:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
description: Doses per pen / vial for inventory tracking. NULL = inventory tracking off.
|
||
active:
|
||
type: boolean
|
||
notificationsEnabled:
|
||
type: boolean
|
||
liveActivityEnabled:
|
||
type: boolean
|
||
description: v1.7.0 iOS Live Activity opt-in for this medication's reminders. Default false. The iOS client owns the
|
||
ActivityKit lifecycle; the server only stores + echoes the flag.
|
||
criticalAlarmEnabled:
|
||
type: boolean
|
||
description: v1.7.0 iOS 26 AlarmKit critical-reminder opt-in. Default false. Critical alarms bypass the device mute
|
||
switch / Focus; the server stores the preference only.
|
||
atcCode:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: v1.9.0 optional WHO ATC classification code (active-substance class, e.g. `A10BX10`).
|
||
User/clinician-asserted; never machine-guessed. Emitted on the FHIR `medicationCodeableConcept` under
|
||
`http://www.whocc.no/atc`. NULL = no code captured.
|
||
rxNormCode:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: v1.9.0 optional RxNorm RxCUI (numeric, US identifier, e.g. `2601723`). Secondary FHIR coding under
|
||
`http://www.nlm.nih.gov/research/umls/rxnorm`, alongside any ATC code. NULL = no code captured.
|
||
pausedAt:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
snoozedUntil:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
nextDueAt:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
description: v1.7.0 server-computed next due instant across all the medication's schedules (earliest
|
||
`nextOccurrenceAfter`). Read-only — computed, not stored. NULL when no schedule has an upcoming slot
|
||
(paused, one-shot in the past, `endsOn` crossed, every schedule PRN). The list GET is cached 60 s, so a 60 s
|
||
staleness is accepted.
|
||
startsOn:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
description: v1.5 course start (ISO date). Anchors RRULE BYDAY / BYMONTHDAY patterns and the rolling-interval
|
||
countdown's first window. NULL means active from creation.
|
||
endsOn:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
description: v1.5 course end (ISO date). NULL means chronic. Equals `startsOn` when `oneShot` is true.
|
||
oneShot:
|
||
type: boolean
|
||
description: v1.5 single-administration flag. When true the medication has at most one schedule (no `rrule` /
|
||
`rollingIntervalDays`), and `active` auto-flips to false once the dose is logged.
|
||
createdAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
updatedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
schedules:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/MedicationScheduleOutput"
|
||
category:
|
||
$ref: "#/components/schemas/MedicationCategory"
|
||
required:
|
||
- id
|
||
- name
|
||
- dose
|
||
- treatmentClass
|
||
- dosesPerUnit
|
||
- active
|
||
- notificationsEnabled
|
||
- liveActivityEnabled
|
||
- criticalAlarmEnabled
|
||
- atcCode
|
||
- rxNormCode
|
||
- pausedAt
|
||
- snoozedUntil
|
||
- nextDueAt
|
||
- startsOn
|
||
- endsOn
|
||
- oneShot
|
||
- createdAt
|
||
- updatedAt
|
||
- schedules
|
||
- category
|
||
additionalProperties: false
|
||
description: Detail variant of the medication resource enriched with the joined `category`. The base medication fields
|
||
are inlined; see the `Medication` component for their semantics.
|
||
GetMedicationResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/MedicationDetail"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
UpdateMedicationResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/MedicationDetail"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
DeleteMedicationResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
deleted:
|
||
type: boolean
|
||
required:
|
||
- deleted
|
||
additionalProperties: false
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
ReplayMedicationIntakeResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/MedicationIntakeEvent"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
MedicationIntakeEvent:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
userId:
|
||
type: string
|
||
medicationId:
|
||
type: string
|
||
scheduledFor:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
takenAt:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
skipped:
|
||
type: boolean
|
||
source:
|
||
type: string
|
||
enum:
|
||
- WEB
|
||
- API
|
||
- REMINDER
|
||
- IMPORT
|
||
idempotencyKey:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
createdAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
required:
|
||
- id
|
||
- userId
|
||
- medicationId
|
||
- scheduledFor
|
||
- takenAt
|
||
- skipped
|
||
- source
|
||
- idempotencyKey
|
||
- createdAt
|
||
additionalProperties: false
|
||
description: Single dose log row. `takenAt` is non-null for confirmed intakes; `skipped:true` represents a
|
||
deliberately-missed dose (no inventory consumption).
|
||
CreateMedicationIntakeResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/MedicationIntakeEvent"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
MedicationExtractResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
name:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 100
|
||
dose:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 50
|
||
doseUnit:
|
||
type: string
|
||
enum:
|
||
- mg
|
||
- ml
|
||
- iu
|
||
- tablets
|
||
- drops
|
||
- puffs
|
||
- sprays
|
||
cadenceKind:
|
||
type: string
|
||
enum:
|
||
- daily
|
||
- weekdays
|
||
- everyNWeeks
|
||
- monthly
|
||
- everyNMonths
|
||
- yearly
|
||
- rolling
|
||
- oneShot
|
||
weekdays:
|
||
maxItems: 7
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MO
|
||
- TU
|
||
- WE
|
||
- TH
|
||
- FR
|
||
- SA
|
||
- SU
|
||
intervalWeeks:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 52
|
||
intervalMonths:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 12
|
||
dayOfMonth:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 31
|
||
rollingIntervalDays:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 365
|
||
timesOfDay:
|
||
maxItems: 12
|
||
type: array
|
||
items:
|
||
type: string
|
||
pattern: ^([01]\d|2[0-3]):[0-5]\d$
|
||
startsOn:
|
||
type: string
|
||
pattern: ^\d{4}-\d{2}-\d{2}$
|
||
endsOn:
|
||
type: string
|
||
pattern: ^\d{4}-\d{2}-\d{2}$
|
||
oneShot:
|
||
type: boolean
|
||
confidence:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties:
|
||
type: number
|
||
minimum: 0
|
||
maximum: 1
|
||
additionalProperties: false
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
GetMedicationCadenceResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/MedicationCadenceResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
MedicationCadenceResponse:
|
||
type: object
|
||
properties:
|
||
windowDays:
|
||
type: integer
|
||
exclusiveMinimum: 0
|
||
maximum: 9007199254740991
|
||
anchorIso:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
next:
|
||
anyOf:
|
||
- type: object
|
||
properties:
|
||
windowStart:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
windowEnd:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
scheduleIndex:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
required:
|
||
- windowStart
|
||
- windowEnd
|
||
- scheduleIndex
|
||
additionalProperties: false
|
||
- type: "null"
|
||
chips:
|
||
$ref: "#/components/schemas/MedicationCadenceChips"
|
||
timeline:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/MedicationCadenceTimelinePoint"
|
||
required:
|
||
- windowDays
|
||
- anchorIso
|
||
- next
|
||
- chips
|
||
- timeline
|
||
additionalProperties: false
|
||
description: Cadence + compliance read for a single medication. `next` is the upcoming-dose envelope (null when the
|
||
course has ended or the rolling clock has no pinning intake yet); `timeline` walks the requested `windowDays`
|
||
worth of slots in ascending time order.
|
||
MedicationCadenceChips:
|
||
type: object
|
||
properties:
|
||
adherenceRate:
|
||
type: number
|
||
streakDays:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
expectedSlots:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
actualDoses:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
required:
|
||
- adherenceRate
|
||
- streakDays
|
||
- expectedSlots
|
||
- actualDoses
|
||
additionalProperties: false
|
||
description: Four compliance summary values for the medication detail page chip row.
|
||
MedicationCadenceTimelinePoint:
|
||
type: object
|
||
properties:
|
||
day:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
windowStart:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
windowEnd:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
scheduleIndex:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
status:
|
||
type: string
|
||
required:
|
||
- day
|
||
- windowStart
|
||
- windowEnd
|
||
- scheduleIndex
|
||
- status
|
||
additionalProperties: false
|
||
description: One expected-vs-actual dose slot for the cadence timeline chart. `status` is one of `taken | skipped |
|
||
missed | pending | future` and drives the chip colour.
|
||
GetMedicationComplianceResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/MedicationComplianceResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
MedicationComplianceResponse:
|
||
type: object
|
||
properties:
|
||
compliance7:
|
||
$ref: "#/components/schemas/ComplianceResult"
|
||
compliance30:
|
||
$ref: "#/components/schemas/ComplianceResult"
|
||
dailyCompliance:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties:
|
||
$ref: "#/components/schemas/DailyComplianceEntry"
|
||
description: Flat per-day map keyed `YYYY-MM-DD` in the user timezone, one entry per day for up to 90 days back, clamped
|
||
to the medication's `createdAt` (so a recently-created med has fewer entries). No weekly/monthly collapse —
|
||
this is the raw daily grid.
|
||
complianceDisplay:
|
||
$ref: "#/components/schemas/ComplianceDisplay"
|
||
required:
|
||
- compliance7
|
||
- compliance30
|
||
- dailyCompliance
|
||
- complianceDisplay
|
||
additionalProperties: false
|
||
description: Adherence read for a single medication. `compliance30` is the authoritative 30-day taken-vs-expected
|
||
summary; `dailyCompliance` is the per-day grid for the history glyph track. The graded raw→week→month→year
|
||
series used elsewhere for AI prompts does NOT apply here — this response is never downsampled.
|
||
ComplianceResult:
|
||
type: object
|
||
properties:
|
||
totalExpected:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
description: "Full denominator over the window: `taken + skipped + missed`. Cadence-aware and clamped to the
|
||
medication's `createdAt`."
|
||
taken:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
skipped:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
description: Doses the user explicitly skipped — excluded from the `rate` denominator.
|
||
missed:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
rate:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 100
|
||
description: Adherence percentage `round(taken / (taken + missed) * 100)` — `skipped` is excluded from the denominator.
|
||
streak:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
description: Consecutive days with every due dose taken.
|
||
required:
|
||
- totalExpected
|
||
- taken
|
||
- skipped
|
||
- missed
|
||
- rate
|
||
- streak
|
||
additionalProperties: false
|
||
description: Rolling-window adherence summary. `compliance30` is the authoritative 'last 30 days, taken vs expected'
|
||
read — clients should display `rate` and use `totalExpected` as the denominator rather than re-deriving it from
|
||
the daily map.
|
||
DailyComplianceEntry:
|
||
type: object
|
||
properties:
|
||
expected:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
description: Engine-computed due-slot count for the day. Equals `expectedCount`; kept for existing consumers.
|
||
expectedCount:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
description: True due-slot count for the day (additive field clients key off so they don't infer due-ness from
|
||
`expected`).
|
||
due:
|
||
type: boolean
|
||
description: "`expectedCount > 0`. Paint a per-day glyph as expected/missed ONLY when `due === true`; off-cadence /
|
||
pre-creation / PRN days are not misses."
|
||
taken:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
skipped:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
onTime:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
description: Doses taken in the on-time band, including the `early` bucket (early counts as compliant).
|
||
late:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
veryLate:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
early:
|
||
description: Doses taken before the on-time band's grace start; already folded into `onTime`, surfaced separately for
|
||
consumers that differentiate.
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
required:
|
||
- expected
|
||
- expectedCount
|
||
- due
|
||
- taken
|
||
- skipped
|
||
- onTime
|
||
- late
|
||
- veryLate
|
||
additionalProperties: false
|
||
description: Per-day compliance cell with the timing breakdown that drives the history glyph track.
|
||
ComplianceDisplay:
|
||
type: object
|
||
properties:
|
||
shortDays:
|
||
type: integer
|
||
exclusiveMinimum: 0
|
||
maximum: 9007199254740991
|
||
longDays:
|
||
type: integer
|
||
exclusiveMinimum: 0
|
||
maximum: 9007199254740991
|
||
expectedShort:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
expectedLong:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
minStableDoses:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
short:
|
||
type: object
|
||
properties:
|
||
rate:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 100
|
||
streak:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 9007199254740991
|
||
required:
|
||
- rate
|
||
- streak
|
||
additionalProperties: false
|
||
long:
|
||
type: object
|
||
properties:
|
||
rate:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 100
|
||
required:
|
||
- rate
|
||
additionalProperties: false
|
||
currentCycle:
|
||
type: object
|
||
properties:
|
||
state:
|
||
type: string
|
||
enum:
|
||
- on_track
|
||
- due
|
||
- missed
|
||
- none
|
||
description: "Open-cycle state, decoupled from the percentage rows: `on_track` = next dose not yet due; `due` = due now
|
||
/ in grace; `missed` = past grace with no logged intake (the only red state); `none` = no projected next
|
||
dose (PRN / paused / ended)."
|
||
nextDueAt:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
description: The open cycle's due instant. Null when `state` is `none`.
|
||
graceUntil:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
description: End of the due slot's grace window. Null when `state` is `none`.
|
||
hasClosedCycles:
|
||
type: boolean
|
||
description: False for a brand-new sparse med with zero closed dose cycles — the percentage rows are vacuous and the
|
||
card should show a neutral 'not enough data yet' state.
|
||
required:
|
||
- state
|
||
- nextDueAt
|
||
- graceUntil
|
||
- hasClosedCycles
|
||
additionalProperties: false
|
||
description: v1.13.x — the current (open) dose cycle, surfaced so a between-doses sparse med renders a neutral 'next
|
||
dose in N days' line instead of a scary red 0%. The percentage rows above already exclude the open forward
|
||
cycle from their denominator.
|
||
required:
|
||
- shortDays
|
||
- longDays
|
||
- expectedShort
|
||
- expectedLong
|
||
- minStableDoses
|
||
- short
|
||
- long
|
||
- currentCycle
|
||
additionalProperties: false
|
||
description: The two-row card block whose windows scale with dosing cadence (dense meds keep 7 / 30 days, sparse meds
|
||
step both windows up). NOT the 30-day denominator — read `compliance30.totalExpected` for that.
|
||
GetCoachPrefsResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
tone:
|
||
default: warm
|
||
type: string
|
||
enum:
|
||
- warm
|
||
- neutral
|
||
- concise
|
||
verbosity:
|
||
default: default
|
||
type: string
|
||
enum:
|
||
- brief
|
||
- default
|
||
- detailed
|
||
excludeMetrics:
|
||
default: []
|
||
maxItems: 11
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- bp
|
||
- weight
|
||
- pulse
|
||
- mood
|
||
- compliance
|
||
- hrv
|
||
- sleep
|
||
- resting_hr
|
||
- steps
|
||
- medications
|
||
- anthropometrics
|
||
showEvidenceByDefault:
|
||
default: false
|
||
type: boolean
|
||
defaultWindow:
|
||
default: allTime
|
||
type: string
|
||
enum:
|
||
- last7days
|
||
- last30days
|
||
- last90days
|
||
- allTime
|
||
dataClusters:
|
||
maxItems: 10
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- cardio
|
||
- body
|
||
- activity
|
||
- workouts
|
||
- sleep
|
||
- mood
|
||
- glucose
|
||
- medication
|
||
- mobility
|
||
- environment
|
||
required:
|
||
- tone
|
||
- verbosity
|
||
- excludeMetrics
|
||
- showEvidenceByDefault
|
||
- defaultWindow
|
||
additionalProperties: false
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
PutCoachPrefsResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
tone:
|
||
default: warm
|
||
type: string
|
||
enum:
|
||
- warm
|
||
- neutral
|
||
- concise
|
||
verbosity:
|
||
default: default
|
||
type: string
|
||
enum:
|
||
- brief
|
||
- default
|
||
- detailed
|
||
excludeMetrics:
|
||
default: []
|
||
maxItems: 11
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- bp
|
||
- weight
|
||
- pulse
|
||
- mood
|
||
- compliance
|
||
- hrv
|
||
- sleep
|
||
- resting_hr
|
||
- steps
|
||
- medications
|
||
- anthropometrics
|
||
showEvidenceByDefault:
|
||
default: false
|
||
type: boolean
|
||
defaultWindow:
|
||
default: allTime
|
||
type: string
|
||
enum:
|
||
- last7days
|
||
- last30days
|
||
- last90days
|
||
- allTime
|
||
dataClusters:
|
||
maxItems: 10
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- cardio
|
||
- body
|
||
- activity
|
||
- workouts
|
||
- sleep
|
||
- mood
|
||
- glucose
|
||
- medication
|
||
- mobility
|
||
- environment
|
||
required:
|
||
- tone
|
||
- verbosity
|
||
- excludeMetrics
|
||
- showEvidenceByDefault
|
||
- defaultWindow
|
||
additionalProperties: false
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
GetDisableCoachResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/DisableCoachFlagOutput"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
PatchDisableCoachResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/DisableCoachFlagOutput"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
GetSourcePriorityResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
steps:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
activeEnergy:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
walkingRunningDistance:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
flightsClimbed:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
sleep:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
weight:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bloodPressure:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
pulse:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bodyFat:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bodyTemperature:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
spo2:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
hrv:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
restingHeartRate:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
vo2Max:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
skinTemperature:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
respiratoryRate:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
recovery:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
metricPriority:
|
||
type: object
|
||
properties:
|
||
steps:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
activeEnergy:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
walkingRunningDistance:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
flightsClimbed:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
sleep:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
weight:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bloodPressure:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
pulse:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bodyFat:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bodyTemperature:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
spo2:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
hrv:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
restingHeartRate:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
vo2Max:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
skinTemperature:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
respiratoryRate:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
recovery:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
additionalProperties: false
|
||
deviceTypePriority:
|
||
type: object
|
||
properties:
|
||
default:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- watch
|
||
- band
|
||
- ring
|
||
- phone
|
||
- scale
|
||
- other
|
||
- unknown
|
||
additionalProperties:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- watch
|
||
- band
|
||
- ring
|
||
- phone
|
||
- scale
|
||
- other
|
||
- unknown
|
||
additionalProperties: false
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
PutSourcePriorityResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
steps:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
activeEnergy:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
walkingRunningDistance:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
flightsClimbed:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
sleep:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
weight:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bloodPressure:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
pulse:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bodyFat:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bodyTemperature:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
spo2:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
hrv:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
restingHeartRate:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
vo2Max:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
skinTemperature:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
respiratoryRate:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
recovery:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
metricPriority:
|
||
type: object
|
||
properties:
|
||
steps:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
activeEnergy:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
walkingRunningDistance:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
flightsClimbed:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
sleep:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
weight:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bloodPressure:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
pulse:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bodyFat:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
bodyTemperature:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
spo2:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
hrv:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
restingHeartRate:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
vo2Max:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
skinTemperature:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
respiratoryRate:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
recovery:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- MANUAL
|
||
- WITHINGS
|
||
- IMPORT
|
||
- APPLE_HEALTH
|
||
- COMPUTED
|
||
- WHOOP
|
||
- FITBIT
|
||
additionalProperties: false
|
||
deviceTypePriority:
|
||
type: object
|
||
properties:
|
||
default:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- watch
|
||
- band
|
||
- ring
|
||
- phone
|
||
- scale
|
||
- other
|
||
- unknown
|
||
additionalProperties:
|
||
maxItems: 8
|
||
type: array
|
||
items:
|
||
type: string
|
||
enum:
|
||
- watch
|
||
- band
|
||
- ring
|
||
- phone
|
||
- scale
|
||
- other
|
||
- unknown
|
||
additionalProperties: false
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
UploadAvatarResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
avatarUrl:
|
||
type: string
|
||
contentType:
|
||
type: string
|
||
enum:
|
||
- image/jpeg
|
||
- image/png
|
||
- image/webp
|
||
updatedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
required:
|
||
- avatarUrl
|
||
- contentType
|
||
- updatedAt
|
||
additionalProperties: false
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
GetProfileResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/ProfileResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
ProfileResponse:
|
||
type: object
|
||
properties:
|
||
username:
|
||
type: string
|
||
displayName:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
email:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
dateOfBirth:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
gender:
|
||
anyOf:
|
||
- type: string
|
||
enum:
|
||
- MALE
|
||
- FEMALE
|
||
- type: "null"
|
||
heightCm:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
locale:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
timezone:
|
||
type: string
|
||
moodReminderEnabled:
|
||
type: boolean
|
||
fullName:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
insurerName:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
insurerIkNumber:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: German insurer institution number (IKNR), 9 digits.
|
||
insuranceNumber:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: German KVNR, decrypted for the form prefill.
|
||
required:
|
||
- username
|
||
- email
|
||
- dateOfBirth
|
||
- gender
|
||
- heightCm
|
||
- locale
|
||
- timezone
|
||
- moodReminderEnabled
|
||
- fullName
|
||
- insurerName
|
||
- insurerIkNumber
|
||
- insuranceNumber
|
||
additionalProperties: false
|
||
description: Flattened profile fields for the native client (GET). The KVNR is decrypted server-side; the IKNR (v1.8.6)
|
||
is plaintext at rest.
|
||
PatchProfileResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/ProfileUpdateResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
ProfileUpdateResponse:
|
||
type: object
|
||
properties:
|
||
username:
|
||
type: string
|
||
displayName:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
email:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
dateOfBirth:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
gender:
|
||
anyOf:
|
||
- type: string
|
||
enum:
|
||
- MALE
|
||
- FEMALE
|
||
- type: "null"
|
||
heightCm:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
locale:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
timezone:
|
||
type: string
|
||
moodReminderEnabled:
|
||
type: boolean
|
||
fullName:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
insurerName:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
insurerIkNumber:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: German insurer institution number (IKNR), 9 digits.
|
||
hasInsuranceNumber:
|
||
type: boolean
|
||
description: Whether a KVNR is on file (the value itself is not echoed).
|
||
required:
|
||
- username
|
||
- email
|
||
- dateOfBirth
|
||
- gender
|
||
- heightCm
|
||
- locale
|
||
- timezone
|
||
- moodReminderEnabled
|
||
- fullName
|
||
- insurerName
|
||
- insurerIkNumber
|
||
- hasInsuranceNumber
|
||
additionalProperties: false
|
||
description: Profile echo returned by the PATCH/PUT update path. Reports KVNR presence as a boolean rather than
|
||
re-emitting the decrypted value.
|
||
InsightsLayout:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/InsightsLayoutBodyOutput"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
InsightsLayoutSaved:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/InsightsLayoutBodyOutput"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
InsightsLayoutReset:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/InsightsLayoutBodyOutput"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
CoachFactsList:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
facts:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
category:
|
||
type: string
|
||
enum:
|
||
- preference
|
||
- condition
|
||
- goal
|
||
- constraint
|
||
- context
|
||
description: "App-side closed category: preference | condition | goal | constraint | context."
|
||
text:
|
||
type: string
|
||
description: Decrypted fact text.
|
||
confidence:
|
||
type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
description: 0..100 server-assigned extraction confidence.
|
||
createdAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
required:
|
||
- id
|
||
- category
|
||
- text
|
||
- confidence
|
||
- createdAt
|
||
additionalProperties: false
|
||
description: The caller's active facts, highest-confidence then newest first. Undecryptable rows are omitted.
|
||
required:
|
||
- facts
|
||
additionalProperties: false
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
CoachFactsCleared:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
cleared:
|
||
type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
description: Number of active facts soft-deleted by the bulk clear.
|
||
required:
|
||
- cleared
|
||
additionalProperties: false
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
CoachFactDeleted:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
deleted:
|
||
type: boolean
|
||
description: True when a fact owned by the caller was soft-deleted; false for an unknown / cross-user / already-deleted
|
||
id (idempotent no-op).
|
||
required:
|
||
- deleted
|
||
additionalProperties: false
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
CoachMessageFeedbackResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
createdAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
required:
|
||
- id
|
||
- createdAt
|
||
additionalProperties: false
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
AdminDiagnosticResponse:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/AdminDiagnosticData"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
AdminDiagnosticData:
|
||
type: object
|
||
properties:
|
||
devices:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/AdminDiagnosticDevice"
|
||
notificationChannels:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/AdminDiagnosticChannel"
|
||
recentPushAttempts:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/AdminDiagnosticPushAttempt"
|
||
required:
|
||
- devices
|
||
- notificationChannels
|
||
- recentPushAttempts
|
||
additionalProperties: false
|
||
description: Admin notification diagnostic snapshot for the calling user — what the dispatcher would see when targeting
|
||
this account. Surfaces device tokens (masked), channel state, and recent push attempts so an operator can debug
|
||
an iOS / Web Push / Telegram / ntfy issue without DB shell access.
|
||
AdminDiagnosticDevice:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
platform:
|
||
type: string
|
||
hasApnsToken:
|
||
type: boolean
|
||
apnsTokenPrefix:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
apnsTokenSuffix:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
apnsEnvironment:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
lastSeenAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
required:
|
||
- id
|
||
- platform
|
||
- hasApnsToken
|
||
- apnsTokenPrefix
|
||
- apnsTokenSuffix
|
||
- apnsEnvironment
|
||
- lastSeenAt
|
||
additionalProperties: false
|
||
description: Per-device APNs registration snapshot. The APNs token is masked to its 8-char hex prefix + 8-char suffix so
|
||
an operator can correlate with iOS-side logs without disclosing the delivery target.
|
||
AdminDiagnosticChannel:
|
||
type: object
|
||
properties:
|
||
type:
|
||
type: string
|
||
enabled:
|
||
type: boolean
|
||
configPresent:
|
||
type: boolean
|
||
required:
|
||
- type
|
||
- enabled
|
||
- configPresent
|
||
additionalProperties: false
|
||
description: Per-channel state. `configPresent` is true when the channel's encrypted-JSON config blob carries the field
|
||
the corresponding sender will read (chatId+botToken for TELEGRAM, topic for NTFY); WEB_PUSH/APNS rely on sibling
|
||
tables so the row's existence is the signal.
|
||
AdminDiagnosticPushAttempt:
|
||
type: object
|
||
properties:
|
||
eventType:
|
||
type: string
|
||
channel:
|
||
type: string
|
||
result:
|
||
type: string
|
||
reason:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
at:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
required:
|
||
- eventType
|
||
- channel
|
||
- result
|
||
- reason
|
||
- at
|
||
additionalProperties: false
|
||
description: Single push delivery attempt. Reserved for the v1.4.48+ follow-up that adds the dedicated PushAttempt table
|
||
— currently always returned as an empty array so the response shape stays stable for iOS consumers.
|
||
DashboardSnapshotResponseEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/DashboardSnapshotResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
DashboardSnapshotResponse:
|
||
type: object
|
||
properties:
|
||
user:
|
||
type: object
|
||
properties:
|
||
username:
|
||
type: string
|
||
timezone:
|
||
type: string
|
||
heightCm:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
dateOfBirth:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
gender:
|
||
anyOf:
|
||
- type: string
|
||
enum:
|
||
- MALE
|
||
- FEMALE
|
||
- type: "null"
|
||
glucoseUnit:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
onboardingTourCompleted:
|
||
type: boolean
|
||
greetingHour:
|
||
type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
required:
|
||
- username
|
||
- timezone
|
||
- heightCm
|
||
- dateOfBirth
|
||
- gender
|
||
- glucoseUnit
|
||
- onboardingTourCompleted
|
||
- greetingHour
|
||
additionalProperties: false
|
||
layout:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties: {}
|
||
layoutCatalogue:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
visible:
|
||
type: boolean
|
||
order:
|
||
type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
required:
|
||
- id
|
||
- visible
|
||
- order
|
||
additionalProperties: false
|
||
description: Full 27-id widget catalogue (server-known + iOS-only) with per-widget visibility + order. iOS-only ids are
|
||
appended default-invisible. The web dashboard reads `layout`; this block is the cold-launch seed for the
|
||
native client.
|
||
metricStates:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties:
|
||
type: object
|
||
properties:
|
||
value:
|
||
type: number
|
||
measuredAt:
|
||
type: string
|
||
unit:
|
||
type: string
|
||
required:
|
||
- value
|
||
- measuredAt
|
||
- unit
|
||
additionalProperties: false
|
||
description: "Latest reading per chartable metric, keyed by the iOS `MetricKind` raw value (the non-obvious raws:
|
||
`oxygenSaturation`, `totalBodyWater`, `heartRateVariability`, `bodyMassIndex`, `walkingAsymmetryPercentage`,
|
||
`walkingDoubleSupportPercentage`, `environmentalAudioExposure`, `headphoneAudioExposure`,
|
||
`activeEnergyBurned`). Each entry carries `value`, `measuredAt` (ISO8601), and the canonical `unit`. Types
|
||
the user has never logged are omitted."
|
||
tiles:
|
||
type: object
|
||
properties:
|
||
summaries:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties: {}
|
||
lastSeenByType:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties: {}
|
||
mood:
|
||
type: object
|
||
properties:
|
||
summary:
|
||
anyOf:
|
||
- type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties: {}
|
||
- type: "null"
|
||
entries:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
date:
|
||
type: string
|
||
score:
|
||
type: number
|
||
samples:
|
||
type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
required:
|
||
- date
|
||
- score
|
||
- samples
|
||
additionalProperties: false
|
||
required:
|
||
- summary
|
||
- entries
|
||
additionalProperties: false
|
||
required:
|
||
- summaries
|
||
- lastSeenByType
|
||
- mood
|
||
additionalProperties: false
|
||
extras:
|
||
anyOf:
|
||
- type: object
|
||
properties:
|
||
bpInTargetPct:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
bpInTargetPct7d:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
bpInTargetPct30d:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
bpInTargetPctAllTime:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
bpInTargetPctPriorMonth:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
bpInTargetPctPriorYear:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
glucoseByContext:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties: {}
|
||
required:
|
||
- bpInTargetPct
|
||
- bpInTargetPct7d
|
||
- bpInTargetPct30d
|
||
- bpInTargetPctAllTime
|
||
- bpInTargetPctPriorMonth
|
||
- bpInTargetPctPriorYear
|
||
- glucoseByContext
|
||
additionalProperties: false
|
||
- type: "null"
|
||
briefing:
|
||
anyOf:
|
||
- type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties: {}
|
||
- type: "null"
|
||
briefingState:
|
||
type: string
|
||
enum:
|
||
- ready
|
||
- preparing
|
||
- disabled
|
||
briefingUpdatedAt:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
generatedAt:
|
||
type: string
|
||
required:
|
||
- user
|
||
- layout
|
||
- layoutCatalogue
|
||
- metricStates
|
||
- tiles
|
||
- extras
|
||
- briefing
|
||
- briefingState
|
||
- briefingUpdatedAt
|
||
- generatedAt
|
||
additionalProperties: false
|
||
description: Unified above-the-fold dashboard payload. `tiles` always arrives (slim summaries + mood + resolved widget
|
||
layout); `extras` (BD-in-target + per-context glucose) is null on a rollup-coverage miss so the strip never
|
||
waits on the slowest read. `briefing` is lifted read-only from the pre-generated insight cache — never generated
|
||
synchronously — and reports `ready` / `preparing` / `disabled` via `briefingState`. `layoutCatalogue` (full
|
||
27-id widget catalogue) and `metricStates` (latest reading per metric, keyed by iOS `MetricKind` raw value) are
|
||
additive cold-launch seeds for the native client; both derive in-process from data already fetched, adding no DB
|
||
round-trip.
|
||
InsightsComprehensiveResponseEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/InsightsComprehensiveResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
InsightsComprehensiveResponse:
|
||
type: object
|
||
properties:
|
||
summary:
|
||
type: string
|
||
recommendations:
|
||
type: array
|
||
items:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties: {}
|
||
citations:
|
||
type: array
|
||
items:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties: {}
|
||
warnings:
|
||
type: array
|
||
items:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties: {}
|
||
dailyBriefing:
|
||
anyOf:
|
||
- type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties: {}
|
||
- type: "null"
|
||
trendAnnotations:
|
||
anyOf:
|
||
- type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties: {}
|
||
- type: "null"
|
||
storyboardAnnotations:
|
||
type: array
|
||
items:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties: {}
|
||
metricSource:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties: {}
|
||
required:
|
||
- summary
|
||
- recommendations
|
||
- citations
|
||
- warnings
|
||
additionalProperties: false
|
||
description: AI-generated insights bundle. Strict-schema validated server-side; Coach-routed when the insight surface
|
||
needs day-level grounding.
|
||
InsightsPregenerateResponseEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/InsightsPregenerateResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
InsightsPregenerateResponse:
|
||
type: object
|
||
properties:
|
||
queued:
|
||
type: boolean
|
||
description: True when the full warm was accepted and enqueued.
|
||
locale:
|
||
type: string
|
||
enum:
|
||
- de
|
||
- en
|
||
description: The locale the assessments are being warmed in.
|
||
required:
|
||
- queued
|
||
- locale
|
||
additionalProperties: false
|
||
description: Acknowledgement that a full assessment warm was enqueued for the calling user. The generation runs out of
|
||
band; the text lands in the read-only status routes.
|
||
AnalyticsRangeResponseEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/AnalyticsRangeResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
AnalyticsRangeResponse:
|
||
type: object
|
||
properties:
|
||
range:
|
||
type: string
|
||
enum:
|
||
- 7d
|
||
- 30d
|
||
- 90d
|
||
- 1y
|
||
description: The range that was read (echoes the request).
|
||
windowDays:
|
||
type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
description: Trailing-window length in days for the chosen range.
|
||
granularity:
|
||
type: string
|
||
description: Rollup granularity the read resolved against (`DAY` / `WEEK` / `MONTH` / `YEAR`, or `none` on a coverage
|
||
miss).
|
||
current:
|
||
description: Aggregate over the current window `[now-N, now)`.
|
||
$ref: "#/components/schemas/AnalyticsWindowAggregate"
|
||
previous:
|
||
description: Aggregate over the previous comparable window `[now-2N, now-N)`.
|
||
$ref: "#/components/schemas/AnalyticsWindowAggregate"
|
||
delta:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
description: "`current.mean - previous.mean`; null when either window has no data (never a misleading 0)."
|
||
deltaPct:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
description: "`delta / previous.mean` as a fraction (0.03 = +3 %); null when the prior window has no / zero mean (no
|
||
divide-by-zero). The client shows 'no prior-period data' in that case."
|
||
required:
|
||
- range
|
||
- windowDays
|
||
- granularity
|
||
- current
|
||
- previous
|
||
- delta
|
||
- deltaPct
|
||
additionalProperties: false
|
||
description: Single-metric period-over-period aggregate. Reads the current and previous comparable windows from the WMY
|
||
rollup tier and composes a count-weighted-mean delta. `count/min/max/mean/sum` are linearly composable across
|
||
buckets; SD/slope/r² are intentionally excluded (not composable).
|
||
AnalyticsWindowAggregate:
|
||
type: object
|
||
properties:
|
||
count:
|
||
type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
description: Reading count composed across buckets.
|
||
min:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
description: Window minimum; null when empty.
|
||
max:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
description: Window maximum; null when empty.
|
||
mean:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
description: Count-weighted mean across buckets; null when empty.
|
||
sum:
|
||
anyOf:
|
||
- type: number
|
||
- type: "null"
|
||
description: Cumulative total for cumulative metrics (steps, energy, distance); null when no bucket carries a sum.
|
||
required:
|
||
- count
|
||
- min
|
||
- max
|
||
- mean
|
||
- sum
|
||
additionalProperties: false
|
||
BloodPressureStatusResponseEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/InsightStatusResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
InsightStatusResponse:
|
||
type: object
|
||
properties:
|
||
hasProvider:
|
||
type: boolean
|
||
description: False when the user has no usable AI provider — `text` then carries the generic no-key guidance.
|
||
text:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: The assessment narrative (plain text, rendered as React text children). Null while a first generation is
|
||
preparing.
|
||
cached:
|
||
type: boolean
|
||
description: True when `text` is served from cache (incl. last-good).
|
||
updatedAt:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
description: When the served assessment was generated; null when none.
|
||
preparing:
|
||
description: True when a first assessment is being generated out of band and no prior text exists yet — the client polls
|
||
until it lands.
|
||
type: boolean
|
||
revalidating:
|
||
description: True when `text` is served from last-good cache (stale-while-revalidate) while a fresh generation is in
|
||
flight. The client keeps polling on `preparing || revalidating` (bounded) so the open card upgrades to the
|
||
warmed assessment without a remount.
|
||
type: boolean
|
||
required:
|
||
- hasProvider
|
||
- text
|
||
- cached
|
||
- updatedAt
|
||
additionalProperties: false
|
||
description: "Specialised per-metric assessment envelope (blood-pressure, pulse, weight, bmi, mood). Identical shape to
|
||
the generic metric-status card so the `InsightStatusCard` consumes it unchanged. Read-only +
|
||
stale-while-revalidate: a cache miss warms a generation out of band and serves the last-good text meanwhile."
|
||
PulseStatusResponseEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/InsightStatusResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
WeightStatusResponseEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/InsightStatusResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
BmiStatusResponseEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/InsightStatusResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
MoodStatusResponseEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/InsightStatusResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
MedicationComplianceStatusResponseEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/MedicationComplianceStatusResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
MedicationComplianceStatusResponse:
|
||
type: object
|
||
properties:
|
||
hasProvider:
|
||
type: boolean
|
||
description: False when the user has no usable AI provider — `summary` then carries the generic no-key guidance.
|
||
summary:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: The overall compliance narrative (plain text). Null while a first generation is preparing.
|
||
medications:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/MedicationComplianceStatusItem"
|
||
description: Per-medication compliance notes. Empty while preparing or when no medication qualifies.
|
||
cached:
|
||
type: boolean
|
||
description: True when the envelope is served from cache (incl. last-good).
|
||
updatedAt:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
description: When the served assessment was generated; null when none.
|
||
preparing:
|
||
description: True when a first assessment is being generated out of band and no prior summary exists yet — the client
|
||
polls until it lands.
|
||
type: boolean
|
||
revalidating:
|
||
description: True when the envelope is served from last-good cache (stale-while-revalidate) while a fresh generation is
|
||
in flight. The client keeps polling on `preparing || revalidating` (bounded).
|
||
type: boolean
|
||
required:
|
||
- hasProvider
|
||
- summary
|
||
- medications
|
||
- cached
|
||
- updatedAt
|
||
additionalProperties: false
|
||
description: Medication-compliance assessment envelope. Unlike the other six specialised cards it carries a `summary`
|
||
plus a per-medication `text` array rather than a single `text` field. Read-only + stale-while-revalidate.
|
||
MedicationComplianceStatusItem:
|
||
type: object
|
||
properties:
|
||
medicationId:
|
||
type: string
|
||
description: The medication this note belongs to.
|
||
text:
|
||
type: string
|
||
description: Per-medication compliance note (plain text).
|
||
required:
|
||
- medicationId
|
||
- text
|
||
additionalProperties: false
|
||
MetricStatusResponseEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/MetricStatusResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
MetricStatusResponse:
|
||
type: object
|
||
properties:
|
||
hasProvider:
|
||
type: boolean
|
||
description: False when the user has no usable AI provider — `text` then carries the generic no-key guidance.
|
||
text:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: The assessment narrative (plain text, rendered as React text children). Null while a first generation is
|
||
preparing, or when the metric has insufficient data.
|
||
cached:
|
||
type: boolean
|
||
description: True when `text` is served from cache (incl. last-good).
|
||
updatedAt:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
description: When the served assessment was generated; null when none.
|
||
preparing:
|
||
description: True when a first assessment is being generated out of band and no prior text exists yet — the client polls
|
||
until it lands.
|
||
type: boolean
|
||
revalidating:
|
||
description: True when `text` is served from last-good cache (stale-while-revalidate) while a fresh generation is in
|
||
flight. The payload is otherwise terminal; the client keeps polling on `preparing || revalidating` (bounded)
|
||
so the open card upgrades to the warmed assessment without a remount.
|
||
type: boolean
|
||
insufficient:
|
||
description: True when the metric has no readings; no assessment is generated (no LLM call). The card shows its
|
||
insufficient-data state.
|
||
type: boolean
|
||
required:
|
||
- hasProvider
|
||
- text
|
||
- cached
|
||
- updatedAt
|
||
additionalProperties: false
|
||
description: "Generic per-metric assessment envelope. Identical shape to the seven specialised `*-status` cards so the
|
||
`InsightStatusCard` consumes it unchanged. Read-only + stale-while-revalidate: a cache miss warms a generation
|
||
out of band and serves the last-good text meanwhile."
|
||
DerivedMetricResponseEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/DerivedMetricResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
DerivedMetricResponse:
|
||
type: object
|
||
properties:
|
||
metric:
|
||
type: string
|
||
enum:
|
||
- VITALS_BASELINE
|
||
- FITNESS_AGE
|
||
- VASCULAR_AGE_DELTA
|
||
- SLEEP_SCORE
|
||
- READINESS
|
||
- COINCIDENT_DEVIATION
|
||
- HRV_BALANCE
|
||
- BMI
|
||
- RECOVERY_SCORE
|
||
- STRESS_SCORE
|
||
- STRAIN_SCORE
|
||
- WRIST_TEMPERATURE_BASELINE
|
||
- STAIR_ASCENT_SPEED_BASELINE
|
||
- STAIR_DESCENT_SPEED_BASELINE
|
||
- SIX_MINUTE_WALK_BAND
|
||
- TRAJECTORY
|
||
description: Echoes the requested derived-metric id (tags the union).
|
||
status:
|
||
type: string
|
||
enum:
|
||
- ok
|
||
- insufficient
|
||
description: "'ok' carries `value` + `confidence`; 'insufficient' carries `reason` and no value, but still carries
|
||
`coverage` + `provenance` so the surface renders the same gating UI."
|
||
value:
|
||
anyOf:
|
||
- type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties: {}
|
||
- type: "null"
|
||
description: Metric-specific value object when status is 'ok' (e.g. { type, center, low, high, spread, sampleDays, k,
|
||
series } for VITALS_BASELINE, where `series` is the trailing per-day mean values for the inline sparkline);
|
||
null when 'insufficient'.
|
||
coverage:
|
||
$ref: "#/components/schemas/DerivedCoverage"
|
||
confidence:
|
||
anyOf:
|
||
- $ref: "#/components/schemas/DerivedConfidence"
|
||
- type: "null"
|
||
description: Present when status is 'ok'; null when 'insufficient'.
|
||
provenance:
|
||
$ref: "#/components/schemas/DerivedProvenance"
|
||
reason:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: Why the value could not be produced; null when status is 'ok'.
|
||
required:
|
||
- metric
|
||
- status
|
||
- value
|
||
- coverage
|
||
- confidence
|
||
- provenance
|
||
- reason
|
||
additionalProperties: false
|
||
description: Flat `Derived<T>` envelope for one derived wellness metric. Pure compute over the rollup tier (no LLM, no
|
||
narrative). iOS decodes one stable shape and combines values across metrics; coverage/confidence/provenance let
|
||
it render the same honesty chips.
|
||
DerivedCoverage:
|
||
type: object
|
||
properties:
|
||
requiredInputs:
|
||
type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
description: Inputs the metric wants (its full input set).
|
||
presentInputs:
|
||
type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
description: Inputs actually present in the user's data.
|
||
historyDays:
|
||
type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
description: Distinct days of history backing the value (the gating floor).
|
||
missing:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: Named inputs still missing — drives the 'track N more' nudge.
|
||
required:
|
||
- requiredInputs
|
||
- presentInputs
|
||
- historyDays
|
||
- missing
|
||
additionalProperties: false
|
||
DerivedConfidence:
|
||
type: object
|
||
properties:
|
||
score:
|
||
type: number
|
||
description: 0..100 confidence; feeds the shared coverage meter unchanged.
|
||
band:
|
||
type: string
|
||
enum:
|
||
- high
|
||
- medium
|
||
- low
|
||
- draft
|
||
description: Confidence band the meter renders.
|
||
required:
|
||
- score
|
||
- band
|
||
additionalProperties: false
|
||
DerivedProvenance:
|
||
type: object
|
||
properties:
|
||
inputs:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: Named inputs that actually backed the value.
|
||
source:
|
||
type: string
|
||
enum:
|
||
- DAY
|
||
- WEEK
|
||
- MONTH
|
||
- YEAR
|
||
- live
|
||
- none
|
||
description: Granularity the dominant read resolved against. 'live' = a coverage-miss live-SQL fallback; 'none' = no
|
||
data backed the value.
|
||
windowDays:
|
||
type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
description: Trailing window the value summarises, in days.
|
||
computedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
description: Compute time (for cache-staleness + the 'as of' chip).
|
||
required:
|
||
- inputs
|
||
- source
|
||
- windowDays
|
||
- computedAt
|
||
additionalProperties: false
|
||
DerivedBatchResponseEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/DerivedBatchResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
DerivedBatchResponse:
|
||
type: object
|
||
properties:
|
||
metrics:
|
||
type: object
|
||
propertyNames:
|
||
type: string
|
||
additionalProperties:
|
||
$ref: "#/components/schemas/DerivedMetricResponse"
|
||
description: Map keyed by the per-request token (`<metric>` or `<metric>:<type>`). Each value is the same flat
|
||
`Derived<T>` envelope the single-metric route returns, so a client decodes one shape and reads back exactly
|
||
the tokens it asked for.
|
||
required:
|
||
- metrics
|
||
additionalProperties: false
|
||
description: Batched derived-metric values. One request resolves the whole dashboard grid (the wellness scores + the
|
||
derived re-frames + one baseline per vital) instead of N concurrent single-metric requests sharing the Prisma
|
||
pool. Pure compute over the rollup tier — no LLM, no narrative, no cache table.
|
||
CorrelationDiscoveryResponseEnvelope:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/CorrelationDiscoveryResponse"
|
||
error:
|
||
type: "null"
|
||
meta:
|
||
type: object
|
||
properties:
|
||
requestId:
|
||
type: string
|
||
additionalProperties: false
|
||
required:
|
||
- data
|
||
- error
|
||
additionalProperties: false
|
||
CorrelationDiscoveryResponse:
|
||
type: object
|
||
properties:
|
||
discovered:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/DiscoveredCorrelation"
|
||
description: Pairs surviving n ≥ 20, p < 0.05, AND the BH-FDR control.
|
||
pairsTested:
|
||
type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
description: Behaviour × outcome pairs assessed (for the honest footer).
|
||
fdrQ:
|
||
type: number
|
||
description: The FDR target the surface used.
|
||
minPairs:
|
||
type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
description: Minimum paired-day count enforced per pair.
|
||
required:
|
||
- discovered
|
||
- pairsTested
|
||
- fdrQ
|
||
- minPairs
|
||
additionalProperties: false
|
||
description: v1.10.0 — FDR-controlled correlation discovery over a curated behaviour × outcome matrix, lagged behaviour
|
||
→ next-day outcome. Only statistically-defensible pairs surface; descriptive, never causal.
|
||
DiscoveredCorrelation:
|
||
type: object
|
||
properties:
|
||
behaviour:
|
||
type: string
|
||
description: Behaviour channel (lag source), e.g. TIME_IN_DAYLIGHT, MOOD.
|
||
outcome:
|
||
type: string
|
||
description: Outcome channel (lag target), e.g. SLEEP_DURATION, HEART_RATE_VARIABILITY.
|
||
n:
|
||
type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
description: Paired-day count after the day+1 lag join (≥ 20).
|
||
r:
|
||
type: number
|
||
description: Pearson r over the lag-joined daily series.
|
||
pValue:
|
||
type: number
|
||
description: Two-sided exact Student-t p-value (< 0.05).
|
||
qValue:
|
||
type: number
|
||
description: Benjamini-Hochberg FDR-adjusted q-value (≤ the surface threshold).
|
||
interpretation:
|
||
type: string
|
||
description: Conservative, descriptive interpretation — never causal.
|
||
lagDays:
|
||
type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
description: Lag in days applied (1).
|
||
required:
|
||
- behaviour
|
||
- outcome
|
||
- n
|
||
- r
|
||
- pValue
|
||
- qValue
|
||
- interpretation
|
||
- lagDays
|
||
additionalProperties: false
|
||
MedicationScheduleOutput:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
medicationId:
|
||
type: string
|
||
windowStart:
|
||
type: string
|
||
description: Legacy single-time-of-intake (HH:mm, user local). Preserved for backwards compatibility; the new
|
||
`timesOfDay` array supersedes it.
|
||
windowEnd:
|
||
type: string
|
||
description: Legacy reminder-window upper bound (HH:mm). Used to derive the late-classification grace span when
|
||
`reminderGraceMinutes` is null.
|
||
label:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
dose:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: Per-schedule dose override. NULL means the schedule inherits `Medication.dose`.
|
||
daysOfWeek:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: Legacy persisted recurrence encoding (`null` | `1,3,5` | `i2;1,3,5`). v1.5 readers consult `rrule` first;
|
||
the field is kept for pre-v1.5 rows. v1.6.0 drops the column.
|
||
timesOfDay:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: v1.5 first-class points-in-time the dose is taken (HH:mm, user local). Backfilled to `[windowStart]` for
|
||
every pre-v1.5 row.
|
||
reminderGraceMinutes:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
description: Reminder grace window in minutes. NULL falls back to the legacy `windowEnd - windowStart` span.
|
||
rrule:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: RFC 5545 RRULE string (subset). Used for calendar-anchored cadences. **Mutually exclusive with
|
||
`rollingIntervalDays`** — exactly one of the two is non-null on any v1.5+ schedule (or both are null on
|
||
legacy rows that haven't been touched since the migration).
|
||
rollingIntervalDays:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
description: Flexible-rolling interval in days, counted forward from the latest `MedicationIntakeEvent.takenAt`.
|
||
**Mutually exclusive with `rrule`.**
|
||
scheduleType:
|
||
type: string
|
||
enum:
|
||
- SCHEDULED
|
||
- PRN
|
||
- CYCLIC
|
||
description: v1.7.0 schedule-type discriminator. SCHEDULED = rrule / rolling / legacy cadence. PRN = as-needed (never
|
||
projected, reminded, or counted in compliance expected; still loggable via the intake route). CYCLIC = N
|
||
weeks on / M weeks off, gating whichever inner cadence the rrule / legacy fields describe.
|
||
cyclicOnWeeks:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
description: v1.7.0 cyclic "on" weeks. Only meaningful when `scheduleType` is CYCLIC; null otherwise.
|
||
cyclicOffWeeks:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
description: v1.7.0 cyclic "off" weeks. Only meaningful when `scheduleType` is CYCLIC; null otherwise.
|
||
required:
|
||
- id
|
||
- medicationId
|
||
- windowStart
|
||
- windowEnd
|
||
- label
|
||
- dose
|
||
- daysOfWeek
|
||
- timesOfDay
|
||
- reminderGraceMinutes
|
||
- rrule
|
||
- rollingIntervalDays
|
||
- scheduleType
|
||
- cyclicOnWeeks
|
||
- cyclicOffWeeks
|
||
additionalProperties: false
|
||
description: Schedule entry attached to a medication. v1.5 promotes `timesOfDay` to first-class and introduces `rrule`
|
||
(calendar-anchored cadences) and `rollingIntervalDays` (flexible-rolling cadences). The two recurrence
|
||
primitives are mutually exclusive — enforced by the Zod refine on writes, the route layer, and a DB CHECK
|
||
constraint (`medication_schedules_rrule_xor_rolling`). v1.7.0 adds `scheduleType` (SCHEDULED / PRN / CYCLIC) and
|
||
the cyclic on/off-week fields.
|
||
DisableCoachFlagOutput:
|
||
type: object
|
||
properties:
|
||
disableCoach:
|
||
type: boolean
|
||
required:
|
||
- disableCoach
|
||
additionalProperties: false
|
||
description: Per-account Coach opt-out toggle (v1.4.47 W3). `true` hides the Coach FAB and short-circuits its API gates.
|
||
InsightsLayoutBodyOutput:
|
||
type: object
|
||
properties:
|
||
version:
|
||
type: number
|
||
const: 1
|
||
tiles:
|
||
minItems: 1
|
||
maxItems: 50
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
enum:
|
||
- overview
|
||
- blood-pressure
|
||
- pulse
|
||
- oxygen
|
||
- body-temperature
|
||
- respiratory-rate
|
||
- weight
|
||
- bmi
|
||
- body-water
|
||
- bone-mass
|
||
- fat-free-mass
|
||
- fat-mass
|
||
- muscle-mass
|
||
- visceral-fat
|
||
- lean-body-mass
|
||
- steps
|
||
- active-energy
|
||
- workouts
|
||
- flights-climbed
|
||
- walking-distance
|
||
- walking-steadiness
|
||
- walking-heart-rate
|
||
- walking-asymmetry
|
||
- double-support-time
|
||
- step-length
|
||
- walking-speed
|
||
- cardio-fitness
|
||
- falls
|
||
- six-minute-walk
|
||
- stair-ascent-speed
|
||
- stair-descent-speed
|
||
- sleep
|
||
- breathing-disturbances
|
||
- resting-pulse
|
||
- hrv
|
||
- pulse-wave-velocity
|
||
- vascular-age
|
||
- cardio-recovery
|
||
- environmental-audio
|
||
- headphone-audio
|
||
- audio-events
|
||
- daylight
|
||
- blood-glucose
|
||
- skin-temperature
|
||
- wrist-temperature
|
||
- mood
|
||
- medications
|
||
- blutdruck
|
||
- puls
|
||
- sauerstoff
|
||
- koerpertemperatur
|
||
- gewicht
|
||
- aktive-energie
|
||
- schlaf
|
||
- ruhepuls
|
||
- stimmung
|
||
- medikamente
|
||
visible:
|
||
type: boolean
|
||
order:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 99
|
||
required:
|
||
- id
|
||
- visible
|
||
- order
|
||
additionalProperties: false
|
||
required:
|
||
- version
|
||
- tiles
|
||
additionalProperties: false
|
||
description: "Per-user Insights tile layout: an ordered list of tiles with a visibility flag. `version` is the layout
|
||
schema version. Tile ids are a closed enum: the canonical ids are English (matching the routed
|
||
`/insights/<slug>` sub-pages). The legacy German ids (blutdruck, puls, sauerstoff, koerpertemperatur, gewicht,
|
||
aktive-energie, schlaf, ruhepuls, stimmung, medikamente) remain accepted on input for backward compatibility and
|
||
are normalised to their English equivalents before persisting; GET responses always carry the canonical English
|
||
ids. The legacy ids are deprecated and will be removed in a future major version."
|
||
Medication:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
name:
|
||
type: string
|
||
dose:
|
||
type: string
|
||
treatmentClass:
|
||
$ref: "#/components/schemas/MedicationTreatmentClass"
|
||
dosesPerUnit:
|
||
anyOf:
|
||
- type: integer
|
||
minimum: -9007199254740991
|
||
maximum: 9007199254740991
|
||
- type: "null"
|
||
description: Doses per pen / vial for inventory tracking. NULL = inventory tracking off.
|
||
active:
|
||
type: boolean
|
||
notificationsEnabled:
|
||
type: boolean
|
||
liveActivityEnabled:
|
||
type: boolean
|
||
description: v1.7.0 iOS Live Activity opt-in for this medication's reminders. Default false. The iOS client owns the
|
||
ActivityKit lifecycle; the server only stores + echoes the flag.
|
||
criticalAlarmEnabled:
|
||
type: boolean
|
||
description: v1.7.0 iOS 26 AlarmKit critical-reminder opt-in. Default false. Critical alarms bypass the device mute
|
||
switch / Focus; the server stores the preference only.
|
||
atcCode:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: v1.9.0 optional WHO ATC classification code (active-substance class, e.g. `A10BX10`).
|
||
User/clinician-asserted; never machine-guessed. Emitted on the FHIR `medicationCodeableConcept` under
|
||
`http://www.whocc.no/atc`. NULL = no code captured.
|
||
rxNormCode:
|
||
anyOf:
|
||
- type: string
|
||
- type: "null"
|
||
description: v1.9.0 optional RxNorm RxCUI (numeric, US identifier, e.g. `2601723`). Secondary FHIR coding under
|
||
`http://www.nlm.nih.gov/research/umls/rxnorm`, alongside any ATC code. NULL = no code captured.
|
||
pausedAt:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
snoozedUntil:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
nextDueAt:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
description: v1.7.0 server-computed next due instant across all the medication's schedules (earliest
|
||
`nextOccurrenceAfter`). Read-only — computed, not stored. NULL when no schedule has an upcoming slot
|
||
(paused, one-shot in the past, `endsOn` crossed, every schedule PRN). The list GET is cached 60 s, so a 60 s
|
||
staleness is accepted.
|
||
startsOn:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
description: v1.5 course start (ISO date). Anchors RRULE BYDAY / BYMONTHDAY patterns and the rolling-interval
|
||
countdown's first window. NULL means active from creation.
|
||
endsOn:
|
||
anyOf:
|
||
- type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
- type: "null"
|
||
description: v1.5 course end (ISO date). NULL means chronic. Equals `startsOn` when `oneShot` is true.
|
||
oneShot:
|
||
type: boolean
|
||
description: v1.5 single-administration flag. When true the medication has at most one schedule (no `rrule` /
|
||
`rollingIntervalDays`), and `active` auto-flips to false once the dose is logged.
|
||
createdAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
updatedAt:
|
||
type: string
|
||
format: date-time
|
||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||
schedules:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/MedicationScheduleOutput"
|
||
required:
|
||
- id
|
||
- name
|
||
- dose
|
||
- treatmentClass
|
||
- dosesPerUnit
|
||
- active
|
||
- notificationsEnabled
|
||
- liveActivityEnabled
|
||
- criticalAlarmEnabled
|
||
- atcCode
|
||
- rxNormCode
|
||
- pausedAt
|
||
- snoozedUntil
|
||
- nextDueAt
|
||
- startsOn
|
||
- endsOn
|
||
- oneShot
|
||
- createdAt
|
||
- updatedAt
|
||
- schedules
|
||
additionalProperties: false
|
||
description: Server-shaped medication row returned by GET / POST / PUT endpoints. Carries the v1.5 course-window fields
|
||
(`startsOn`, `endsOn`, `oneShot`) at the medication level and the per-schedule cadence fields on the nested
|
||
`schedules` array.
|
||
securitySchemes:
|
||
bearerAuth:
|
||
type: http
|
||
scheme: bearer
|
||
bearerFormat: hlk_<64hex>
|
||
description: Native-client API token. Use the `token` field returned by /api/auth/login on a native client.
|
||
cookieAuth:
|
||
type: apiKey
|
||
in: cookie
|
||
name: healthlog_session
|
||
description: Browser session cookie — set by /api/auth/login.
|