数据
结构化记录
POST /v1/data、GET /v1/data、DELETE /v1/data:写入、读取与擦除结构化健康记录。
把结构化记录直接写进 Subject 的存储,并读回。结构化记录最主要的形式就是设备与可穿戴数据(见下文)。每次写入都会经过平台的标准化管线:解析值与单位,为识别出的指标补上 LOINC 编码和规范名称,每条记录都镜像到 FHIR。未解析出编码的行仍可正常读取。agent 通过内置工具读取同一份数据。
健康数据有四种形态,每种形态各有一个写入端点:
| 数据形态 | 典型来源 | 录入方式 | 端点 |
|---|---|---|---|
| 结构化记录 | 首要是设备与可穿戴数据(由你自己接入,日聚合后写入),另有手动记录 | 结构化记录 | POST /v1/data,高频样本先自聚合;episode 型(睡眠、运动)用 time + end_time 携带时段。见设备数据 cookbook。 |
| 文件/照片 | 化验单、体检报告 PDF、手机拍照 | 文件上传 | POST /v1/files,保存原件、抽取文本,并自动抽出标准化读数。POST /v1/standardize 可同步执行同一抽取,例如做 dry-run 预览。 |
| 带读数的叙事 | “头疼了一天,体温 38.2 °C”、对话口述 | 叙事文本 | POST /v1/standardize,抽取其中的可量化读数,周边叙事被丢弃。 |
| 纯主观日记 | “整个下午头晕头疼”、心情记录 | 单轮 agent 调用 | POST /v1/responses 带 store: true,自动从中抽取读数与持久记忆。见写日记配方。 |
Mirobody 负责存储源数据、标准化结构化读数,并将二者提供给 AI 使用;数据如何采集、是否预处理,由你自行决定。
POST /v1/dataAuthorization: Bearer mb_live_*Content-Type: application/json| 字段 | 类型 | 说明 |
|---|---|---|
records | array | 每条记录 {indicator, value, unit?, time?, end_time?, source?}。每次请求最多 500 条。 end_time(可选,ISO 时间)标记一段 episode(睡眠、运动)的结束,起点是 time。measured_at / start_time 是 time 的别名,POST /v1/standardize 返回的行(字段名为 measured_at)可原样转发。 |
user | string | 记录所属的 Subject(租户隔离键)。 |
retention | string | 必填:没有默认值。 取值为 permanent / 1h / 2h / 6h / 1d / session 之一(persistent 作为 permanent 的别名也接受)。有时限的档位到期即自动删除,1d 是最长的一档。省略它(或传枚举之外的任何值)返回 400(code: invalid_retention)。 |
session_id | string | retention=session 时必填(否则 400,code: invalid_session)。应使用全局唯一、不含业务语义的 id(如 UUID),且不能跨 Subject 复用。它为记录添加标签,使 DELETE /v1/sessions/{id} 能清除它们。 |
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"} ] }'响应如下,其中 standardized 统计写入途中成功解析到 LOINC 编码的记录数:
{ "status": "ok", "ingested": 2, "standardized": 2, "subject": "alice" }Episode 型(睡眠、运动)
Section titled “Episode 型(睡眠、运动)”Episode 型记录跨越一个时段而非时间点,起点放 time,终点放可选的 end_time:
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"} ] }'GET /v1/data?user=alice&indicator=fasting_glucose&limit=100Authorization: Bearer mb_live_*| Query | 说明 |
|---|---|
user | 读取哪个 Subject。 |
indicator | 可选,按指标名过滤(子串匹配)。 |
limit | 1–1000(默认 100)。 |
offset | 跳过 N 条用于翻页(排序稳定:最新在前)。has_more: true 时用 offset += limit 取下一页。 |
响应为 OpenAI 风格的列表,最新在前。value 是原样写入的值(如 "97"),单位单独放在 parsed_unit 中,与其它机读字段一同返回:
{ "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", "time": "2026-06-16T07:30:00+00", "end_time": null, "source": "api", "comment": "" } ], "has_more": false, "subject": "alice"}| 字段 | 说明 |
|---|---|
data | 记录数组(至多 limit 条)。 |
id | 行 id(整数),传给 DELETE /v1/data?id= 做行级删除。 |
value | 原样写入的值(字符串,如 "97");单位放在 parsed_unit 中。 |
parsed_value / parsed_unit | 标准化管线产出的数值 + UCUM 单位(无法解析时为 null)。 |
loinc_code / canonical_name | 确定性 LOINC 解析结果及其展示名(名称未解析时为 null)。 |
end_time | Episode 型记录(睡眠、运动)时段的结束时间;时间点读数为 null。 |
reference_low / reference_high / reference_text / abnormal | 参考范围与异常标记,来源(如化验单)携带时返回;否则为 null。 |
source / comment | 记录的来源方式:api(经 POST /v1/data 写入)、extract(来自 POST /v1/standardize)、upload(从 /v1/files 上传中读出)或 consolidation(从已存储的对话中得来)。你在记录自己的 source 字段里传入的来源标签,会在 comment 中返回。 |
has_more | 本页取满 limit 时为 true,用 offset += limit 取下一页。 |
subject | 记录所属的 Subject(由 user 解析)。 |
DELETE /v1/data?user=alice&indicator=fasting_glucoseAuthorization: Bearer mb_live_*永久擦除该 Subject 的记录:被遗忘权语义(数据被真正移除,而非仅从读取中隐藏)。三种范围,由窄到宽:
| Query | 范围 |
|---|---|
id | 恰好一行,即 GET /v1/data 返回的整数 id。优先于 indicator。 |
indicator | 单个指标(子串匹配,与 GET 过滤一致)。 |
| (都不传) | 该 Subject 的全部记录。 |
DELETE /v1/data?user=alice&id=1287{ "status": "ok", "deleted": 1, "subject": "alice" }如需一次性擦除某个 Subject 的一切(记录 + 文件 + 对话 + 身份映射),使用 DELETE /v1/subjects/{user},见合规。
设备与可穿戴数据
Section titled “设备与可穿戴数据”结构化记录最主要的形式就是设备与可穿戴数据。当你的厂商接入(Terra、Junction 或你自己 对接的厂商 API)已经把样本送进你的后端之后,写入做法如下。
接收
你的厂商接入以 webhook 推送或定时拉取的方式送来样本。
聚合到日级(建议)
高频序列在写入前先归并为每天一条。episode 型记录保留它自己的时段。
用 POST /v1/data 写入
单次请求最多 500 条,必须带 retention —— 契约见上文。
该聚合到什么粒度
Section titled “该聚合到什么粒度”POST /v1/data 接受任意粒度,但对多数产品来说,每个有意义的读数一行就够了:写入量更小,
序列也更容易解读。
- 日累计(步数、卡路里、距离):每天一条。
- 连续序列(心率、血氧等每几分钟一个采样):先聚合成日级值,例如静息心率或平均心率; 确实需要时再把最小值/最大值作为各自的指标写入。只在产品真正需要处才做得更细。
- episode 型(睡眠、运动):每段一条,用
time+end_time携带时段。
一次调用写入一天的设备记录
Section titled “一次调用写入一天的设备记录”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": "steps", "value": 12450, "time": "2026-07-10T00:00:00Z", "source": "garmin"}, {"indicator": "resting_heart_rate", "value": 58, "unit": "/min", "time": "2026-07-10T00:00:00Z", "source": "garmin"} ] }'按 Subject 把一整天(或一个回填窗口)批到一次调用里。用 source 标注厂商 —— 读取记录时
它会出现在 comment 字段中。
端上批量样本
Section titled “端上批量样本”有些样本根本不经过厂商 API:由你的 App 从手机自带的健康库读出(Apple HealthKit、 Android Health Connect,或直接与你的 App 配对的蓝牙体脂秤),再由 App 自己上传。 端点还是这一个,客户端需要额外定下两件事:
- 在设备本地成批并预聚合。 一个月的 HealthKit 心率样本有数万行,先归并成你真正会 查询的日级值,再按每次请求最多 500 条发送。
- 发送样本自带的时间戳,而不是上传时间:
time是读数被测量的时刻,睡眠与运动的 时段由end_time携带。
用 source 记下样本的来源("healthkit"、"health_connect"、你的设备型号),
便于事后追溯一条读数的出处。
用完即弃的数据
Section titled “用完即弃的数据”没有 retention: "none":写入存储却要求不存储是自相矛盾的,该值与其它非枚举值一样被拒绝。两种干净的替代模式:
- 零副作用的 dry-run 分析:用
POST /v1/standardize的store=false(默认),获得标准化结果,什么都不写。 - 短生命周期工作数据:以
retention: "1h"写入(一小时后自动过期),或以retention: "session"+ 一个用完即删的session_id写入。