The data
Delivery schema
Fields, record kinds and payload examples.
The receiver contract for automatic destinations. Manual exports package the same record shape differently; see Export formats.
Conventions#
- Timestamps are ISO 8601 in UTC with fractional seconds and a
Zsuffix:2026-08-22T21:10:46.500Z. The one exception is the opt-in Health Auto Export compatibility mode, which uses local time in that app's format. - Units are untranslated HealthKit strings:
count/min, notbpm. - Identifiers are lowercase HealthKit UUID strings, stable across delivery.
- Nothing is null. Unknown fields are absent. Missing
devicemeans HealthKit reported none.
The record#
NDJSON and JSON deliver this shape unchanged. CSV, Metrics JSON and line protocol are projections of it.
| Field | Type | Notes |
|---|---|---|
| schemaVersion | Integer | Currently 1. |
| catalogVersion | Integer | Version of Hozz’s Health type catalogue. |
| id | String | Lowercase UUID. Stable across redeliveries. |
| type | String | HealthKit type identifier. |
| kind | String | See below. |
| startDate | String | ISO 8601 UTC. |
| endDate | String | ISO 8601 UTC. Equals startDate for instantaneous samples. |
| source | Object | Where the sample came from. |
| device | Object | Optional. The hardware, when HealthKit named one. |
| metadata | Object | Optional. HealthKit metadata, type-tagged. |
{
"schemaVersion": 1,
"catalogVersion": 6,
"id": "2f1a9c04-8e2b-4a1d-9f30-11c4e6a7b920",
"type": "HKQuantityTypeIdentifierHeartRate",
"kind": "quantity",
"startDate": "2026-08-22T21:10:46.500Z",
"endDate": "2026-08-22T21:10:46.500Z",
"quantity": { "unit": "count/min", "value": 62.5, "description": "62.5 count/min" },
"source": {
"name": "Brandon's Apple Watch",
"bundleIdentifier": "com.apple.health.ABC123",
"version": "11.2",
"productType": "Watch7,1",
"operatingSystem": { "major": 26, "minor": 5, "patch": 0 }
},
"device": {
"name": "Apple Watch",
"manufacturer": "Apple Inc.",
"model": "Watch",
"hardwareVersion": "Watch7,1",
"softwareVersion": "26.5"
},
"metadata": {
"HKWasUserEntered": { "type": "bool", "value": true },
"HKTimeZone": { "type": "string", "value": "Europe/London" }
}
}kind#
| kind | Extra fields | What it is |
|---|---|---|
| quantity | quantity | A measurement — steps, heart rate, weight. |
| category | value (Integer) | A classified event — a sleep stage, a stand hour. |
| workout | activityType, duration, events | One workout. |
| correlation | members | A grouping, such as a blood pressure reading. |
| workoutRoute | workout | A GPS route’s own record. |
| workoutRouteLocations | route, sequence, offset, count, locations | One page of route points. |
| workoutRouteEnd | route, locations | Marks a route as completely written. |
| deletion | — | A tombstone. Carries only kind, id, type, schemaVersion. |
| sampleEncodingError | message | A sample Hozz could not encode, written in its place so the batch never silently omits it. |
| sample | — | An HKSample subclass this build has no specific handling for. |
Metadata is type-tagged#
Each metadata value carries a type: string, number, bool, date, data (base64), quantity (which carries a description string rather than a value), array (whose value is an array of tagged values), or unsupported (with a class naming what it was).
Workout routes#
Routes use three record kinds so large tracks can be paged.
{
"kind": "workoutRouteLocations",
"route": "…",
"sequence": 0,
"offset": 0,
"count": 500,
"locations": [
{
"timestamp": "2026-08-22T07:00:00.000Z",
"latitude": 51.5007,
"longitude": -0.1246,
"altitude": 11.2,
"horizontalAccuracy": 4.1,
"verticalAccuracy": 3.0,
"course": 182.0,
"speed": 3.1
}
]
}Pages hold 500 points at fixed offsets. Negative “unknown” values are omitted. workoutRouteEnd carries the final count, exposing truncation.
workout.state is resolved or unresolved. Unresolved routes carry a reason, not a guessed workout.
NDJSON#
The default. One record per line, \n terminated, application/x-ndjson. Lossless.
{"catalogVersion":6,"endDate":"2026-08-22T21:10:46.500Z","id":"2f1a…","kind":"quantity","metadata":{},"quantity":{"description":"62.5 count/min","unit":"count/min","value":62.5},"schemaVersion":1,"source":{"bundleIdentifier":"com.apple.health.ABC","name":"Apple Watch"},"startDate":"2026-08-22T21:10:46.500Z","type":"HKQuantityTypeIdentifierHeartRate"}
{"id":"9c40…","kind":"deletion","schemaVersion":1,"type":"HKQuantityTypeIdentifierStepCount"}Sorted keys make identical records produce identical bytes.
JSON#
The same records as one array, application/json. Lossless.
[
{"catalogVersion":6,"id":"2f1a…","kind":"quantity","…":"…"},
{"id":"9c40…","kind":"deletion","schemaVersion":1,"type":"HKQuantityTypeIdentifierStepCount"}
]CSV#
One flat table, text/csv, with a header row.
id,type,kind,startDate,endDate,value,unit,sourceName,deleted
2f1a…,HKQuantityTypeIdentifierHeartRate,quantity,2026-08-22T21:10:46.500Z,2026-08-22T21:10:46.500Z,62.5,count/min,Apple Watch,false
9c40…,HKQuantityTypeIdentifierStepCount,deletion,,,,,,trueWhole numbers omit decimals. deleted is true only for deletion.
Metrics JSON#
Grouped by metric, application/json.
{
"data": {
"metrics": [
{
"name": "heart_rate",
"units": "count/min",
"data": [
{
"date": "2026-08-22T21:10:46.500Z",
"qty": 62.5,
"units": "count/min",
"source": "Apple Watch"
}
]
},
{
"name": "sleep_analysis",
"units": "count",
"data": [
{
"date": "2026-08-22T22:00:00.000Z",
"endDate": "2026-08-22T22:30:00.000Z",
"qty": 3,
"units": "count",
"source": "Apple Watch"
}
]
}
],
"workouts": [
{ "id": "7b21…", "name": "Workout", "start": "…", "end": "…" }
],
"deletions": [
{ "id": "9c40…", "name": "step_count", "type": "HKQuantityTypeIdentifierStepCount", "date": "" }
]
}
}| Field | Type | Notes |
|---|---|---|
| metrics[].name | String | Short snake_case name — see below. |
| metrics[].units | String | HealthKit’s unit string, or count when the type has none. |
| …data[].date | String | ISO 8601 UTC. The sample’s startDate. |
| …data[].qty | Number | Absent when the record has no numeric value. |
| …data[].units | String | Repeated per point. |
| …data[].source | String | Absent when HealthKit named no source. |
| …data[].endDate | String | Present only when the sample covers an interval. |
| workouts[] | Array | Absent when the batch has none. |
| deletions[] | Array | Absent when the batch has none. |
endDate preserves intervals. Hozz sends HealthKit samples, not rollups.
deletions carries tombstones. Health provides no removal time.
Metric names#
Lowercase snake_case. A curated name is used where one exists: HKQuantityTypeIdentifierStepCount becomes step_count, HKQuantityTypeIdentifierActiveEnergyBurned becomes active_energy, HKQuantityTypeIdentifierOxygenSaturation becomes blood_oxygen_saturation.
Uncurated types drop the HealthKit prefix and become snake_case.
InfluxDB line protocol#
text/plain; charset=utf-8. See the InfluxDB page for measurements, tags, precision and escaping.
Delivery mechanics#
Headers, status-code handling and how the idempotency key is derived are on Your own endpoint. Folder file naming is on Folder, and topic layout is on MQTT.