Files
HealthLog/docs/api/openapi.yaml
T
Marc-André BombeckandClaude Opus 4.7 f1e6630958 chore(format): prettier sweep across develop
Mostly table column normalisation under docs/audit/ + planning, object-
literal expansion in src/lib/ai/coach/snapshot.ts after the W4 F6 Apple
Health metric block landed, and small wrap/unwrap line adjustments in
the device + APNs routes / tests touched across W3/W4/W5 sessions.
No behavioural change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:27:51 +02:00

1158 lines
34 KiB
YAML

components:
schemas:
AccessRefreshBundle:
additionalProperties: false
description: Native-client token bundle returned by login + refresh. Web cookie-only callers see only `user`.
properties:
refreshToken:
description: Refresh token (`hlr_<64hex>`); only present for native-policy callers.
type: string
refreshTokenExpiresAt:
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: string
token:
description: Access token (`hlk_<64hex>`).
type: string
tokenExpiresAt:
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: string
user:
additionalProperties: false
properties:
id:
type: string
username:
type: string
required:
- id
- username
type: object
required:
- token
- tokenExpiresAt
type: object
AppleHealthBatchEntry:
properties:
endDate:
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: string
externalId:
description: HKSample.uuid string — the dedup key.
maxLength: 120
minLength: 1
type: string
externalSourceVersion:
maxLength: 120
minLength: 1
type: string
hkIdentifier:
description: HealthKit identifier (e.g. `HKQuantityTypeIdentifierBodyMass`).
maxLength: 120
minLength: 1
type: string
sleepStage:
description: HKCategoryValueSleepAnalysis codepoint; only for sleep samples.
maximum: 20
minimum: 0
type: integer
startDate:
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: string
unit:
maxLength: 60
minLength: 1
type: string
value:
type: number
required:
- hkIdentifier
- value
- unit
- startDate
- endDate
- externalId
type: object
AppleHealthBatchEntryResult:
additionalProperties: false
properties:
index:
maximum: 9007199254740991
minimum: 0
type: integer
reason:
type: string
status:
enum:
- inserted
- duplicate
- skipped
type: string
required:
- index
- status
type: object
AppleHealthBatchRequest:
description: Apple Health batch ingest. ≤500 entries per call; idempotent via `Idempotency-Key`.
properties:
entries:
items:
$ref: "#/components/schemas/AppleHealthBatchEntry"
maxItems: 500
minItems: 1
type: array
required:
- entries
type: object
AppleHealthBatchResponse:
additionalProperties: false
properties:
duplicate:
maximum: 9007199254740991
minimum: 0
type: integer
entries:
items:
$ref: "#/components/schemas/AppleHealthBatchEntryResult"
type: array
inserted:
maximum: 9007199254740991
minimum: 0
type: integer
skipped:
maximum: 9007199254740991
minimum: 0
type: integer
required:
- inserted
- duplicate
- skipped
- entries
type: object
BatchMeasurementsResponse:
additionalProperties: false
properties:
data:
$ref: "#/components/schemas/AppleHealthBatchResponse"
error:
type: "null"
meta:
additionalProperties: false
properties:
requestId:
type: string
type: object
required:
- data
- error
type: object
CoachMessageFeedbackRequest:
description: Per-message helpful/unhelpful feedback (v1.4.23 H7). Optional `reason` is free-form prose, capped at 200 chars.
properties:
rating:
enum:
- helpful
- unhelpful
type: string
reason:
maxLength: 200
minLength: 1
type: string
required:
- rating
type: object
CoachMessageFeedbackResponse:
additionalProperties: false
properties:
data:
additionalProperties: false
properties:
createdAt:
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: string
id:
type: string
required:
- id
- createdAt
type: object
error:
type: "null"
meta:
additionalProperties: false
properties:
requestId:
type: string
type: object
required:
- data
- error
type: object
CreateMeasurementResponse:
additionalProperties: false
properties:
data:
$ref: "#/components/schemas/MeasurementResource"
error:
type: "null"
meta:
additionalProperties: false
properties:
requestId:
type: string
type: object
required:
- data
- error
type: object
DeviceRegisterRequest:
description:
Native device registration. Re-registering an APNs token belonging to another user returns 409
(cross-user-hijack guard).
properties:
apnsEnvironment:
description: Gateway the iOS client received `apnsToken` from. Server never auto-detects.
enum:
- sandbox
- production
type: string
apnsToken:
description: Hex-encoded APNs device token. Must be paired with `apnsEnvironment`.
maxLength: 256
minLength: 8
pattern: ^[A-Fa-f0-9]+$
type: string
appVersion:
maxLength: 32
minLength: 1
type: string
bundleId:
maxLength: 128
minLength: 1
type: string
locale:
maxLength: 16
minLength: 2
type: string
model:
maxLength: 64
minLength: 1
type: string
token:
description: Generic device identifier (legacy; APNs token below).
maxLength: 512
minLength: 8
pattern: ^[A-Za-z0-9+/=._:-]+$
type: string
required:
- token
- bundleId
type: object
DeviceRegisterResponse:
additionalProperties: false
properties:
data:
additionalProperties: false
properties:
id:
type: string
required:
- id
type: object
error:
type: "null"
meta:
additionalProperties: false
properties:
requestId:
type: string
type: object
required:
- data
- error
type: object
ErrorEnvelope:
additionalProperties: false
description: "Standard error response: data is null, error is human prose."
properties:
data:
type: "null"
error:
type: string
meta:
additionalProperties: false
properties:
errorCode:
type: string
requestId:
type: string
type: object
required:
- data
- error
type: object
GetCoachPrefsResponse:
additionalProperties: false
properties:
data:
additionalProperties: false
properties:
excludeMetrics:
default: []
items:
enum:
- bp
- weight
- pulse
- mood
- compliance
- hrv
- sleep
- resting_hr
- steps
type: string
maxItems: 9
type: array
showEvidenceByDefault:
default: false
type: boolean
tone:
default: warm
enum:
- warm
- neutral
- concise
type: string
verbosity:
default: default
enum:
- brief
- default
- detailed
type: string
required:
- tone
- verbosity
- excludeMetrics
- showEvidenceByDefault
type: object
error:
type: "null"
meta:
additionalProperties: false
properties:
requestId:
type: string
type: object
required:
- data
- error
type: object
InsightsComprehensiveResponse:
additionalProperties: false
description:
AI-generated insights bundle. Strict-schema validated server-side; Coach-routed when the insight surface
needs day-level grounding.
properties:
citations:
items:
additionalProperties: {}
propertyNames:
type: string
type: object
type: array
dailyBriefing:
anyOf:
- additionalProperties: {}
propertyNames:
type: string
type: object
- type: "null"
metricSource:
additionalProperties: {}
propertyNames:
type: string
type: object
recommendations:
items:
additionalProperties: {}
propertyNames:
type: string
type: object
type: array
storyboardAnnotations:
items:
additionalProperties: {}
propertyNames:
type: string
type: object
type: array
summary:
type: string
trendAnnotations:
anyOf:
- additionalProperties: {}
propertyNames:
type: string
type: object
- type: "null"
warnings:
items:
additionalProperties: {}
propertyNames:
type: string
type: object
type: array
weeklyReport:
anyOf:
- additionalProperties: {}
propertyNames:
type: string
type: object
- type: "null"
required:
- summary
- recommendations
- citations
- warnings
type: object
InsightsComprehensiveResponseEnvelope:
additionalProperties: false
properties:
data:
$ref: "#/components/schemas/InsightsComprehensiveResponse"
error:
type: "null"
meta:
additionalProperties: false
properties:
requestId:
type: string
type: object
required:
- data
- error
type: object
ListMeasurementsResponse:
additionalProperties: false
properties:
data:
additionalProperties: false
properties:
measurements:
items:
$ref: "#/components/schemas/MeasurementResource"
type: array
total:
maximum: 9007199254740991
minimum: 0
type: integer
required:
- measurements
- total
type: object
error:
type: "null"
meta:
additionalProperties: false
properties:
requestId:
type: string
type: object
required:
- data
- error
type: object
LoginResponse:
additionalProperties: false
properties:
data:
$ref: "#/components/schemas/AccessRefreshBundle"
error:
type: "null"
meta:
additionalProperties: false
properties:
requestId:
type: string
type: object
required:
- data
- error
type: object
MeasurementResource:
additionalProperties: false
description: Server-shaped measurement row returned by GET endpoints.
properties:
id:
type: string
measuredAt:
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: string
notes:
anyOf:
- type: string
- type: "null"
source:
enum:
- MANUAL
- WITHINGS
- IMPORT
- APPLE_HEALTH
type: string
type:
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
type: string
unit:
type: string
value:
type: number
required:
- id
- type
- value
- unit
- measuredAt
- source
type: object
PasskeyLoginVerifyRequest:
description: Passkey assertion verification. Same native-client token issuance as the password path.
properties:
challengeId:
description: Server-issued WebAuthn challenge id.
minLength: 1
type: string
credential:
description: SimpleWebAuthn-style assertion response payload.
properties:
authenticatorAttachment:
enum:
- platform
- cross-platform
type: string
clientExtensionResults:
additionalProperties: {}
propertyNames:
type: string
type: object
id:
type: string
rawId:
type: string
response:
additionalProperties: {}
propertyNames:
type: string
type: object
type:
const: public-key
type: string
required:
- id
- rawId
- type
- response
type: object
required:
- challengeId
- credential
type: object
PasskeyLoginVerifyResponse:
additionalProperties: false
properties:
data:
$ref: "#/components/schemas/AccessRefreshBundle"
error:
type: "null"
meta:
additionalProperties: false
properties:
requestId:
type: string
type: object
required:
- data
- error
type: object
PutCoachPrefsResponse:
additionalProperties: false
properties:
data:
additionalProperties: false
properties:
excludeMetrics:
default: []
items:
enum:
- bp
- weight
- pulse
- mood
- compliance
- hrv
- sleep
- resting_hr
- steps
type: string
maxItems: 9
type: array
showEvidenceByDefault:
default: false
type: boolean
tone:
default: warm
enum:
- warm
- neutral
- concise
type: string
verbosity:
default: default
enum:
- brief
- default
- detailed
type: string
required:
- tone
- verbosity
- excludeMetrics
- showEvidenceByDefault
type: object
error:
type: "null"
meta:
additionalProperties: false
properties:
requestId:
type: string
type: object
required:
- data
- error
type: object
RefreshResponse:
additionalProperties: false
properties:
data:
$ref: "#/components/schemas/AccessRefreshBundle"
error:
type: "null"
meta:
additionalProperties: false
properties:
requestId:
type: string
type: object
required:
- data
- error
type: object
RefreshTokenRequest:
description: Exchange a one-time-use refresh token for a fresh access + refresh pair.
properties:
refreshToken:
description: Caller-presented refresh token (`hlr_<64hex>`).
minLength: 1
type: string
revoke:
description: When true, revoke the supplied token instead of rotating.
type: boolean
required:
- refreshToken
type: object
securitySchemes:
bearerAuth:
bearerFormat: hlk_<64hex>
description: Native-client API token. Use the `token` field returned by /api/auth/login on a native client.
scheme: bearer
type: http
cookieAuth:
description: Browser session cookie — set by /api/auth/login.
in: cookie
name: healthlog_session
type: apiKey
info:
contact:
name: HealthLog
url: https://healthlog.dev
description: Self-hosted personal-health-tracking PWA — public API surface for the iOS native client and external ingest.
license:
name: AGPL-3.0
url: https://github.com/MBombeck/HealthLog/blob/main/LICENSE
title: HealthLog API
version: 1.4.23
openapi: 3.1.0
paths:
/api/auth/login:
post:
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:
content:
application/json:
schema:
properties:
email:
minLength: 1
type: string
password:
minLength: 1
type: string
required:
- email
- password
type: object
required: true
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/LoginResponse"
description: Login succeeded.
"401": &a1
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorEnvelope"
description: Authentication required or invalid credentials.
"422": &a2
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorEnvelope"
description: Request validation failed.
"429": &a3
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorEnvelope"
description: Rate limit exceeded.
summary: Email-or-username login (password)
tags:
- Auth
/api/auth/me/coach-prefs:
get:
description:
Returns the persisted preferences with defaults filled in. Null persisted state resolves to the legacy
v1.4.22 defaults.
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/GetCoachPrefsResponse"
description: Resolved preferences.
"401": *a1
"422": *a2
"429": *a3
summary: Read per-user Coach prompt-tuning preferences
tags:
- Auth
put:
description:
Persists the supplied prefs. Body is validated against `CoachPrefs`; missing keys fall back to the
documented defaults.
requestBody:
content:
application/json:
schema:
properties:
excludeMetrics:
default: []
items:
enum:
- bp
- weight
- pulse
- mood
- compliance
- hrv
- sleep
- resting_hr
- steps
type: string
maxItems: 9
type: array
showEvidenceByDefault:
default: false
type: boolean
tone:
default: warm
enum:
- warm
- neutral
- concise
type: string
verbosity:
default: default
enum:
- brief
- default
- detailed
type: string
type: object
required: true
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/PutCoachPrefsResponse"
description: Saved preferences echoed back.
"401": *a1
"422": *a2
"429": *a3
summary: Replace per-user Coach prompt-tuning preferences
tags:
- Auth
/api/auth/passkey/login-verify:
post:
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/PasskeyLoginVerifyRequest"
required: true
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/PasskeyLoginVerifyResponse"
description: Assertion verified — session + optional bearer issued.
"401": *a1
"422": *a2
"429": *a3
summary: Passkey assertion verification
tags:
- Auth
/api/auth/refresh:
post:
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.
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/RefreshTokenRequest"
required: true
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/RefreshResponse"
description: Rotation succeeded — new pair issued.
"401": *a1
"422": *a2
"429": *a3
summary: Rotate refresh token (one-time use)
tags:
- Auth
/api/devices:
post:
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:
content:
application/json:
schema:
$ref: "#/components/schemas/DeviceRegisterRequest"
required: true
responses:
"201":
content:
application/json:
schema:
$ref: "#/components/schemas/DeviceRegisterResponse"
description: Device registered or refreshed.
"401": *a1
"409":
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorEnvelope"
description: Device or APNs token already registered to another user.
"422": *a2
"429": *a3
summary: Register native device + APNs token
tags:
- Devices
/api/insights/chat/messages/{id}/feedback:
post:
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:
content:
application/json:
schema:
$ref: "#/components/schemas/CoachMessageFeedbackRequest"
required: true
responses:
"201":
content:
application/json:
schema:
$ref: "#/components/schemas/CoachMessageFeedbackResponse"
description: Feedback saved.
"401": *a1
"404":
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorEnvelope"
description: Message not found or not owned by the caller.
"409":
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorEnvelope"
description: Caller has already rated this message text.
"422": *a2
"429": *a3
summary: Rate a Coach assistant message
tags:
- Insights
/api/insights/comprehensive:
get:
description:
Full Insights surface — daily briefing, recommendations with rationale, optional weekly report + storyboard
annotations. Strict-schema validated server-side.
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/InsightsComprehensiveResponseEnvelope"
description: Insights bundle.
"401": *a1
"422": *a2
"429": *a3
summary: Comprehensive AI insights bundle
tags:
- Insights
/api/measurements:
get:
description: Filter by type + date range. Response is paged via offset/limit; default limit 100, hard cap 500.
parameters:
- in: query
name: type
schema:
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
type: string
- in: query
name: from
schema:
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: string
- in: query
name: to
schema:
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: string
- in: query
name: limit
schema:
default: 100
maximum: 500
minimum: 1
type: integer
- in: query
name: offset
schema:
default: 0
maximum: 9007199254740991
minimum: 0
type: integer
- in: query
name: sortBy
schema:
default: measuredAt
enum:
- type
- value
- measuredAt
- source
type: string
- in: query
name: sortDir
schema:
default: desc
enum:
- asc
- desc
type: string
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/ListMeasurementsResponse"
description: Measurement page.
"401": *a1
"422": *a2
"429": *a3
summary: List measurements
tags:
- Measurements
post:
description: Single ingest. Use `/api/measurements/batch` for Apple Health upload streams.
requestBody:
content:
application/json:
schema:
properties:
glucoseContext:
enum:
- FASTING
- POSTPRANDIAL
- RANDOM
- BEDTIME
type: string
measuredAt:
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: string
notes:
maxLength: 25
type: string
source:
default: MANUAL
enum:
- MANUAL
- WITHINGS
- IMPORT
- APPLE_HEALTH
type: string
type:
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
type: string
value:
type: number
required:
- type
- value
- measuredAt
type: object
required: true
responses:
"201":
content:
application/json:
schema:
$ref: "#/components/schemas/CreateMeasurementResponse"
description: Created.
"401": *a1
"422": *a2
"429": *a3
summary: Create one measurement
tags:
- Measurements
/api/measurements/batch:
post:
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.
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/AppleHealthBatchRequest"
required: true
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/BatchMeasurementsResponse"
description: Batch processed.
"401": *a1
"422": *a2
"429": *a3
summary: Apple Health batch ingest
tags:
- Measurements
servers:
- description: Production
url: https://healthlog.bombeck.io
- description: Local dev
url: http://localhost:3000
tags:
- name: Auth
- name: Measurements
- name: Medications
- name: Mood
- name: Insights
- name: Notifications
- name: Devices
- name: Admin