> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mirobody.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Data

> POST /v1/data, GET /v1/data, DELETE /v1/data — write, read, and erase structured health records.

Write structured records straight into a Subject's store, and read them back. Every write runs through the platform's [standardization pipeline](/en/api-reference/standardization): values and units are parsed, recognized indicators receive a LOINC code and canonical name, and records are mirrored into FHIR. Uncoded rows remain readable. The agent reads this same data with its built-in tools.

Health data arrives in four shapes — each has exactly one door in:

| Data shape                    | Typical source                                                                       | How it goes in         | Endpoint                                                                                                                                                                                                                                                     |
| ----------------------------- | ------------------------------------------------------------------------------------ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Structured readings**       | Devices / wearables (**you integrate them**, write daily aggregates), manual entries | Structured records     | [`POST /v1/data`](/en/api-reference/data) — pre-aggregate high-frequency samples; episodes (sleep, workouts) carry `time` + `end_time`. See the [device-data cookbook](/en/api-reference/device-data).                                                       |
| **Files / photos**            | Lab reports, checkup PDFs, phone photos                                              | File upload            | [`POST /v1/files`](/en/api-reference/files) — stores the original, extracts its text, and pulls out standardized readings automatically. [`POST /v1/standardize`](/en/api-reference/extract) runs the same extraction synchronously (e.g. a dry-run preview) |
| **Narrative with readings**   | "headache all day, temperature was 38.2 °C", dictated notes                          | Narrative text         | [`POST /v1/standardize`](/en/api-reference/extract) — the quantifiable readings are extracted; the narrative around them is dropped                                                                                                                          |
| **Purely subjective journal** | "dizzy and a headache all afternoon", mood notes                                     | Single-turn agent call | [`POST /v1/responses`](/en/api-reference/responses) with `store: true` — indicators and durable memories are extracted from the entry. See the [Journaling recipe](/en/api-reference/state-and-memory#journaling).                                           |

Mirobody stores source data, standardizes structured readings, and makes both available to AI. You decide how data is collected and prepared before it reaches the API.

<Note>
  **The `user` field is the tenant-isolation key.** The backend maps `(your account, user)` to an internal **Subject**; each `user` you pass is fully isolated from the others. Pass each end-user's stable id and their data never crosses over. Omit it and the call falls back to your account's default Subject. Subjects are invisible to the Mirobody web app and to other developers. The `user` value is normalized (trimmed + lowercased) before mapping, so `Alice` and `alice` resolve to the SAME Subject — pass a stable, canonical id.
</Note>

## Write records

```http theme={null}
POST /v1/data
Authorization: Bearer mb_live_*
Content-Type:  application/json
```

| Field        | Type   | Description                                                                                                                                                                                                                                                                                                                                                        |
| ------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `records`    | array  | Each record `{indicator, value, unit?, time?, end_time?, source?}`. **Max 500 records per request.** `end_time` (optional, ISO time) marks the end of an episode — sleep, workouts — whose start is `time`. `measured_at` / `start_time` are accepted as aliases for `time` — rows from POST /v1/standardize (which returns `measured_at`) can be forwarded as-is. |
| `user`       | string | Subject the records belong to (tenant-isolation key).                                                                                                                                                                                                                                                                                                              |
| `retention`  | string | **Required — there is no default.** One of `permanent` / `1h` / `2h` / `6h` / `1d` / `session` (`persistent` is accepted as an alias of `permanent`). Hour grains auto-delete after that long (hard-capped ≤ 24h). Omitting it — or sending any value outside the enum — returns `400` (`code: invalid_retention`).                                                |
| `session_id` | string | **Required when `retention=session`** (`400`, `code: invalid_session`, otherwise). Use a globally unique, opaque id (for example a UUID; never reuse it across Subjects). Tags the records so [`DELETE /v1/sessions/{id}`](/en/api-reference/lifecycle#sessions) can purge them.                                                                                   |

```bash theme={null}
curl https://api.mirobody.ai/v1/data \
  -H "Authorization: Bearer $MIROBODY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "user": "alice",
        "retention": "permanent",
        "records": [
          {"indicator": "fasting_glucose", "value": 97, "unit": "mg/dL", "time": "2026-06-16T07:30:00Z"},
          {"indicator": "fasting_glucose", "value": 92, "unit": "mg/dL", "time": "2026-06-17T07:25:00Z"}
        ]
      }'
```

Response — `standardized` counts the records that resolved to a LOINC code on the way in:

```json theme={null}
{ "status": "ok", "ingested": 2, "standardized": 2, "subject": "alice" }
```

<Note>
  **Every write is standardized, not just stored.** Each value and unit is parsed to UCUM, and the indicator name is matched to a LOINC code — never a guessed one, so a low-confidence match simply stays uncoded. See [Standardization](/en/api-reference/standardization). To preview all of that without writing anything, use [`POST /v1/standardize`](/en/api-reference/extract) with `store=false`.
</Note>

<Warning>
  Writes are not idempotent. A retry can create duplicate readings, so check the outcome before retrying a timed-out request and keep your own ingestion ledger.
</Warning>

### Episodes (sleep, workouts)

Episode-type records span a period rather than a point in time — put the start in `time` and the end in the optional `end_time`:

```bash theme={null}
curl https://api.mirobody.ai/v1/data \
  -H "Authorization: Bearer $MIROBODY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "user": "alice",
        "retention": "permanent",
        "records": [
          {"indicator": "sleep_duration", "value": 7.5, "unit": "h",
           "time": "2026-07-09T23:10:00Z", "end_time": "2026-07-10T06:40:00Z",
           "source": "garmin"}
        ]
      }'
```

Writing device data at scale (daily aggregation, batching, vendors)? See the [device-data cookbook](/en/api-reference/device-data).

## Read records

```http theme={null}
GET /v1/data?user=alice&indicator=fasting_glucose&limit=100
Authorization: Bearer mb_live_*
```

| Query       | Description                                                                                                                    |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `user`      | Subject to read from.                                                                                                          |
| `indicator` | Optional filter by indicator name (substring match).                                                                           |
| `limit`     | `1`–`1000` (default `100`).                                                                                                    |
| `offset`    | Skip N records for paging (ordering is stable: newest first). `has_more: true` → request the next page with `offset += limit`. |

The response is an OpenAI-style list, newest first. `value` is the value exactly as written (`"97"`); the unit lives separately in `parsed_unit`, alongside the other machine-readable fields:

```json theme={null}
{
  "object": "list",
  "data": [
    {
      "id": 1287,
      "indicator": "fasting_glucose",
      "value": "97",
      "parsed_value": "97",
      "parsed_unit": "mg/dL",
      "loinc_code": "1558-6",
      "canonical_name": "Fasting glucose [Mass/volume] in Serum or Plasma",
      "fhir_resource_id": "8f3c9a1e-...",
      "time": "2026-06-16T07:30:00+00",
      "end_time": null,
      "source": "api",
      "comment": ""
    }
  ],
  "has_more": false,
  "subject": "alice"
}
```

| Field                                                              | Description                                                                                                                                                                                                                                                                                   |
| ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `data`                                                             | The records (up to `limit`).                                                                                                                                                                                                                                                                  |
| `id`                                                               | Row id (integer) — pass it to `DELETE /v1/data?id=` for a row-level delete.                                                                                                                                                                                                                   |
| `value`                                                            | The value as written (string, e.g. `"97"`); the unit lives in `parsed_unit`.                                                                                                                                                                                                                  |
| `parsed_value` / `parsed_unit`                                     | Numeric value + **UCUM** unit from the standardization pipeline (`null` when unparseable).                                                                                                                                                                                                    |
| `loinc_code` / `canonical_name`                                    | Deterministic LOINC resolution + its display name (`null` when the name didn't resolve).                                                                                                                                                                                                      |
| `fhir_resource_id`                                                 | Id of the record's FHIR Observation mirror.                                                                                                                                                                                                                                                   |
| `end_time`                                                         | End of the period for episode records (sleep, workouts); `null` for point-in-time readings.                                                                                                                                                                                                   |
| `reference_low` / `reference_high` / `reference_text` / `abnormal` | Reference range and abnormality flag, when the source (e.g. a lab report) carried them; `null` otherwise.                                                                                                                                                                                     |
| `source` / `comment`                                               | How the record was created: `api` (written via `POST /v1/data`), `extract` (from `POST /v1/standardize`), `upload` (read from a `/v1/files` upload), or `consolidation` (drawn from a stored conversation). Any origin tag you sent in a record's own `source` field comes back in `comment`. |
| `has_more`                                                         | `true` when this page hit `limit` — fetch the next page with `offset += limit`.                                                                                                                                                                                                               |
| `subject`                                                          | The Subject the records belong to (resolved from `user`).                                                                                                                                                                                                                                     |

## Erase records

```http theme={null}
DELETE /v1/data?user=alice&indicator=fasting_glucose
Authorization: Bearer mb_live_*
```

**Permanently erases** the Subject's records — right-to-be-forgotten semantics (the data is removed, not merely hidden from reads). Three scopes, from narrowest to widest:

| Query       | Scope                                                                                            |
| ----------- | ------------------------------------------------------------------------------------------------ |
| `id`        | Exactly **one row** — the integer ids `GET /v1/data` returns. Takes precedence over `indicator`. |
| `indicator` | One indicator (substring match, mirroring the GET filter).                                       |
| *(neither)* | **All** of the Subject's records.                                                                |

```http theme={null}
DELETE /v1/data?user=alice&id=1287
```

```json theme={null}
{ "status": "ok", "deleted": 1, "subject": "alice" }
```

To erase *everything* about a Subject (records + files + conversations + the identity mapping) at once, use [`DELETE /v1/subjects/{user}`](/en/api-reference/lifecycle#offboarding-a-subject) — see [Compliance](/en/api-reference/compliance).

## Use-and-forget data

There is **no `retention: "none"`** — writing to the store while asking not to store is contradictory, and the value is rejected like any other non-enum value. Two clean patterns instead:

* **Dry-run analysis, zero side effects** — [`POST /v1/standardize`](/en/api-reference/extract) with `store=false` (the default): standardization results out, nothing written.
* **Short-lived working data** — write with `retention: "1h"` ([auto-expires](/en/api-reference/overview#data-retention) after an hour), or with `retention: "session"` + a `session_id` you [delete](/en/api-reference/lifecycle#sessions) when done.
