---
title: "Bulk, idempotent ingest of a device's daily readings"
method: POST
path: "/wellness/observations/batch"
tags: ["Recovery"]
---

# Bulk, idempotent ingest of a device's daily readings

`POST /wellness/observations/batch`

## Request

- Base URL: `https://bodylab.lol/api/v1`
- URL: `https://bodylab.lol/api/v1/wellness/observations/batch`
- Auth: HTTP bearer

For Apple Health/Health Connect syncing: each item upserts by (source, measurement, date), so a retried or re-synced request is safe. At most 500 items; an unsupported measurement or an implausible value is reported "rejected" per item rather than failing the whole batch. Writes the way a connected sync does, then re-resolves each day it touched once.

## Headers

- `X-Client-Platform` string
- `X-Client-Version` string

## Request body

- object
  - `items` object[], required
    - `source` string, required: Which device integration a batch ingest item came from. Known values: apple_health, health_connect. Treat unknown values as "other".
    - `date` string, date, required: The athlete's local day the reading belongs to.
    - `measurement` string, required: A measurement from the catalogue this ingest supports today. Known values: resting_hr, hrv_sdnn_ms, vo2max, weight_kg, sleep_minutes, sleep_start, sleep_end. Treat unknown values as "other".
    - `value` number, required: The reading, in the measurement's own unit (see HEALTH_OBSERVATION_MEASUREMENTS/measurements.ts) — bpm, ms, ml/kg/min, kg, minutes, or for sleep_start/sleep_end wall-clock minutes after 12:00 the day before `date` (0–1800).
    - `externalId` string
    - `deleted` boolean: True removes this source's reading for the day (HealthKit's anchored queries report deletions).

## Response `200`

OK

- object
  - `results` object[], required
    - `date` string, required: The item's date as it was sent, so a caller can match the result to what it sent.
    - `measurement` string, required
    - `result` string, required: What happened to one item in a batch ingest request. created/updated/unchanged/deleted describe what changed; rejected means this item alone was invalid (see its reason) — the rest of the batch still went through. Known values: created, updated, unchanged, deleted, rejected. Treat unknown values as "other".
    - `reason` string, nullable, required: Why one reading item was rejected. unsupported_source: not an allowed device source. unsupported_measurement: not one of HEALTH_OBSERVATION_MEASUREMENTS. value_out_of_range: outside the measurement's plausible range. malformed: the item didn't match the schema above, so the rest of the batch ran without it. superseded: this phone's Apple Health days for that stream now arrive as documents (healthDays), which own the day's readings. Known values: unsupported_source, unsupported_measurement, value_out_of_range, malformed, superseded. Treat unknown values as "other".

## Other responses

- `401`: Error: UNAUTHORIZED.

---

[API](https://skmtc.dev/bodylab/apis/body-lab-api.md) · [All operations](https://skmtc.dev/bodylab/apis/body-lab-api/llms.txt) · [OpenAPI document](https://skmtc.dev/bodylab/apis/body-lab-api/revisions/544c201eaf88?raw)
