Documentation Index
Fetch the complete documentation index at: https://docs.synheart.ai/llms.txt
Use this file to discover all available pages before exploring further.
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
class WearMetrics {
final DateTime timestamp;
final String deviceId;
final String source;
final Map<String, num?> metrics;
final Map<String, Object?> meta;
final List<double>? rrIntervalsMs;
}
JSON shape (canonical):
{
"timestamp": "2025-10-20T18:30:00Z",
"device_id": "applewatch_1234",
"source": "apple_healthkit",
"metrics": {
"hr": 72,
"hrv_rmssd": 45,
"hrv_sdnn": 62,
"steps": 1045,
"calories": 120.4,
"distance": 2.5,
"stress": 23
},
"meta": {
"battery": 0.82,
"synced": true
},
"rr_ms": [800, 850, 820]
}
Top-level fields
| Field | Type | Notes |
|---|
timestamp | ISO-8601 UTC string | Time the measurement applies to (sample time, not emit time). Always UTC. |
device_id | string | Stable identifier for this physical device. Format is vendor-shaped (e.g. "applewatch_1234", "whoop_<uuid>", BLE MAC for HRM). |
source | string | Lowercase snake_case adapter id. Stable across versions of the same adapter. |
metrics | object | Keyed by metric short-name (see below). Values may be null when the adapter knows the metric is not available; missing keys mean “not produced by this sample”. |
meta | object | Free-form per-sample metadata. Stable keys are documented below; adapters may add vendor-specific keys. |
rr_ms | array of doubles | absent | Inter-beat intervals in milliseconds when the source provides them. Absent when not available. |
metrics keys
Maps from the MetricType enum:
| Key | Type | Unit | Source |
|---|
hr | number | bpm | HR sample. |
hrv_rmssd | number | ms | RMSSD over the adapter’s window. |
hrv_sdnn | number | ms | SDNN over the adapter’s window. |
steps | number | count | Period total (since the previous sample, or per-day depending on adapter). |
calories | number | kcal | Active energy. |
distance | number | km | Total distance over the period. |
stress | number | unitless | Vendor-specific score (e.g. Garmin 0–100); adapters do not normalise this across vendors. |
Adapters MAY emit additional vendor-specific metric keys but downstream consumers MUST tolerate unknown keys. Stable keys above are the normalised contract.
| Key | Type | Notes |
|---|
battery | number 0–1 | Device battery level. |
synced | bool | Whether this sample arrived via real-time path (true) or backlog/sync (false). |
Adapter-specific meta: Garmin adds connection_state; WHOOP adds recovery_score, strain, etc. — refer to the per-vendor models.
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
timestamp can share an rr_ms payload via the meta field.
- Range gates apply downstream: the runtime’s RR push rejects values outside
[200, 6000].
Adapter source IDs
The top-level source field carries the adapter id — typically the bare vendor/source name. More specific flavor information (cloud vs BLE, etc.) lives in meta.source_type.
source value | Adapter | meta.source_type flavor |
|---|
"apple_healthkit" | Apple HealthKit | — |
"health_connect" | Android Health Connect | — |
"ble_hrm" | BLE Heart Rate Profile | — |
"whoop" | WHOOP | "whoop_cloud" for OAuth + webhooks; WHOOP via BLE Broadcast HR uses source = "ble_hrm" |
"garmin" | Garmin via Connect API | "garmin_cloud" |
"garmin_sdk" | Garmin Health SDK (RTS, license-gated) | — |
"fitbit" | Fitbit | (planned) |
"oura" | Oura | proxied through HealthKit / Health Connect today |
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
source values.
- Producers MUST NOT remove or rename existing keys.
- Consumers MUST tolerate unknown metric keys and unknown
source values.
- A future change to the top-level shape would be a breaking change;
the SDK would expose both producers in parallel during migration.
This table is informational only. Each adapter is the authoritative source for its mapping; this is a reading aid.
| Vendor field | Maps to | Notes |
|---|
HealthKit HKQuantityTypeIdentifierHeartRate | metrics.hr | Average per query window. |
| HealthKit RR samples | rr_ms | Workout-mode only on most Apple Watch models. |
Health Connect HeartRateRecord | metrics.hr | One sample per record. |
WHOOP webhook recovery_score | meta.recovery_score | Not part of the canonical metric set. |
Garmin Connect restingHeartRate | metrics.hr | Daily resting HR baseline. |
| BLE GATT 0x180D HR characteristic | metrics.hr | Plus optional RR field → rr_ms. |
Apple XML HKQuantityTypeIdentifierHeartRateVariabilitySDNN | metrics.hrv_sdnn | Backfill only. |
How downstream uses this
synheart-session’s WearBiosignalProvider wraps streamHR / streamHRV and converts each WearMetrics into a BiosignalSample:
BiosignalSample(
timestampMs: metrics.timestamp.millisecondsSinceEpoch,
bpm: metrics.getMetric(MetricType.hr) ?? 0.0,
rrIntervalMs: metrics.rrIntervalsMs?.first,
accelerometer: ...,
)
synheart-core forwards the same shape into the runtime via pushHr / pushRr after consent gating.