Files
HealthLog/docs/integrations/garmin.md
Marc-André Bombeck 75eb0637e6 feat(strava): add per-user BYO-OAuth workout source + Garmin indirect-path docs
Add Strava as a per-user BYO-OAuth workout source, mirroring the shipped
WHOOP/Oura/Polar pattern. Strava caps every new app at single-player mode, so
each self-hoster registers their own app and pastes the client id/secret into
Settings (falling back to a shared env app). Strava is a workout source only —
it exposes no daily metrics and no training-depth analytics are derived.

- Schema (migration 0233): per-user encrypted client id/secret + access/refresh
  tokens, plaintext athlete id, incremental cursor, and backfill marker on
  `User`; `STRAVA` added to `MeasurementSource`.
- Token model follows Oura's reactive-refresh-on-401 with a compare-and-set on
  the stored refresh ciphertext, because Strava rotates its refresh token on
  every refresh.
- Client over safeFetch: authorization-code + refresh, activity list + budget-
  aware per-activity detail (for calories/HR), honouring the 200/15min cap; each
  activity maps to one `Workout` row. Activity name/description stay in metadata
  as data, never surfaced or interpolated.
- Dedup reuses the existing source-agnostic two-layer picker: `STRAVA` slots
  into the workout ladder below the device-native wearables and above
  MANUAL/IMPORT, so an Apple-Watch + Strava twin collapses to one canonical row.
- Poll-cohort hourly sync (:17) + self-converging boot backfill, wired into the
  integration-sync registrar. Webhook deferred.
- OAuth routes (connect/callback/credentials/disconnect/status/test) with signed
  state, cross-account re-auth rejection, and a surgical `www.strava.com`
  connect-src gated to the Strava surfaces.
- Settings card + consolidated status envelope entry; env + compose whitelist;
  i18n across all six locales.
- Garmin: no direct connector is possible (business-partner-only). Add
  docs/integrations/garmin.md documenting the Apple Health / Google Health
  Connect path and Garmin's withheld proprietary scores, cross-linked from both
  platform docs, plus a non-connector info note in the Connections panel.

Regenerate docs/api/openapi.yaml for the new `STRAVA` MeasurementSource value.
2026-07-09 21:25:25 +02:00

2.1 KiB

Garmin (via Apple Health or Google Health Connect)

There is no direct Garmin connector for a self-hosted HealthLog instance, and there cannot be one. Garmin's data-sharing program is business-partner-only: it is approval-gated, requires a public callback endpoint, and is not the "register your own app, paste a key" model the wearable integrations here rely on. So instead of a broken half-integration, HealthLog reads Garmin data the honest way — through the phone health platform your Garmin already feeds.

The path that works

Garmin device  →  Garmin Connect  →  Apple Health (iOS)  ─┐
                                   →  Google Health Connect ├─→  HealthLog
                                      (Android)            ─┘
  • iOS: Garmin Connect writes to Apple Health automatically. Import it with the existing Apple Health path (the export.zip upload or the native client's background sync).
  • Android: since Garmin Connect 5.14.1 (July 2025) the app writes to Google Health Connect. Connect HealthLog through the existing Google Health integration.

No extra code, no Garmin developer account, no approval queue — if your Garmin already syncs to your phone's health platform, HealthLog already reads it.

What comes through

Everything Garmin shares with Apple Health / Health Connect flows in: steps, heart rate (including beat-to-beat variability), sleep stages (REM / deep / light), calories (active and total), distance, weight and body composition, cadence, and — partially — blood-oxygen saturation.

What Garmin withholds

These are Garmin's proprietary scores. Garmin does not share them with Apple Health or Health Connect, so no self-hosted integration — and no amount of configuration — can obtain them. They are only available inside Garmin Connect itself:

  • Body Battery
  • HRV Status
  • VO₂ max
  • Training Load and Training Effect
  • Recovery Time
  • Intensity Minutes
  • Stress score
  • Running dynamics

If you rely on those specific numbers, keep the Garmin Connect app for them; everything else lands in HealthLog through the platform path above.