mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
Server-side prep for the next iOS TestFlight build. Five surfaces land together so the iOS engineer can pick them up in one cut-over: a locked externalId shape for daily-aggregated cumulative HealthKit rows, the SyncMode foundation columns + handshake + bulk-backfill endpoints, a first-class MoodEntry.note column that replaces the legacy tag-based workaround, a cross-source workout dedup helper, and two new MeasurementType enums plus the shared category overlay that drives the iOS permission picker and the future Insights nav.
2587 lines
78 KiB
YAML
2587 lines
78 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:
|
|
deviceType:
|
|
anyOf:
|
|
- enum:
|
|
- watch
|
|
- band
|
|
- ring
|
|
- phone
|
|
- scale
|
|
- other
|
|
- unknown
|
|
type: string
|
|
- type: "null"
|
|
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.
|
|
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
|
|
BatchWorkoutsResponse:
|
|
additionalProperties: false
|
|
properties:
|
|
data:
|
|
$ref: "#/components/schemas/WorkoutBatchResponse"
|
|
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:
|
|
defaultWindow:
|
|
default: allTime
|
|
enum:
|
|
- last7days
|
|
- last30days
|
|
- last90days
|
|
- allTime
|
|
type: string
|
|
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
|
|
- defaultWindow
|
|
type: object
|
|
error:
|
|
type: "null"
|
|
meta:
|
|
additionalProperties: false
|
|
properties:
|
|
requestId:
|
|
type: string
|
|
type: object
|
|
required:
|
|
- data
|
|
- error
|
|
type: object
|
|
GetSourcePriorityResponse:
|
|
additionalProperties: false
|
|
properties:
|
|
data:
|
|
additionalProperties: false
|
|
properties:
|
|
activeEnergy:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bloodPressure:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bodyFat:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bodyTemperature:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
deviceTypePriority:
|
|
additionalProperties:
|
|
items:
|
|
enum:
|
|
- watch
|
|
- band
|
|
- ring
|
|
- phone
|
|
- scale
|
|
- other
|
|
- unknown
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
properties:
|
|
default:
|
|
items:
|
|
enum:
|
|
- watch
|
|
- band
|
|
- ring
|
|
- phone
|
|
- scale
|
|
- other
|
|
- unknown
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
type: object
|
|
flightsClimbed:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
hrv:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
metricPriority:
|
|
additionalProperties: false
|
|
properties:
|
|
activeEnergy:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bloodPressure:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bodyFat:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bodyTemperature:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
flightsClimbed:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
hrv:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
pulse:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
restingHeartRate:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
sleep:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
spo2:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
steps:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
vo2Max:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
walkingRunningDistance:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
weight:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
type: object
|
|
pulse:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
restingHeartRate:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
sleep:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
spo2:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
steps:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
vo2Max:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
walkingRunningDistance:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
weight:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
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
|
|
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
|
|
- 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
|
|
type: string
|
|
unit:
|
|
type: string
|
|
value:
|
|
type: number
|
|
required:
|
|
- id
|
|
- type
|
|
- value
|
|
- unit
|
|
- measuredAt
|
|
- source
|
|
type: object
|
|
MeasurementsDeleteByExternalIdsRequest:
|
|
description: iOS deletion-sync. Up to 500 externalIds per call; matching rows owned by other users are silently skipped
|
|
(cross-user 404 guard).
|
|
properties:
|
|
externalIds:
|
|
items:
|
|
maxLength: 120
|
|
minLength: 1
|
|
type: string
|
|
maxItems: 500
|
|
minItems: 0
|
|
type: array
|
|
required:
|
|
- externalIds
|
|
type: object
|
|
MeasurementsDeleteByExternalIdsResponse:
|
|
additionalProperties: false
|
|
properties:
|
|
data:
|
|
$ref: "#/components/schemas/MeasurementsDeleteByExternalIdsResult"
|
|
error:
|
|
type: "null"
|
|
meta:
|
|
additionalProperties: false
|
|
properties:
|
|
requestId:
|
|
type: string
|
|
type: object
|
|
required:
|
|
- data
|
|
- error
|
|
type: object
|
|
MeasurementsDeleteByExternalIdsResult:
|
|
additionalProperties: false
|
|
properties:
|
|
deletedCount:
|
|
maximum: 9007199254740991
|
|
minimum: 0
|
|
type: integer
|
|
required:
|
|
- deletedCount
|
|
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:
|
|
defaultWindow:
|
|
default: allTime
|
|
enum:
|
|
- last7days
|
|
- last30days
|
|
- last90days
|
|
- allTime
|
|
type: string
|
|
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
|
|
- defaultWindow
|
|
type: object
|
|
error:
|
|
type: "null"
|
|
meta:
|
|
additionalProperties: false
|
|
properties:
|
|
requestId:
|
|
type: string
|
|
type: object
|
|
required:
|
|
- data
|
|
- error
|
|
type: object
|
|
PutSourcePriorityResponse:
|
|
additionalProperties: false
|
|
properties:
|
|
data:
|
|
additionalProperties: false
|
|
properties:
|
|
activeEnergy:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bloodPressure:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bodyFat:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bodyTemperature:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
deviceTypePriority:
|
|
additionalProperties:
|
|
items:
|
|
enum:
|
|
- watch
|
|
- band
|
|
- ring
|
|
- phone
|
|
- scale
|
|
- other
|
|
- unknown
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
properties:
|
|
default:
|
|
items:
|
|
enum:
|
|
- watch
|
|
- band
|
|
- ring
|
|
- phone
|
|
- scale
|
|
- other
|
|
- unknown
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
type: object
|
|
flightsClimbed:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
hrv:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
metricPriority:
|
|
additionalProperties: false
|
|
properties:
|
|
activeEnergy:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bloodPressure:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bodyFat:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bodyTemperature:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
flightsClimbed:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
hrv:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
pulse:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
restingHeartRate:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
sleep:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
spo2:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
steps:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
vo2Max:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
walkingRunningDistance:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
weight:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
type: object
|
|
pulse:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
restingHeartRate:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
sleep:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
spo2:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
steps:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
vo2Max:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
walkingRunningDistance:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
weight:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
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
|
|
WorkoutBatchEntryResult:
|
|
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
|
|
WorkoutBatchResponse:
|
|
additionalProperties: false
|
|
properties:
|
|
duplicates:
|
|
maximum: 9007199254740991
|
|
minimum: 0
|
|
type: integer
|
|
entries:
|
|
items:
|
|
$ref: "#/components/schemas/WorkoutBatchEntryResult"
|
|
type: array
|
|
inserted:
|
|
maximum: 9007199254740991
|
|
minimum: 0
|
|
type: integer
|
|
processed:
|
|
maximum: 9007199254740991
|
|
minimum: 0
|
|
type: integer
|
|
skipped:
|
|
items:
|
|
additionalProperties: false
|
|
properties:
|
|
index:
|
|
maximum: 9007199254740991
|
|
minimum: 0
|
|
type: integer
|
|
reason:
|
|
type: string
|
|
required:
|
|
- index
|
|
- reason
|
|
type: object
|
|
type: array
|
|
required:
|
|
- processed
|
|
- inserted
|
|
- duplicates
|
|
- skipped
|
|
- entries
|
|
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.
|
|
|
|
|
|
## 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
|
|
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:
|
|
defaultWindow:
|
|
default: allTime
|
|
enum:
|
|
- last7days
|
|
- last30days
|
|
- last90days
|
|
- allTime
|
|
type: string
|
|
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/me/source-priority:
|
|
get:
|
|
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":
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/GetSourcePriorityResponse"
|
|
description: Resolved priority.
|
|
"401": *a1
|
|
"422": *a2
|
|
"429": *a3
|
|
summary: Read per-user source priority
|
|
tags:
|
|
- Auth
|
|
put:
|
|
description: Persists the supplied priority. Body is validated against `SourcePriority`; missing keys read as
|
|
`DEFAULT_SOURCE_PRIORITY` at the call site.
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
properties:
|
|
activeEnergy:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bloodPressure:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bodyFat:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bodyTemperature:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
deviceTypePriority:
|
|
additionalProperties:
|
|
items:
|
|
enum:
|
|
- watch
|
|
- band
|
|
- ring
|
|
- phone
|
|
- scale
|
|
- other
|
|
- unknown
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
properties:
|
|
default:
|
|
items:
|
|
enum:
|
|
- watch
|
|
- band
|
|
- ring
|
|
- phone
|
|
- scale
|
|
- other
|
|
- unknown
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
type: object
|
|
flightsClimbed:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
hrv:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
metricPriority:
|
|
properties:
|
|
activeEnergy:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bloodPressure:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bodyFat:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
bodyTemperature:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
flightsClimbed:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
hrv:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
pulse:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
restingHeartRate:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
sleep:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
spo2:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
steps:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
vo2Max:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
walkingRunningDistance:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
weight:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
type: object
|
|
pulse:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
restingHeartRate:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
sleep:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
spo2:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
steps:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
vo2Max:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
walkingRunningDistance:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
weight:
|
|
items:
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
maxItems: 8
|
|
type: array
|
|
type: object
|
|
required: true
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/PutSourcePriorityResponse"
|
|
description: Saved priority (defaulted) echoed back.
|
|
"401": *a1
|
|
"422": *a2
|
|
"429": *a3
|
|
summary: Replace per-user source priority
|
|
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
|
|
- 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
|
|
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: 5000
|
|
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
|
|
- in: query
|
|
name: aggregate
|
|
schema:
|
|
enum:
|
|
- raw
|
|
- daily
|
|
- weekly
|
|
- monthly
|
|
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:
|
|
deviceType:
|
|
anyOf:
|
|
- maxLength: 32
|
|
minLength: 1
|
|
type: string
|
|
- type: "null"
|
|
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
|
|
- 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
|
|
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. 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:
|
|
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
|
|
/api/measurements/by-external-ids:
|
|
delete:
|
|
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:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/MeasurementsDeleteByExternalIdsRequest"
|
|
required: true
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/MeasurementsDeleteByExternalIdsResponse"
|
|
description: Delete batch processed.
|
|
"401": *a1
|
|
"422": *a2
|
|
"429": *a3
|
|
summary: Delete measurements by external ID (iOS deletion-sync)
|
|
tags:
|
|
- Measurements
|
|
/api/workouts/batch:
|
|
post:
|
|
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:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
properties:
|
|
workouts:
|
|
items:
|
|
properties:
|
|
avgHeartRate:
|
|
maximum: 300
|
|
minimum: 20
|
|
type: integer
|
|
elevationM:
|
|
maximum: 10000
|
|
minimum: -500
|
|
type: number
|
|
endedAt:
|
|
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:
|
|
maxLength: 128
|
|
type: string
|
|
externalSourceVersion:
|
|
maxLength: 64
|
|
type: string
|
|
maxHeartRate:
|
|
maximum: 300
|
|
minimum: 20
|
|
type: integer
|
|
metadata:
|
|
additionalProperties: {}
|
|
propertyNames:
|
|
type: string
|
|
type: object
|
|
minHeartRate:
|
|
maximum: 300
|
|
minimum: 20
|
|
type: integer
|
|
pauseDurationSec:
|
|
maximum: 86400
|
|
minimum: 0
|
|
type: integer
|
|
route:
|
|
properties:
|
|
geometry:
|
|
properties:
|
|
coordinates:
|
|
items:
|
|
items:
|
|
type: number
|
|
prefixItems:
|
|
- maximum: 180
|
|
minimum: -180
|
|
type: number
|
|
- maximum: 90
|
|
minimum: -90
|
|
type: number
|
|
type: array
|
|
maxItems: 20000
|
|
minItems: 2
|
|
type: array
|
|
type:
|
|
const: LineString
|
|
type: string
|
|
required:
|
|
- type
|
|
- coordinates
|
|
type: object
|
|
sampleTimestamps:
|
|
items:
|
|
properties:
|
|
hr:
|
|
maximum: 300
|
|
minimum: 20
|
|
type: integer
|
|
speedMs:
|
|
maximum: 50
|
|
minimum: 0
|
|
type: number
|
|
t:
|
|
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
|
|
required:
|
|
- t
|
|
type: object
|
|
type: array
|
|
required:
|
|
- geometry
|
|
type: object
|
|
source:
|
|
default: MANUAL
|
|
enum:
|
|
- MANUAL
|
|
- WITHINGS
|
|
- IMPORT
|
|
- APPLE_HEALTH
|
|
type: string
|
|
sportType:
|
|
enum:
|
|
- walking
|
|
- running
|
|
- cycling
|
|
- hiking
|
|
- swimming
|
|
- rowing
|
|
- elliptical
|
|
- stairClimber
|
|
- yoga
|
|
- mindAndBody
|
|
- strength
|
|
- hiit
|
|
- dance
|
|
- golf
|
|
- tennis
|
|
- basketball
|
|
- soccer
|
|
- crossTraining
|
|
- mixedCardio
|
|
- other
|
|
type: string
|
|
startedAt:
|
|
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
|
|
stepCount:
|
|
maximum: 200000
|
|
minimum: 0
|
|
type: integer
|
|
totalDistanceM:
|
|
maximum: 1000000
|
|
minimum: 0
|
|
type: number
|
|
totalEnergyKcal:
|
|
maximum: 50000
|
|
minimum: 0
|
|
type: number
|
|
required:
|
|
- sportType
|
|
- startedAt
|
|
- endedAt
|
|
type: object
|
|
maxItems: 100
|
|
minItems: 1
|
|
type: array
|
|
required:
|
|
- workouts
|
|
type: object
|
|
required: true
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/BatchWorkoutsResponse"
|
|
description: Batch processed.
|
|
"400":
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ErrorEnvelope"
|
|
description: Batch validation failed (over-cap, schema reject, or oversized route).
|
|
"401": *a1
|
|
"413":
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ErrorEnvelope"
|
|
description: Request body exceeds the 5 MB ceiling.
|
|
"422": *a2
|
|
"429": *a3
|
|
summary: Typed workout batch ingest (v1.4.25 W16b)
|
|
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
|