From 600145427b4410bf3e89ea192817004a89f04a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 15 Oct 2026 14:58:15 +0200 Subject: [PATCH] docs(measurements): describe the 10-minute heart-rate bucket in the batch route header --- src/app/api/measurements/batch/route.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/app/api/measurements/batch/route.ts b/src/app/api/measurements/batch/route.ts index fa0c55400..4b42d4efb 100644 --- a/src/app/api/measurements/batch/route.ts +++ b/src/app/api/measurements/batch/route.ts @@ -24,10 +24,12 @@ * · per-day cumulative totals — `stats::` (Steps, * Active Energy, Sleep Duration, Walking/Running Distance, * Flights Climbed); - * · v1.19.0 (iOS #34) hourly heart-rate buckets — - * `stats:HKQuantityTypeIdentifierHeartRate:` - * carrying the hour's AVERAGE bpm as one PULSE row, so iOS stops - * uploading one row per raw HR sample. + * · v1.19.0 (iOS #34) heart-rate buckets, 10 minutes wide since + * v1.30.7 — `stats:HKQuantityTypeIdentifierHeartRate:` + * where `` is the ISO-8601 UTC 10-minute boundary + * (minutes 00/10/20/30/40/50), carrying the bucket's AVERAGE bpm + * as one PULSE row, so iOS stops uploading one row per raw HR + * sample. * Both surface as `updated` (the row's value is overwritten) rather * than `duplicate` (the new value dropped). Sample-class externalIds * (every other prefix) keep the strict immutable `duplicate` @@ -153,11 +155,11 @@ const batchEntrySchema = z.object({ // device's own verdict / severity; resolved to a `rhythmClassification` // by `mapAppleHealthEntry`. Ignored for every non-event identifier. categoryValue: z.number().int().min(0).max(20).optional(), - // v1.19.2 (iOS #34 extension) — per-bucket spread for the hourly - // heart-rate bucket. `value` carries the hour's AVERAGE bpm; these carry - // the hour's MIN / MAX so a client renders the intra-hour band without + // v1.19.2 (iOS #34 extension) — per-bucket spread for the 10-minute + // heart-rate bucket. `value` carries the bucket's AVERAGE bpm; these carry + // its MIN / MAX so a client renders the band inside the bucket without // re-uploading raw samples. Persisted ONLY on a well-formed - // `stats:HKQuantityTypeIdentifierHeartRate:` row; ignored (stored + // `stats:HKQuantityTypeIdentifierHeartRate:` row; ignored (stored // NULL) for every other entry. Optional + backward-compatible: a pre-W192 // iOS build omits them and the bucket keeps the avg-only contract. valueMin: z.number().finite().optional(),