Syncky cookbook
Private recipes: weight, calories, workouts. Each uses generic send/inbox/receive and exact owner-approved permission arrays. Read setup and its security rules, and use the verified feature skill. No local Health ledger, summary or policy exists. Receive prints no records to stdout by default; records stay durable for inbox and MCP. receive --emit-records writes private JSONL to stdout. Services and schedules require explicit owner approval; keep discarding receive stdout as defence in depth and log metadata only. These are examples, never standing authorization to write Health values.
end must not be later than now + 1 h; the phone rejects a later end with invalid. For a running daily total, set start to local day start and end to now or the last diary entry time. Never use the end of the day. When a total changes, resend the same command_id with version + 1.
Put all nutrients of one day in one send with several commands. Send several entries in ONE send with a commands array; the transport does not combine separate calls. The 256 KiB (262144 bytes) data-envelope cap counts the complete serialized UTF-8 JSON, including base64 ciphertext and recipient metadata. The inner commands JSON can be up to roughly 191 KiB for one recipient, less with more recipients. The relay allows a burst of up to 60 data envelopes per agent, then about one a minute (about 60 data envelopes per hour per agent sustained), within the per-token limit of 60 requests a minute shared with polling, ACKs and heartbeats. A 429 rate_limited from either limit is retried automatically by the outbox after Retry-After when the receiver runs, so the caller does not resend. status --json shows transport.send_lanes (lane id to next allowed Unix time in milliseconds, including any legacy data-lane deadline) and pending sends with code, next_attempt_at and retry_after_until.
Access setup and recovery
access_request is an optional ask-ahead, not a required step before writes. For a catalog-writable type, a phone ack of type_not_in_scope, type_not_allowed or healthkit_denied means: tell the owner to allow that type on the phone (including Health Settings for iOS denial). With explicit owner approval, send an access_request for exactly those write types if the phone has not asked yet. Resend only after the next ordinary sync shows the effective write scope. A refused write creates no access request or automatic system consent prompt; its Home card may offer an owner-tapped Allow action. Unknown or read-only types cannot be enabled; correct the type instead.
On an invalid ack, check the command format first (--validate): fix timestamps, units or JSON. Schema validation does not cover phone policy; check interval bounds and units, then report an unexplained rejection without looping. Never ask for access on invalid. After a phone rejection, keep the same command_id and increment version before retrying; the phone caches completed rejections.
No phone app version or access-request capability is exposed in sync or status. If no matching access_reply arrives after an access_request, have the owner open Syncky and sync; if it remains missing, check whether consent is still pending on the phone and whether the request expired, then suggest the owner check for an app update without looping. Missing replies do not prove an old app: the phone may be offline or consent pending. A phone running an app version older than this feature cannot show the request; explain expiry from the timestamps rather than inventing a reply. access_request lists are sorted and deduplicated before sealing. --validate / syncky_send validation checks known types and writable writes; use exactly 24 h (expires_at - created_at == 24h) for compatibility with phones 0.1.1/0.1.2. An access_reply with outcome invalid carries an optional value-free detail (unknown_type, not_writable, ttl, empty, reason_too_long, malformed, created_in_future, too_large); read it in the raw inbox, correct the request and use a new request_ref. It grants no access.
For each writing recipe, start with its access_request setup for exactly the listed write types, then batch writes using the guidance above. This is the cookbook setup convention; already permitted writes do not require an access request. Replace historical request timestamps with current times exactly 24 hours apart and use a fresh request_ref. Access arrays must be sorted and unique; request only catalog-writable write types.
The sole confirmed phone is selected automatically; add --to when several are confirmed. A queued request is not a grant. Ask the owner to Allow/Decline each read/write item on the phone and confirm. Keep the existing receiver; on demand use syncky receive --once and respect Retry-After. Read the actual reply with syncky inbox --type access_reply, then wait for next ordinary sync and inspect effective scope with syncky status --json and syncky inbox --type sync. Never invent a local reply. Item outcomes are allowed, declined or needs_health_settings. Request-level expired, pending_limit or rate_limited is not a user decline.
The phone enforces one pending slot per agent, one admission per rolling day, 24-hour expiry and a seven-day type-wide Decline cooldown. Never bypass limits by changing the reason or reference. Syncky cannot observe Health read permission; missing data is unknown. For needs_health_settings, follow the phone's conditional Health Settings guidance; manual scope selection is Settings › Data & Health access › Choose Health data.
Allow adds only approved types to Health selection, without selecting their categories. Existing selected categories and individually allowed types are preserved. Choose Health data shows partial selection as Individually allowed: … under an unchecked category; Allow texts name the exact types. Existing read all stays all and follows later Health selection changes. Approved reads turn none into a chosen list and are unioned into existing read lists. Allowed writes add only the approved write types to the app allowance and request Health sharing only for those exact types, without adding reads; agent scope and iOS write permission still apply. If writing was off or the agent's effective write allowance was empty, its write scope becomes only the approved write types, even from all or an absent scope; otherwise existing write scope is unioned as before. Decline revokes nothing. The reply alone never updates cached scope. Permission to request access is separate from permission to write values: the type must be in the app's write allowance, within the agent's scope, and authorized by HealthKit.
Your own server
For a direct-server alternative, use the own-server recipe: Worker or Docker storage and server-local OAuth consent for remote MCP. Follow the server contract for uploads and replies. The agent recipes above retain their setup and permission rules; this alternative has no agent pairing.
The optional write tool is disabled by default and needs Allow newly consented writes in /admin plus fresh explicit per-client write consent for the intended types. The tool only queues commands for the next ordinary data reply. In the app, this direct server's Accept writes to Health setting must be on. The app applies writes only for supported types in its per-type write allowance, with iOS Health write permission and the Sync entitlement. There is no prompt per write. These controls grant permission only; each command should reflect the owner's intended entry.