September 16, 2026 · 13 min read · Sugam Budhraja

Health Data Has Four Authorization Models, and a Cross-Platform App Is Usually Running Three

HealthKit's permission sheet, Health Connect's runtime grants, OAuth against a wearable cloud, and vendor partner programs are not variations of the same consent. They fail differently, they reach a server differently, and a product that treats them as one integration will mis-scope the Apple Watch path, the Android history window, and Garmin access in the same quarter.

“We support wearables” is usually scoped as one integration. It is four authorization models that do not share a consent UI, a failure mode, or a path to your server.

HealthKit puts a system sheet on the iPhone and will not tell you whether the user denied a read [1]. Health Connect puts a runtime grant on Android and, by default, will not let you read further back than thirty days from that grant [2]. Oura and WHOOP use OAuth 2.0, so a backend can pull, and a revoked token at least returns 401 [3] [4]. Garmin’s Health API is OAuth too, but only after Garmin has admitted you to a partner program that is currently telling new applicants to stay tuned [5].

A cross-platform coaching app is typically running HealthKit, Health Connect, and one OAuth cloud at once. The partner-program cell is the one that closed.

This is the map. The deep dives sit underneath it: what still works now that Garmin paused new API access, why a web app cannot read Apple Health, and why App Review rejects HealthKit apps.


The four models

HealthKitHealth ConnectOAuth cloudPartner program
Where consent happensiOS system sheet, per typeAndroid runtime screen, per type, plus extra history and background grantsBrowser or in-app OAuth 2.0You apply; they review; then OAuth
Who holds the grantAn installed iOS appAn installed Android appYour backend, via refresh tokensYour backend, after they issue credentials
Server can pull?Only if your app uploadsOnly if your app uploadsYesYes, once you are in
Denial looks likeEmpty query. Read status is hiddengetGrantedPermissions misses the type; history/background declined separately401 / revoked grantThe form is gone, or you wait indefinitely
Typical sourcesApple Watch, iPhone, apps writing to HealthGalaxy Watch, Fitbit on-device, phone steps, apps writing to Health ConnectOura, WHOOP, Withings, Fitbit via Google Health APIGarmin Health / Activity / Training / Courses / Women’s Health APIs

Oura and WHOOP sit in the OAuth column because a backend can pull after consent. They still have a partner-program admission step — ten users until production approval — which is why the licence layer and the permission layer are different conversations.

Two facts fall out of that table immediately.

A server cannot OAuth against Apple Watch. There is no HealthKit REST API [6]. The Watch writes to the phone; the phone’s HealthKit store is readable only by an app running on that phone with a granted type. If your architecture is a Next.js app with a webhook and no iOS binary, Apple Watch is not a source. Capacitor does not create a web API; it creates a native app that happens to render HTML.

OAuth is not the general case. It is the case for vendors who published a cloud and still let you in. That set is smaller than the device list on a marketing page, and it moves: Fitbit’s Web API sunsets in September 2026, Google Fit’s REST API ends with 2026, and Garmin’s program is paused for new applicants.

Scope the on-device stores first if your users carry phones. Apple Watch data, Galaxy Watch data, and the phone’s own steps live in HealthKit and Health Connect. Starting with a Garmin OAuth ticket assumes a partner-program outcome you do not control and leaves the majority device path unbuilt.

HealthKit is a framework on iOS, iPadOS, watchOS and visionOS [6]. You add the HealthKit capability, you set NSHealthShareUsageDescription (and NSHealthUpdateUsageDescription if you write), you call requestAuthorization(toShare:read:), and the system presents a sheet listing every type you asked for.

Three properties of that sheet decide architecture.

Read denial is invisible. authorizationStatus(for:) reports whether your app may save a type. It does not report whether the user allowed a read. Apple’s privacy design is that revealing a denied read would itself leak information. You query; you get samples or an empty array; empty is also what you get when the user has no Apple Watch, or did not wear it, or had a quiet day [1] [7]. Provenance (HKSourceRevision, HKDevice) tells you who wrote a sample. It does not tell you why nothing was written. We unpacked the hardware split in Apple Health without an Apple Watch.

The grant is to your app, not your company. A backend never holds a HealthKit token. Background delivery, which is how you sync without the user opening the app, requires the com.apple.developer.healthkit.background-delivery entitlement on iOS 15 and later; without it, enableBackgroundDelivery fails with authorization denied [8]. If you do not call the observer-query completion handler, HealthKit backs off and, after three failures, stops sending background updates [14].

App Review sits on this model. Guideline 2.5.1 requires HealthKit to be used for health and fitness purposes and to integrate with the Health app. Guidelines 5.1.2(vi) and 5.1.3(i) prohibit using that data for advertising, marketing, or use-based data mining, including by third parties [9]. The review post is the field guide; the architectural point here is that choosing HealthKit is choosing Apple’s privacy regime, not just Apple’s sensors.


Health Connect is the on-device store on Android [10]. From Android 14 it is a framework module; on 13 and lower it is a Play Store app. You declare each type in the manifest, you declare the same set in the Play Console Health apps form, and you request runtime permission. Google’s permission APIs will tell you what was granted, which is the first material difference from HealthKit.

The second is the extras.

History is a separate permission. By default an app can read data for up to 30 days prior to when any permission was first granted. PERMISSION_READ_HEALTH_DATA_HISTORY lifts that. Without it, reading older records errors. Reinstalling the app revokes permissions and restarts the clock from the new grant [2]. We covered the step-counting version of this in Health Connect without a wearable.

Background is a separate permission. PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND is required to read after the user or the system has backgrounded you. Decline it and you have a foreground-only integration that looks like a passive one in the spec.

The 30-day history window is not the 30-day change-token window. Incremental sync uses getChangesToken / getChanges. Those tokens expire about a month after they are issued; an expired token is a full resync, not a denied permission [15]. Teams that treat “30 days” as one number mix a consent rule with a cursor lifetime.

The server still cannot pull. Health Connect has no OAuth. Google’s cloud path is the Google Health API, which is account-centric, uses Restricted scopes, and documents Fitbit and Pixel hardware. Do not assume it returns a Samsung or Garmin watch that wrote into Health Connect on the phone.


OAuth cloud: the model everyone wishes they had

Oura, WHOOP, Withings, and Fitbit-via-Google expose cloud APIs. The user signs in at the vendor, your backend receives an authorization code, you store a refresh token, you pull or subscribe to webhooks. A revoked grant fails as an HTTP error. That is why this model dominates architecture diagrams drawn before anyone has shipped on iOS.

It has its own gates.

Oura caps a new application at ten users until Oura approves it for production, and Gen3 and later users without an active Oura Membership cannot be read through the API at all [3]. WHOOP is similar: development is limited to ten members; a public launch requires App approval [4]. Those are partner-program dynamics wearing OAuth clothing.

The licence layer is not the permission layer. Strava’s API Policy prohibits putting Strava data into a context window. Oura splits aggregator vs direct developer. Garmin (for those already in) permits AI processing subject to a disclosure and consent regime. We quoted those documents in Can you feed wearable data to an AI?. An OAuth grant is not a licence to do whatever your product does with the payload.

Coverage is the users who connected that cloud, not the users who own a watch. An Apple Watch user has nothing to OAuth against unless they also have Oura or WHOOP. A Garmin user has a cloud, but you may not be allowed to call it.


Partner program: OAuth behind a door that can close

Garmin’s Connect Developer Program is the clean example, because it is five APIs behind one application: Health, Activity, Women’s Health inbound; Training and Courses outbound [5]. The Health API is REST, ping/pull or push, JSON summaries after the user syncs to Garmin Connect [11]. None of that is available until Garmin has issued you credentials.

As of this writing, the public program pages lead with “Stay tuned for more updates on the program” [5]. Applicants posting Garmin’s reply on the developer forums have been told new API access requests are paused, with no timeline [12]. Existing partners keep working. Connect IQ, which builds watch faces and on-device apps, is a different program and is still shipping — 9.2.0 went out on 25 August 2026 [13].

The architectural fact is independent of this particular pause. A partner program is an authorization model whose first failure mode is you never get in. You cannot retry it in code. You cannot A/B it. You put it on a critical path only if you have a fallback that does not need it. For Garmin, that fallback is whatever Connect writes into Health Connect and Apple Health, plus the phone, which is a different and thinner payload. The Garmin post is the current state of those paths.

Other vendors run quieter versions of the same gate: Oura’s production review, WHOOP’s App approval, Dexcom’s partner process, the Google Health API’s Restricted-scope security review. If your roadmap says “we’ll add X once we get API access,” you have a partner-program dependency, even if the word Garmin never appears.


Which model actually covers the user

User in front of youModel that can see themWhat fails
iPhone, Apple Watch, no other wearableHealthKitAny plan that is OAuth-only
iPhone, no WatchHealthKit, phone-motion types onlyHeart rate, HRV, sleep stages — Watch or another writer
Android, Galaxy WatchHealth ConnectA backend with no Android app
Android, Pixel WatchHealth Connect, or Google Health API if you have Restricted-scope OAuthAssuming Pixel is “just another watch”
Garmin, and you are already a partnerPartner-program OAuthEveryone else on the waitlist
Garmin, and you are not a partnerHealth Connect / Apple Health if they enabled the writeComposites, Training API, users who never toggled it
Oura or WHOOP, under the ten-user capOAuth cloudProduction until they approve you
No wearablePhone sensors, via a native appA web-only product

The Watch-versus-phone type split is Apple Health without an Apple Watch. Pixel via the Google Health API is still not a Galaxy Watch that only wrote into Health Connect.


What a real architecture does

A product that spans iPhone, Android, and a couple of premium wearables is not choosing one row of the first table. It is running:

  1. A native iOS surface (your app or an SDK inside it) that requests HealthKit types, handles empty reads as ambiguous, and uploads. Background delivery entitlement on, observer queries registered at launch, completion handler called on every path.
  2. A native Android surface that requests Health Connect types and history and background, persists a change token, and degrades when any of the three is declined or the token has expired.
  3. OAuth clients for the clouds you can actually enter, with token refresh, webhook verification, and a source filter before anything hits a model, because the licences disagree.
  4. A partner-program queue treated as a lead time, not a sprint task, with an explicit product for users who never get that source.

The silent-failure row is the one to design first. HealthKit empty, Health Connect revocation, and a watch left in a drawer are indistinguishable from a user who stopped moving unless you have another signal. Cloud 401s at least throw. Partner-program silence does not even produce a user.

The next sunset will hit one cell, not all four. Fitbit’s Web API, Google Fit REST, Garmin’s pause, Apple dropping iPhone sleep tracking in iOS 18 — each broke a single authorization model. An architecture that keeps HealthKit, Health Connect and OAuth behind one schema survives that. An architecture that is one vendor’s OAuth does not.

There are two structural answers, and both are defensible. Keep each source a thin module behind your own types, so the next closed form is a contained project. Or put the class of problem below an abstraction layer such as Sahha, which still has to obtain the same four grants — it does not invent a fifth — but presents one schema and one webhook to the product. What does not work is drawing a single OAuth box and labelling it “wearables.”

References

  1. Apple. Authorizing access to health data, including the asymmetry between read and write permission visibility. Retrieved 17 September 2026. https://developer.apple.com/documentation/healthkit/authorizing-access-to-health-data
  2. Android Developers. Read data older than 30 days; PERMISSION_READ_HEALTH_DATA_HISTORY. Last updated 8 September 2026. https://developer.android.com/health-and-fitness/health-connect/read-data
  3. Oura. API documentation: applications start with a ten-user limit; production requires approval. Gen3 and later users without Membership cannot be read through the API. https://cloud.ouraring.com/v2/docs and https://support.ouraring.com/hc/en-us/articles/4415266939155-The-Oura-API
  4. WHOOP. App Approval: development limited to 10 members; public launch requires approval. https://developer.whoop.com/docs/developing/app-approval/
  5. Garmin. Connect Developer Program overview. Public pages currently lead with “Stay tuned for more updates on the program.” Retrieved 17 September 2026. https://developer.garmin.com/gc-developer-program/overview/
  6. Apple. HealthKit. There is no server-side API; access is through the on-device framework. https://developer.apple.com/documentation/healthkit
  7. Apple. HKHealthStore.authorizationStatus(for:). Reports authorization to save, not to read. https://developer.apple.com/documentation/healthkit/hkhealthstore/authorizationstatus(for:)
  8. Apple. Configuring HealthKit access — Background Delivery capability adds com.apple.developer.healthkit.background-delivery. On iOS 15+, enableBackgroundDelivery fails without it. https://developer.apple.com/documentation/xcode/configuring-healthkit-access
  9. Apple. App Store Review Guidelines, 2.5.1, 5.1.2(vi), 5.1.3(i). Retrieved 17 September 2026. https://developer.apple.com/app-store/review/guidelines/
  10. Android Developers. Get started with Health Connect. Last updated 8 September 2026. https://developer.android.com/health-and-fitness/health-connect/get-started
  11. Garmin. Health API. REST, ping/pull or push, JSON summaries after the user syncs to Garmin Connect. Retrieved 17 September 2026. https://developer.garmin.com/gc-developer-program/health-api/
  12. Garmin Forums. Developer onboarding blocked. Applicants quoting Garmin Connect Developer Program email: new API access requests temporarily paused, no specific timeline. Thread includes the quoted reply. https://forums.garmin.com/developer/fit-sdk/f/discussion/435484/developer-onboarding-blocked
  13. Garmin. Connect IQ SDK overview. Latest version 9.2.0, updated 25 August 2026. A separate program from the Connect Developer Program APIs. https://developer.garmin.com/connect-iq/overview/
  14. Apple. HKObserverQueryCompletionHandler. If you do not call the block, HealthKit backs off; after three failures it stops background updates. https://developer.apple.com/documentation/healthkit/hkobserverquerycompletionhandler
  15. Android Developers. Synchronize data with Health Connect — getChangesToken / getChanges; tokens expire after an extended period (on the order of a month). https://developer.android.com/health-and-fitness/health-connect/sync-data

Related