* refactor(config): consolidate media model lists * refactor(config): unify memory configuration * refactor(config): consolidate TTS ownership * refactor(config): move typing policy to agents * refactor(config): retire product-level config surfaces * refactor(config): share scoped tool policy type * chore(config): refresh generated baselines * fix(config): honor agent typing overrides * fix(config): migrate sibling config consumers * refactor(infra): keep base64url decoder private * fix(config): strip invalid legacy TTS values * chore(config): refresh rebased baseline hash * fix(doctor): route legacy messages.tts.realtime voice to talk during tts move * refactor(config): polish final layout names * refactor(config): freeze retired tuning defaults * feat(config): add fast mode default symmetry * refactor(config): key agent entries by id * docs(config): update final layout reference * test(config): cover final layout migrations * chore(config): refresh final layout baselines * fix(config): align final layout runtime readers * fix(config): align remaining readers * fix(config): stabilize final layout migrations * fix(config): finalize config projection proof * fix(config): address final layout review * docs(release): preserve historical config names * fix(config): complete keyed agent migration * fix(config): close final migration gaps * fix(config): finish full-branch review * fix(config): complete runtime secret detection * fix(config): close final review findings * fix(config): finish canonical docs and heartbeat migration * fix(config): integrate latest main after rebase * refactor(env): isolate test-only controls * refactor(env): isolate build and development controls * refactor(env): collapse process identity indirection * refactor(env): remove duplicate config and temp aliases * docs(env): define the operator-facing allowlist * ci(env): ratchet production variable count * fix(env): remove stale provider helper import * fix(env): make ratchet sorting explicit * test(env): keep test seam in dead-code audit * test(env): cover ratchet growth and boundary; document surface budgets * docs(config): document tier-eval consolidations * docs(config): clarify speech preference ownership * test(memory): align retired tuning fixtures * refactor(memory): freeze engine heuristics * refactor(config): apply tier-eval tranche * refactor(tts): move persona shaping to providers * refactor(compaction): move prompt policy to providers * test(config): align hookified prompt fixtures * chore(deadcode): classify test-only exports * chore(github): remove unused spawn helper * chore(deadcode): classify queue diagnostics * chore(deadcode): remove unused lane snapshot export * chore(plugin-sdk): ratchet consolidated surface * fix(config): integrate latest main after rebase
6.3 KiB
summary, read_when, title
| summary | read_when | title | |||
|---|---|---|---|---|---|
| Enable and invoke privacy-gated HealthKit summaries from an iOS node |
|
HealthKit summaries |
HealthKit summaries
OpenClaw can request a read-only summary of the current calendar day from a connected iPhone or iPad node. The device computes the aggregate on-device and returns only steps, sleep duration, average resting heart rate, and workout count/duration. Individual HealthKit samples, sources, metadata, clinical records, background ingestion, and writes are not supported.
This feature is off by default. It requires separate consent on the iOS device and authorization on the Gateway.
Requirements
- An iPhone or iPad running the OpenClaw iOS app where HealthKit reports health data as available.
- A connected and approved iOS node. See iOS app setup.
- A current Gateway that can reach the iOS node.
- Readable Health data for any metrics you expect to see. An Apple Watch can contribute data to the Apple Health store, but the OpenClaw watchOS app is not required for HealthKit summaries.
Enable access
1. Authorize the Gateway command
Add health.summary to the existing gateway.nodes.commands.allow array in
openclaw.json. Preserve any commands already present:
{
gateway: {
nodes: {
commands: { allow: ["health.summary"] },
},
},
}
health.summary is classified as privacy-heavy and is never allowed by the
iOS platform default. An entry in gateway.nodes.commands.deny overrides the
allow entry. See Node command policy.
2. Enable sharing on the iOS device
In the iOS app:
- Open Settings -> Permissions and find Apple Health Summaries in the always-visible Apple Health section.
- Tap Enable Apple Health Summaries.
- Read the disclosure, then choose which Health categories OpenClaw may read in Apple's permission sheet.
The switch records your explicit OpenClaw sharing choice. It does not claim that Apple granted every requested category.
Enabling Health summaries adds health.summary to the node's declared command
surface. Approve the resulting node pairing update:
openclaw nodes pending
openclaw nodes approve <requestId>
Then verify that the connected iOS device exposes an effective health.summary
command:
openclaw nodes describe --node "<iOS device name>"
Request today's summary
Only today is supported. It covers local midnight through the request time,
using the iOS device's current calendar and time zone.
openclaw nodes invoke \
--node "<iOS device name>" \
--command health.summary \
--params '{"period":"today"}' \
--json
Agents can call the same command with the nodes tool:
{
"action": "invoke",
"node": "<iOS device name>",
"invokeCommand": "health.summary",
"invokeParamsJson": "{\"period\":\"today\"}"
}
The summary payload contains:
| Field | Meaning |
|---|---|
period |
Always today |
startISO |
Local start of day, encoded as an ISO instant |
endISO |
Request time, encoded as an ISO instant |
timeZoneIdentifier |
iOS device time-zone identifier |
stepCount |
Rounded cumulative steps |
sleepDurationMinutes |
Deduplicated asleep time, clipped to today |
restingHeartRateBpm |
Average resting heart rate |
workoutCount |
Workouts that started today |
workoutDurationMinutes |
Total duration of those workouts |
Metric fields are optional and are omitted when HealthKit returns no readable value. Sleep stages and overlapping sources are merged before duration is calculated, so the same minute is not counted twice.
Privacy behavior
- Aggregation happens on the iOS device. Raw samples do not leave the device.
- The requested aggregate leaves the device through your Gateway. When an agent requests it, the aggregate reaches the configured AI provider and may remain in chat history. A direct CLI invocation returns it to the CLI operator.
- OpenClaw requests read access only. It cannot add or modify Health data.
- OpenClaw reads HealthKit only when
health.summaryis invoked. There is no background health ingestion. - HealthKit deliberately does not reveal whether read access was denied. A missing metric can mean denied access, no matching samples, or an unavailable data type. OpenClaw cannot distinguish those cases.
- The summary is for personal health and fitness context, not diagnosis or medical advice.
To stop sharing, return to Apple Health Summaries and tap Turn Off Summaries.
The iOS device then removes the Health capability and health.summary command from its node
surface. You can also remove health.summary from
gateway.nodes.commands.allow to close the Gateway side of the gate.
Troubleshooting
Command is not declared by the node
Confirm Apple Health summaries are enabled in the iOS app and the device is connected.
Run openclaw nodes pending and approve any capability update, then inspect
openclaw nodes describe --node "<iOS device name>" again.
Command requires explicit opt-in
Add health.summary to gateway.nodes.commands.allow. Also check that
gateway.nodes.commands.deny does not contain it; the deny list wins.
HEALTH_ACCESS_DISABLED
The app-side sharing switch is off. Enable Apple Health Summaries under Settings -> Permissions -> Apple Health on the iOS device.
Summary succeeds but metrics are missing
Open Apple's Health app and confirm that data exists for today. Review OpenClaw's access in Apple's Health settings, but do not treat an empty result as proof that access was denied: HealthKit intentionally hides that distinction.
Older ranges fail
The command accepts only {"period":"today"}. Multi-day and historical
summaries are not supported.