How to send Apple Health data to Google Sheets

What do you get?

Syncky Health sends selected Apple Health data to a direct webhook on an automation receiver, which writes rows to Google Sheets. Free during launch; plans are coming later. See Syncky launch access and pricing; automation service plans are separate.

This route does not require MCP or Syncky Relay, and no agent pairing is needed. Nothing passes through Syncky's servers on this route. The automation receiver receives plaintext inside the connection and forwards chosen fields to Google Sheets. Restrict spreadsheet sharing and review both services' storage and history. iOS schedules background work and Health reads require an unlocked iPhone, so deliveries can be hours late.

What do you need before setup?

Use a spreadsheet and an automation receiver: n8n, Make or Zapier. This guide uses Zapier's documented Google Sheets actions, including Create Spreadsheet Row, as the concrete destination. Prepare parsing, a test guard and a deduplication store before enabling row creation.

Do not put an Apps Script /exec URL into Syncky as the receiver. Google's Content Service documentation requires following a redirect to a one-time script.googleusercontent.com URL. The known /exec POST response is 302; Syncky never follows redirects and treats all 3xx as failure. A script could already have appended a row before the failed acknowledgment, so retries can duplicate it. Google's web-app request event exposes parameters and postData.contents, with no request-header field for token or Syncky HMAC checks.

Not verified by Syncky: a live Apps Script deployment or an end-to-end Sheets automation. The documented Content Service redirect conflicts with Syncky's delivery contract, so this guide routes through an automation receiver and supplies no direct Apps Script recipe.

How do you set up Google Sheets?

  1. Create a private sheet with columns for device, UUID, part, type, start, end, value, unit and source. Store one row per sample or series part. For CSV/HAE, also assign a separate receiver identity to each phone/connection.
  2. Configure a production webhook using the Zapier guide, or the linked n8n/Make guide. Use an HTTPS URL that answers 2xx without redirects. Choose the receiver's authentication from the table below and arrange for no-data tests to create no rows.
  3. In Syncky Health, open Settings › Connections › + › Server or automation. Enter the automation URL, choose Syncky native JSON for UUID-based deduplication, and match its authentication. Health Auto Export JSON is a fallback with the collision risk below; CSV requires a receiver/parser that handles raw CSV. Select types and Health read permissions; keep Accept writes to Health off.
  4. In the automation, parse and iterate one sample at a time using the mapping below. For each key, update an existing row or create one if absent. In Zapier, configure the documented Google Sheets Lookup Spreadsheet Row, Update Spreadsheet Row and Create Spreadsheet Row actions as appropriate. Serialize or atomically reserve keys in your automation store so concurrent retries cannot both append.
  5. Tap Check connection. Require a successful acknowledgment and no new sheet rows. Only then deliberately use Sync now and check row counts privately. Not verified by Syncky: connector permissions, row mapping and concurrent deduplication in your chosen automation; validate them with synthetic records first.

What can Google Sheets read and write?

The table describes authentication at the automation receiver. Direct Apps Script is unusable because its 302 fails Syncky's connection check. Authorization from the automation to Google Sheets is separate; use the connector's Google account permissions. Google Sheets itself is not a Syncky webhook receiver. Review authentication and signatures for your receiver.

Authentication Automation webhook receiver
URL secret Supported via a secret automation hook URL.
Bearer Workaround: validate at a capable automation receiver.
Header Supported via Make's API Key authentication with Syncky Header (x-make-apikey); see also the n8n guide.
Basic Workaround: configure the receiver to validate Basic.
HMAC Workaround: verify exact raw bytes and timestamp at a capable receiver.

With a URL-secret hook, the URL is the only secret: anyone who obtains it can submit rows. Keep it out of screenshots and logs. A downstream check cannot undo disclosure to the receiving service. Do not assume an API-key option also verifies HMAC.

For Syncky native JSON (the default), iterate samples and use (device, uuid), adding part for split series as (device, uuid, part). Take device from the envelope. Process deleted by removing all rows for (device, uuid), including every series part.

For CSV, use an RFC 4180 parser, not splitting on commas or newlines. Skip each request's header row and create one row per sample; treat value_name=deleted as removal of the stored record, not a numeric sample. Deduplicate by (receiver identity, uuid). CSV's device column describes source hardware, not Syncky's install ID. The receiver identity must come from your connection configuration. CSV omits some Health kinds; see the format contract.

For the HAE JSON fallback, iterate data.metrics, then each metric's data array, creating one row per data point with metric name, units, date, source and its available value fields. Preserve Min, Avg, Max when present instead of inventing a single value. Use (receiver identity, metric name, units, point date, point source) as a provisional replacement key; HAE has no sample UUID, so distinct samples can collide. A key collision replaces a distinct sample and loses its data. Use native JSON or CSV if you require UUID-based deduplication. Store workouts separately by (receiver identity, workout id) and update a repeated workout; HAE carries no deletions.

For all formats, deduplicate by record identity rather than delivery ID: a resend can contain the same sample in a new delivery. Do not add repeated values together. This route has no Health write-back; editing a cell cannot change Apple Health. Google's values API distinguishes RAW from USER_ENTERED; configure literal text handling for imported strings so they cannot become spreadsheet formulas.

How do you test the connection?

Check connection sends no health data. HAE is {"data":{"metrics":[],"workouts":[]}}; CSV is the header row alone; native has delivery.test: true and empty arrays. The automation must accept these without creating rows and answer 2xx; an empty body is sufficient. This checks the receiver, not Google permissions or row mapping. Use synthetic fixtures to check those separately, without reading Health sample values.

Syncky starts with request bodies up to 1,000,000 bytes. Each 413 halves the limit for a later attempt, down to 65,536 bytes. Choose a receiver that accepts those sizes; a lower limit cannot be solved by further shrinking. See responses and limits.

A 302 from Apps Script is a failed connection check. Do not save the one-time redirected URL or retry the direct recipe. Keep the automation's stable webhook URL in Syncky. Inspect status and counts without logging full URLs, authentication headers, signatures or bodies.

How do you troubleshoot this setup?

What are the frequently asked questions?

Can I fix direct Apps Script delivery by putting a token in the URL?

No. A query parameter does not remove Content Service's redirect or expose headers to doPost. Keep the stable automation receiver between Syncky and Sheets.

Does a successful check test spreadsheet writes?

No. The no-data request must create no rows. Validate row mapping and deduplication with synthetic records before deliberately syncing selected Health data.

Agent setup · MCP comparison · All guides · Privacy · Public security policy · FAQ · Support.