Skip to main content
Need help with your Google Health integration? Pop into our Discord if you have questions or want to discover how Open Wearables can solve your problems.

Overview

Google Health data reaches Open Wearables through two independent paths, each its own provider:
  • Health Connect (mobile SDK) — provider health_connect. Data pushed from an Android device via the Mobile SDK. See the Android SDK guide.
  • Google Health API (cloud OAuth) — provider google_health, this guide. A server-side OAuth 2.0 flow against the Google Health API that lets you connect a user’s Google account and pull their data over REST, plus receive notify-only webhooks.
They have separate scopes, connections and lifecycles: connecting one leaves the other untouched, and each reports its own connection.created / connection.revoked events.
google_health is this provider’s name everywhere — in payloads, in ?provider= filters, and in the URLs new integrations should use. Before the split it was reachable at /google/; the OAuth and webhook endpoints still serve that path so anything already registered with Google keeps working, but it is a compatibility path that goes away in 1.0.
The Google Health API is an aggregation layer: it surfaces data from every source connected to the user’s Google Health account — the phone’s Health Connect store, Fitbit, Google Fit, and other apps — not just one device.

Supported data types

Data delivery

Data granularity and fetch modes

The Health API exposes the same underlying data through three different operations. Open Wearables reads every 24/7 metric at native resolution, picking the operation per the GOOGLE_USE_RECONCILE flag.
Windowed dataPoints:rollUp aggregates are currently disabled. A rollUp window starts wherever the requested range does, so the same reading came back under a different timestamp on a later sync and was inserted as a duplicate rather than updated.Because hourly and daily cannot be honoured without it, Google Health refuses them rather than storing raw data under an aggregating setting: a scheduled pull is reported as a failed data_247 sync, and a webhook notification is reported to the error tracker and dropped (it is not answered with a 5xx, which Google would only retry). Sleep sessions and derived daily totals are unaffected and keep syncing. Set the provider’s granularity to raw to resume the rest.
Daily basal energy is the one exception: it is derived from dataPoints:dailyRollUp, whose windows are anchored to civil days and so do not drift.

Reconcile vs. list — why it matters

A user commonly has the same activity reported by multiple sources (e.g. the phone’s pedometer and the Fitbit app both counting steps). The two modes handle that overlap differently:
  • reconcile (default) returns one merged stream, deduplicated across all sources at the interval level — exactly what the native Google Health / Fitbit app displays. There is no single device behind a merged value, so reconciled points carry no device attribution (device_model is empty).
  • list returns the raw per-source points, each tagged with its originating device. This preserves device attribution but stores overlapping sources separately; Open Wearables then deduplicates on read by source priority (it does not sum them).
A concrete example — steps for one day for a user tracked by both their phone (Health Connect) and Fitbit MobileTrack: Reconcile (2560) matches the app exactly — it keeps intervals that either source captured while removing the overlap. Read-time dedup of the list data would instead pick a single source (1919), which is why reconcile is the default: it produces app-accurate totals.
Choose list (GOOGLE_USE_RECONCILE=false) only when you specifically need per-device attribution (e.g. to know which watch recorded a reading). For app-matching totals, keep the default.

What you need by the end

  • A Google Cloud project with the Health API enabled
  • OAuth client credentials (Client ID + Secret) with the Google Health scopes
  • Redirect URI registered in your OAuth client
  • (Webhooks only) A service account for project-level subscriber registration

Prerequisites

  • A Google Cloud project
  • Access to the Google Health API for that project (gcloud services enable health.googleapis.com)

Application walkthrough

1

Enable the Health API and create an OAuth client

In the Google Cloud Console, select your project and enable the Health API:
Then create an OAuth 2.0 Client ID (Web application) under APIs & Services → Credentials, and add your server-side callback as an Authorized redirect URI:
  • http://localhost:8000/api/v1/oauth/google_health/callback
Then set GOOGLE_LEGACY_OAUTH_PATH=false so Open Wearables sends that URI. New integrations should always use this path.
Upgrading from before the provider split? This provider was reachable at /oauth/google/ back then, and the flag defaults to true so your registered OAuth client keeps working untouched. That default is a compatibility patch, not the intended configuration — it is removed in 1.0. Register the google_health URI above, set the flag to false, and you are on the supported path. Google accepts several redirect URIs, so both can be registered while you migrate.
Store the Client Secret securely — Google shows it once.
2

Configure credentials in Open Wearables

Add the following to your .env file:
Configuration details:
The redirect URI is derived from API_BASE_URL and must match an Authorized redirect URI on your OAuth client exactly: {API_BASE_URL}/api/v1/oauth/google_health/callback.
3

Connect a user via OAuth

With credentials configured and your instance running, initiate the OAuth flow.
Using the Open Wearables frontend? You don’t need any of the curls below — open the connect view and click Connect on Google Health. The frontend runs this whole authorize → consent → callback → verify flow for you. The steps below are for integrating directly against the API.
1. Get the authorization URL:
2. Redirect the user to the returned authorization_url. They log in to Google and grant consent.3. Google redirects back to {API_BASE_URL}/api/v1/oauth/google_health/callback; Open Wearables exchanges the code for tokens and stores the connection. It also resolves the user’s stable healthUserId (via the Health API identity endpoint) as the connection’s provider user id — this is what inbound webhooks are matched against.4. Verify the connection:
You should see a connection with "provider": "google_health" and "status": "active".
4

Sync data

An initial sync runs automatically after a successful OAuth connection. To trigger one manually:
5

Verify the integration

Fetch a synced 24/7 series (e.g. steps):
If data is returned, your Google Health API integration is working end-to-end.

Webhooks

Google Health webhooks are notify-only: each notification names the changed dataType, operation (UPSERT/DELETE), and the physical-time intervals that changed — but carries no data. Open Wearables verifies the request, then fetches the changed data over REST for those intervals. Two distinct secrets are involved:
  • GOOGLE_WEBHOOK_SECRET — a bearer token you register with Google that it echoes back in the Authorization header of every notification, so Open Wearables can verify the ping is genuine. Defaults to SECRET_KEY if unset.
  • Service-account credentials — used only to register the subscriber (a project-level admin call). Not involved in receiving notifications.
Receiving and processing webhooks needs only GOOGLE_WEBHOOK_SECRET. The service account is required only to register the subscriber programmatically — you can also register it manually via gcloud/the API console.

Webhook subscriber registration

Subscribers live at the project level (POST /v4/projects/{project}/subscribers), so registration authenticates as the project via a service account, not a user’s OAuth token.
gcloud commands need the project ID (e.g. open-wearables-prod). The GOOGLE_PROJECT_ID env var, however, needs the project number (e.g. 123456789012) — the subscriber API requires the number in its path.
1

Create a service account

2

Provide the credentials

Either mount a JSON key and point to it, or rely on Application Default Credentials (ADC) if running on GCP:
Add to .env:
Subscriber registration runs in the Celery worker, not the backend/API service. The key file (or credentials) and the GOOGLE_* env vars must be present on the worker service — a key mounted only into the backend container won’t be seen.
A JSON key never expires until deleted — prefer ADC / Workload Identity in production, and keep key files out of version control.
3

Register the subscriber

Switching the provider’s live sync mode to webhook registers the subscriber automatically. Google then performs an endpoint-verification handshake (POST {"type":"verification"}) against {API_BASE_URL}/api/v1/providers/google_health/webhooks, which must return 2xx when authenticated. Once verified, notifications begin flowing.

Managing subscribers without app credentials (gcloud / console)

If you’d rather not give the app a service account, create and manage the subscriber yourself and leave GOOGLE_SERVICE_ACCOUNT_FILE/GOOGLE_PROJECT_ID unset. The app then needs only GOOGLE_WEBHOOK_SECRET to verify inbound notifications — it never calls the project-level subscriber API.
In this mode the in-app .../webhooks/subscriptions management endpoints (list / register / update / delete) won’t work — they require project credentials. Use the commands below instead.
The endpointAuthorization.secret you set here must equal "Bearer " + the app’s GOOGLE_WEBHOOK_SECRET (which defaults to SECRET_KEY). If they don’t match, every notification fails signature verification and Open Wearables returns 401.
All calls authenticate with your own gcloud identity, which must hold the Health API subscriber role on the project. You can also run these from the Cloud Console API Explorer, but the CLI steps below are easier to copy end-to-end. Use the same subscriberId (open-wearables) the app uses, so it stays interchangeable.
dataTypes above is an abbreviated example — use the set the app supports for webhooks (steps, distance, hydration-log, heart-rate, run-vo2-max, daily-resting-heart-rate, heart-rate-variability, daily-heart-rate-variability, weight, body-fat, blood-glucose, daily-respiratory-rate, daily-oxygen-saturation, sleep, exercise). Registering a data type the app doesn’t handle just means its notifications are ignored.

Next Steps

API Reference

Explore the Open Wearables API endpoints.

Supported Providers

See all supported providers.

Support

Need Help?