把 Apple Health 数据发送到 webhook
Apple Health 没有网络 API,所以只有你 iPhone 上的应用主动发送,数据才会到达 webhook。Marrow Health 可以免费做到这一点:一个自动化会按你选的计划,在应用关闭的状态下,把你的健康指标、训练、力量组、饮食日记,或症状与心情,以 JSON 或 CSV 的形式 POST 到任意 URL。下面是设置方法、负载,以及 n8n、Home Assistant 和普通服务器上可用的接收端。Marrow 由我们开发。
设置自动化
- 在 Marrow Health 中打开“导出”标签页,选择自动,再点按新建自动化.
- 粘贴你的 webhook URL(请使用 HTTPS)。再添加接收端会检查的标题(请求头),比如
Authorization承载令牌,或 n8n 的 header-auth 密钥。 - 选择数据类型:健康指标、训练、力量日志、饮食日记或症状与心情。一个自动化只发送一种,最多可以建十个。
- 选择 JSON 或 CSV、日期范围(默认是距离上次跑步),以及运行频率,单位可以是分钟、小时或天。
- 开启后点按立即运行,在你盯着接收端的同时发出第一份负载。
每个请求都是一个 POST,带有 Content-Type: application/json(或 text/csv)和你设置的标题。每次运行及其响应码都会列在应用中该自动化的历史记录里。
收到的内容
健康指标以每日总计的形式送达,单位见 units。心率给出每天的平均值、最小值和最大值,睡眠则按每晚给出各个阶段,以小时为单位:
{"data": {"metrics": [
{"name": "body_mass", "units": "kg",
"data": [{"date": "2026-09-11", "qty": 81.3}]},
{"name": "step_count", "units": "count",
"data": [{"date": "2026-09-11", "qty": 8746}]},
{"name": "heart_rate", "units": "bpm",
"data": [{"date": "2026-09-11", "Avg": 71, "Min": 52, "Max": 148}]},
{"name": "sleep_analysis", "units": "h",
"data": [{"date": "2026-09-11", "totalSleep": 7.2, "asleep": 7.2, "deep": 1.1, "rem": 1.6, "core": 4.3, "awake": 0.2,
"sleepStart": "2026-09-10 23:36:00 -0400", "sleepEnd": "2026-09-11 07:02:00 -0400"}]}
]}}
训练、力量、营养和心情的负载、用原始样本代替每日总计的格式,以及 CSV 的排布,见自动化参考页面.
在 n8n 中接收
- 添加一个 Webhook 节点。把 HTTP Method 设为
POST,并给它一个路径,比如apple-health. - 如果要设密钥,把 Authentication 设为 Header Auth,并把同样的标题名称和值添加到 Marrow 的自动化里。
- 点击 Listen for test event,然后在两分钟内到 Marrow 里点按立即运行。负载会出现在编辑器中。
- 发布工作流,再让 Marrow 指向 Production URL(带
/webhook/的那个,不是/webhook-test/)。测试 URL 在一次测试之后就不再监听。
负载在 body 下面,所以在表达式中,今天的步数是 {{ $json.body.data.metrics.find(m => m.name === 'step_count').data.at(-1).qty }}。在 body.data.metrics 上添加一个 Split Out 节点,就能把每项指标当作单独的条目来处理。n8n 默认接受最大 16 MB 的负载,足够容纳数年的每日总计。正式运行显示在 Executions 下,而不是编辑器里。 (n8n Webhook 文档)
在 Home Assistant 中接收
webhook 触发器在 /api/webhook/<webhook_id> 接收这个 POST。下面这个自动化把最新的步数存入名为 input_number.steps_today 的数字辅助元素(请先创建它,最大值要高于你步数最多的那天):
automation:
- alias: "Apple Health from Marrow"
triggers:
- trigger: webhook
webhook_id: "pick-a-long-random-id"
allowed_methods:
- POST
local_only: false
actions:
- action: input_number.set_value
target:
entity_id: input_number.steps_today
data:
value: >-
{{ ((trigger.json.data.metrics
| selectattr('name', 'eq', 'step_count') | first).data
| last).qty }}
- JSON 内容在
trigger.json里,因为 Marrow 发送的是Content-Type: application/json. local_only默认开启,这样只接受来自你家庭网络或 Nabu Casa 云端 webhook URL 的请求。如果手机是通过互联网向你自己的 HTTPS 地址发送,请把它设为false。- webhook ID 是唯一的密钥,所以要让它又长又随机。
(Home Assistant webhook 触发器文档)
在你自己的服务器上接收
任何接受 HTTPS POST 并返回 2xx 的端点都行。下面这个会把每份负载存档到一个文件里:
from http.server import BaseHTTPRequestHandler, HTTPServer
import time
class H(BaseHTTPRequestHandler):
def do_POST(self):
body = self.rfile.read(int(self.headers.get("Content-Length", 0)))
with open(f"marrow-{int(time.time())}.json", "wb") as f:
f.write(body)
self.send_response(200)
self.end_headers()
HTTPServer(("0.0.0.0", 8798), H).serve_forever()
用 Cloudflare Tunnel、Tailscale Funnel 或反向代理把它放到 HTTPS 后面。如果你想要的是可查询的数据库,而不是一个文件夹,Marrow 的自托管镜像就是已经写好的这种接收端,AI 智能体也能查询它。
不丢数据的接收端要守的三条规则
- 以最新为准。按指标和日期为键保存每个值,重复时直接覆盖。距离上次跑步会特意重发最近两天,因为 Apple Health 在一天结束后还会继续补充数据。
- 先保存原始内容,再解析。解析出错绝不该让你丢数据。
- 尽快响应。Marrow 最多等到你设置的超时时间(默认 60 秒),失败的运行会在下一轮从上一个成功的时间窗口起重试。
它什么时候运行?
只要 iOS 允许 Marrow 运行就会运行:在它与 Apple Health 同步之后、你打开它时,以及后台刷新期间,前提是你选的间隔已过。后台刷新何时发生由 iOS 决定,所以整天锁屏的手机会在下次解锁时发送,距离上次跑步会补上这段空档。
其他做法
- Health Auto Export 也能把 Apple Health 数据发送到 webhook,它的指标 JSON 与 Marrow 的结构相同。它的自动化需要 Premium 档。
- Apple 快捷指令可以读取健康样本,并用 Get Contents of URL 在设定的时间通过个人自动化发送出去。它免费,但只有在手机解锁时才能读取“健康”数据,而且每项指标都要你自己搭建和维护。
- Apple 的 export.zip 是一次性的完整备份,不是持续的数据流。要把它转成 CSV,请用我们的浏览器内转换器.