WearMetrics is the single shape every adapter writes into. Downstream
layers (synheart-session, synheart-core, the runtime ingest queue)
read this shape; they do not consume vendor-specific JSON.
WearMetrics
rrIntervals, meta holds strings only, and
battery is a metric (BATTERY / .battery) instead of meta.battery.
JSON shape (canonical, Flutter toJson()):
Top-level fields
metrics keys
Maps from the MetricType enum:
HealthKit and Health Connect each report a single HRV figure: SDNN and RMSSD
respectively. They are different statistics, so each is stored only under its
own key (
hrv_sdnn from HealthKit, hrv_rmssd from Health Connect) and the
other key is absent. Flutter releases before 0.7.0 copied the one value into
both keys.
Adapters MAY emit additional vendor-specific metric keys but downstream consumers MUST tolerate unknown keys. Stable keys above are the normalised contract.
meta keys (stable)
Adapter-specific meta on Flutter: Garmin cloud adds
garmin_data (the vendor record), body_battery, vo2_max, fitness_age and source_type; when Garmin reports no stress value, stress falls back to 100 - body_battery. WHOOP cloud adds recovery_score, strain_score, sleep_score, whoop_data and source_type.
rr_ms semantics
When present:
- Each value is one inter-beat interval in milliseconds.
- Order is the order observed by the device (chronological).
- The list is per sample, not cumulative — multiple samples at the same
timestampcan share anrr_mspayload via the meta field. - Range gates apply downstream: the runtime’s RR push rejects values outside
[200, 6000].
Adapter source IDs
The top-levelsource field carries the adapter id — typically the bare vendor/source name. More specific flavor information (cloud vs BLE, etc.) lives in meta.source_type.
Consumers MUST tolerate unknown
source and meta.source_type values.
When new adapters land, they take new stable strings. Existing strings do not change meaning.
Compatibility rules
The shape is unversioned. Compatibility rules:- Producers MAY add new metric keys, new meta keys, and new
sourcevalues. - Producers MUST NOT remove or rename existing keys.
- Consumers MUST tolerate unknown metric keys and unknown
sourcevalues. - A future change to the top-level shape would be a breaking change; the SDK would expose both producers in parallel during migration.
Vendor → schema mapping (informal)
This table is informational only. Each adapter is the authoritative source for its mapping; this is a reading aid.How downstream uses this
synheart-session turns wear readings into its own BiosignalSamples:
- Swift:
HealthKitBiosignalProvider(wear:)readsstreamHR, andWearBiosignalProvider(bleHrmProvider:)bridges a BLE HRM, carrying its RR intervals as a list. - Kotlin:
WearBiosignalProviderbridges a BLE HRM;HealthConnectBiosignalProviderpolls Health Connect. - Flutter: the session SDK gets HR from the watch
(
WatchBiosignalProvider), not fromsynheart_wear.
synheart-core forwards the same shape into the runtime via pushHr / pushRr after consent gating.
Related
- Wear Overview — full SDK surface.
- Adapters — per-vendor flow.
- Errors — failure modes.