> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getbased.health/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect wearables and track biometrics

> Sync supported wearable providers, import Apple Health, enable WHOOP or Google Health when self-hosting, or log readings manually.

The Body lens pulls data from your connected devices into a unified biometrics workspace. HRV, resting heart rate, sleep score, readiness, steps, weight, blood pressure, SpO₂, and more can appear in the Body lens and in the **Biometrics Overview** dashboard widget. If you do not own a wearable, you can log weight, blood pressure, and resting heart rate manually.

In **Settings → Wearables**, integrations are grouped as **Connected**, **Ready to connect**, **For self-hosted**, and **Add without a connection**, so unavailable hosted integrations are not presented as ordinary connect buttons.

## Body lens and Biometrics Overview

Each selected metric appears as a card in **Body** and in the dashboard's **Biometrics Overview** widget. Tap any card to open a 90-day chart with statistics and a full reading history. Cards for metrics your device does not measure are hidden automatically.

Metrics surfaced on the strip include:

* HRV (RMSSD), resting heart rate, sleep score, readiness
* Activity and steps
* Weight, blood pressure, SpO₂, body-temperature delta
* Body composition (body fat %, fat mass, muscle mass, bone mass, lean mass, water, visceral fat)
* Vascular health (pulse wave velocity, vascular age, cardio fitness)
* Sleep architecture (deep, light, REM, awake durations; average HR; breathing rate; snoring; apnea-class disturbance)

## Supported vendors

| Vendor | Auth | Notes |
| - | - | - |
| **Oura** | OAuth 2.0 | Requires an Oura account and ring. |
| **Withings** | OAuth 2.0 | Surfaces every metric your Withings hardware produces. Cards for unsupported metrics are hidden automatically. |
| **Polar** | OAuth 2.0 | Sync your device to Polar Flow before connecting — Polar's API returns no data until the device has uploaded. HRV is workout-only (chest strap), not overnight. |
| **Google Health** | Google OAuth 2.0 | Self-host only. Uses read-only access for supported Fitbit, Pixel Watch, and other data in the connected Google account. The official hosted app does not enable it. |
| **Fitbit (legacy)** | Fitbit OAuth 2.0 (PKCE) | Existing connections only. They can continue syncing while their authorization remains valid and the legacy API is available, but new connections and legacy reconnection are not offered. |
| **Apple Health** | File import | Export from the iPhone Health app and drop the `.zip` file. No OAuth, no server contact. |
| **WHOOP** | OAuth 2.0 | Supported on configured self-hosted deployments only. Requires the operator's WHOOP developer app, client ID, client secret, explicit enable flag, and compliance with WHOOP's developer requirements. The official hosted app does not enable it. |
| **Ultrahuman** | Experimental OAuth 2.0 | Self-host only. Requires the operator's Ultrahuman developer app, client ID, client secret, and explicit enable flag. Hidden on unconfigured hosted deployments. |
| **Manual** | None | Built-in. Log weight, blood pressure, and resting HR directly on the dashboard without a device. |

On the official app, Oura, Withings, Polar, and an existing legacy Fitbit connection use getbased's dedicated compatibility relay for OAuth exchanges and provider fetches. Google Health, WHOOP, and Ultrahuman are self-hosted only and never fall back to the official relay.

## How to connect a wearable

<Steps>
  <Step title="Open Settings">
    Click the gear icon to open **Settings**, then select **Wearables**.
  </Step>

  <Step title="Click Connect">
    Find the vendor row you want and click **Connect**.
  </Step>

  <Step title="Approve the hosted relay when applicable">
    Before an official hosted connection sends anything, review the provider-specific relay notice and approve it. If you decline, getbased sends nothing.
  </Step>

  <Step title="Authorize on the vendor's site">
    For OAuth-based vendors, you are redirected to the vendor's authorization page. Approve access and you are redirected back automatically.
  </Step>

  <Step title="Wait for the initial sync">
    getbased fetches the last 90 days of data. Raw daily rows are stored in your browser's local database; a compact summary populates the Body lens and Biometrics Overview widget.
  </Step>
</Steps>

The hosted relay notice explains that getbased s.r.o. forwards account credentials and requested health responses for that provider. The relay can read them transiently to complete the request but does not intentionally log or persist them. Approval is scoped to the current browser profile and provider. Disconnecting withdraws that approval, removes the local connection and its imported provider data, and leaves any separate provider-side authorization for you to revoke in the provider account.

<Warning>
  Google says the legacy Fitbit Web API will stop syncing in September 2026, but has not published an exact shutdown day. Existing getbased connections are retained as a temporary bridge; do not assume they will work through September 30. Google Health is the replacement only on self-hosted deployments configured with their own Google Cloud OAuth project. See [Google's migration notice](https://developers.google.com/health/about).
</Warning>

## WHOOP on a self-hosted deployment

WHOOP is a first-class self-host integration, not a hosted getbased connection. Your deployment owner registers and operates the WHOOP developer application, and your deployment's same-origin `/api/proxy` handles confidential token exchange, refresh, and WHOOP API requests. No WHOOP request falls back to getbased's operated compatibility relay.

Before authorization, getbased shows the requested read access and explains the data path, local storage, optional encrypted Sync and AI context, disconnection, and provider-side revocation. The requested scopes are:

* `read:profile`
* `read:cycles`
* `read:recovery`
* `read:sleep`
* `read:workout`
* `offline`

The current integration reads WHOOP's v2 profile, cycle, recovery, and sleep resources. It joins recovery records to their v2 cycle and sleep IDs so recovery, HRV, resting heart rate, readiness, sleep performance, strain, and average heart rate are attributed to the physiological day rather than the later processing timestamp.

WHOOP credentials, imported daily rows, and WHOOP-specific connection/derived profile values are AES-GCM encrypted with this browser's non-exportable device key. Raw WHOOP rows, tokens, and the connection record are excluded from Sync and backups. Allowlisted compact derived surfaces, such as the wearable summary and source preferences, can join end-to-end-encrypted profile Sync or the AI/Agent context you enable.

**Disconnect** deletes this browser's WHOOP tokens, imported rows, derived WHOOP source data, and local connection approval. It does not automatically revoke the operator's application at WHOOP; revoke that access in your WHOOP account as a separate step when you want provider-side access to stop.

<Note>
  Each self-host operator is the WHOOP application owner and is responsible for app approval, user support, consent/privacy disclosures, retention, security, and the current [WHOOP API Terms of Use](https://developer.whoop.com/api-terms-of-use/). See [Self-hosting](/guides/self-hosting#whoop-developer-program-and-operator-responsibility).
</Note>

## Apple Health setup

Apple Health uses a file export instead of OAuth.

<Steps>
  <Step title="Export from your iPhone">
    Open the **Health** app → tap your profile photo (top right) → **Export All Health Data**.
  </Step>

  <Step title="Transfer the file">
    AirDrop or email the `export.zip` to your computer.
  </Step>

  <Step title="Import into getbased">
    In **Settings → Wearables**, drop the `.zip` file onto the Apple Health row.
  </Step>
</Steps>

<Note>
  Parsing runs entirely in your browser. The file is never sent to a server. Large exports (multi-year history) can take 30–60 seconds to parse.
</Note>

## Manual entry

You can log weight, blood pressure, and resting heart rate without any wearable. The Body lens and Biometrics Overview widget show empty cards for these metrics when no device provides them.

**To log a reading:**

<Steps>
  <Step title="Tap the empty card">
    Tap the card (for example, the `Weight –` card with a `+ Log` affordance at the bottom).
  </Step>

  <Step title="Enter the value">
    Type the number into the inline input — weight in your configured kg or lb display unit, blood pressure as systolic/diastolic with optional pulse, resting HR in bpm. Weight is normalized to kg internally, so changing display units does not change the underlying reading.
  </Step>

  <Step title="Add context (optional)">
    Tap a context chip — **resting**, **morning-fasted**, **post-workout**, or **stress** — to help the AI interpret the reading correctly.
  </Step>

  <Step title="Save">
    Press **Enter** or tap **Save**. The card updates immediately with a *via Manual* badge.
  </Step>
</Steps>

**To edit or delete past readings:** tap any card to open its detail modal, then scroll past the chart to the **Manual entries** list. Each row has a **×** button to delete it. To correct a value, delete the old reading and log the correct one with the **+ Add reading** button (which accepts backfilled dates).

**To delete all manual entries:** go to **Settings → Wearables**, expand the **Manual** row, and click **Delete all manual entries**. Wearable data from Oura, Withings, and other connected vendors is not affected.

## Multi-vendor metric ownership

When two vendors report the same metric — for example, Oura HRV and Fitbit HRV — getbased displays the most recent non-null value by default and shows a *via {vendor}* badge on the card.

To switch sources:

1. Tap the *via {vendor}* badge on a card.
2. Select the source you want from the picker.

Your choice is saved per metric, per profile, and syncs across your devices. Vendors that are the sole provider of a metric (for example, only Withings measures weight) do not show a badge.

## Sync controls

Each current supported vendor row in **Settings → Wearables** normally has two sync actions:

* **Update now** — refetches the last 7 days. Use this when you've just synced your device and want the dashboard to update immediately.
* **Import last 90 days** — refetches the full 90-day window. Use this after returning from a trip, switching devices, or noticing a gap in the chart. This is slower; some vendors apply rate limits that add 30 seconds or more to the request.

Background sync runs automatically every 6 hours while the tab is open, so the dashboard typically stays current on its own.

Legacy Fitbit connections remain eligible for background sync while their current authorization works, but their row offers migration and disconnection instead of a legacy reconnect flow. If Fitbit rejects the token, that connection cannot be reauthorized in getbased.

<Tip>
  An "as of {date}" hint on a card means that metric's most recent reading is older than other data from the same vendor. This is usually the vendor's processing pipeline finishing late — Oura's HRV often lags its sleep score by several hours. Hover the hint for an explanation.
</Tip>

## Choosing dashboard metrics

Use **Add metrics** in the Biometrics Overview widget to choose which wearable and manual metrics appear on the dashboard. The full Body lens remains the dedicated workspace for sources, sync state, and metric history.

## Privacy

* **Provider requests follow the connection boundary shown in Settings.** On the official app, Oura, Withings, Polar, and legacy Fitbit credentials and responses pass transiently through the dedicated compatibility relay after consent. Self-hosted providers use the operator's same-origin endpoint. Apple Health parsing remains entirely in the browser.
* **Raw daily rows stay device-local after import.** WHOOP and Google Health rows are always browser-device encrypted. WHOOP-specific connection metadata and derived profile values use the same device-only boundary. Other provider rows use passphrase encryption when **Settings → Data → Security** protection is enabled; without it, they remain readable inside the browser profile.
* **Compact summary data** syncs to your other devices via Evolu CRDT, which is end-to-end encrypted using your mnemonic identity.
* **OAuth refresh tokens stay local and encrypted.** They use the browser's per-profile device vault and are excluded from sync and backups, so you reconnect each vendor on each device.
* **AI chat context** includes a compact wearable summary by default when **Insight → Manage → Context → Data sources → Body → Wearable recovery context** is on. Turn that source off to keep wearables out of in-app chat and Body-related score modifiers.
* **Personal Agent (MCP)** is controlled separately in **Settings → Agent Access**. When Agent Access is enabled, the agent receives the compact wearable summary; you can optionally push a daily time series there for richer time-series reasoning.

## Self-hosting OAuth

If you self-host getbased, register your own OAuth app with each provider and set the corresponding environment variables. Official hosted credentials and redirect registrations are not reusable on a custom hostname.

| Variable | Provider | Notes |
| - | - | - |
| `OURA_CLIENT_ID` | Oura | Also requires `OURA_CLIENT_SECRET` in `.env.local`. |
| `WITHINGS_CLIENT_ID` | Withings | Also requires `WITHINGS_CLIENT_SECRET`. |
| `POLAR_CLIENT_ID` | Polar | Also requires `POLAR_CLIENT_SECRET`. |
| `ULTRAHUMAN_ENABLED` | Ultrahuman | Must be exactly `true`; experimental self-host integration. |
| `ULTRAHUMAN_CLIENT_ID` | Ultrahuman | Also requires `ULTRAHUMAN_CLIENT_SECRET`. |
| `ULTRAHUMAN_CLIENT_SECRET` | Ultrahuman | Keep server-side. |
| `WHOOP_ENABLED` | WHOOP | Must be exactly `true`; enables the supported self-host integration. |
| `WHOOP_CLIENT_ID` | WHOOP | Also requires `WHOOP_CLIENT_SECRET`; WHOOP uses a confidential OAuth client. |
| `WHOOP_CLIENT_SECRET` | WHOOP | Keep server-side. |
| `GOOGLE_HEALTH_ENABLED` | Google Health | Must be exactly `true`. |
| `GOOGLE_HEALTH_CLIENT_ID` | Google Health | Requires your deployment's confidential Web OAuth client. |
| `GOOGLE_HEALTH_CLIENT_SECRET` | Google Health | Keep server-side; never expose it in browser code or public docs. |

Register your actual redirect URI (for example, `http://localhost:8000/app` for local dev and your production hostname) in each provider's developer portal. For a custom hostname that is not already listed, add the same URI to the provider's `oauth.redirectUris` entry in `js/wearable-adapters.ts` in your fork. Apple Health is file-import only and requires no credentials on any install.

<Note>
  When provider variables are set, the app picks them up at startup and uses your `client_id` for authorization and token exchange. Google Health, WHOOP, and Ultrahuman stay disabled unless their `*_ENABLED=true` flag and both OAuth credential variables are present. The browser receives only public client IDs and availability flags; secrets stay behind `/api/proxy`.
</Note>

On `localhost`, disabled self-host setup rows remain visible so an operator can discover the required variables. On other unconfigured hosted domains, WHOOP and Ultrahuman are hidden. A complete enabled configuration exposes **Connect**. If an operator later disables either provider, existing connections remain removable but sync and reconnect pause.

Google Health requests three restricted, read-only scopes: activity and fitness, health metrics and measurements, and sleep. The operator of each self-hosted OAuth project is responsible for Google's consent-screen, verification, user-limit, and any security-assessment requirements. Personal testing can use Google's testing mode; a public rollout may require verification and an annual Cloud App Security Assessment. Follow the [Google Health setup guide](https://developers.google.com/health/setup) for the current requirements.

## Troubleshooting

<AccordionGroup>
  <Accordion title="'Polar connected — waiting on first device sync'">
    The OAuth handshake succeeded, but Polar's API uses a transactions model that returns no data until your device has uploaded a sync to Polar Flow. Open the Polar app on your phone, sync your watch, then click **Update now** in getbased.
  </Accordion>

  <Accordion title="'Needs reconnection' badge on a vendor row">
    The OAuth refresh token expired or was revoked. Click **Reconnect** on a current supported vendor row to re-authorize. Legacy Fitbit is the exception: its old authorization cannot be reconnected in getbased.
  </Accordion>

  <Accordion title="Google Health says 'self-host only' or 'host access unavailable'">
    The official hosted app intentionally does not provide Google Health OAuth. On a self-hosted deployment, confirm that `GOOGLE_HEALTH_ENABLED=true`, `GOOGLE_HEALTH_CLIENT_ID`, and `GOOGLE_HEALTH_CLIENT_SECRET` are all present on the server, then restart the deployment. Existing Google Health credentials remain removable when host access is unavailable, but sync and reconnect stay paused.
  </Accordion>

  <Accordion title="A vendor doesn't appear when picking a metric source">
    The vendor doesn't expose that metric through their API. For example, WHOOP does not provide weight data, and Withings does not provide HRV (RMSSD). Only vendors that actually report a given metric appear in the source picker for that metric.
  </Accordion>

  <Accordion title="Apple Health import is slow">
    Large Apple Health exports can be 100 MB or more for multi-year history. Parsing runs entirely in your browser — no server is involved — so expect 30–60 seconds for large files.
  </Accordion>

  <Accordion title="WHOOP is not in the connect list or returns host access unavailable">
    WHOOP is self-host only and hidden on an unconfigured hosted domain. Confirm that `WHOOP_ENABLED=true`, `WHOOP_CLIENT_ID`, and `WHOOP_CLIENT_SECRET` are all present on the server, that the redirect URI matches both WHOOP and `js/wearable-adapters.ts`, then restart the deployment. `localhost` shows the disabled setup row before configuration. An existing connection remains removable when host access is unavailable, but sync and reconnect pause.
  </Accordion>

  <Accordion title="Ultrahuman is not in the connect list">
    Ultrahuman remains an experimental self-host integration and is hidden on an unconfigured hosted domain. Set `ULTRAHUMAN_ENABLED=true` with `ULTRAHUMAN_CLIENT_ID` and `ULTRAHUMAN_CLIENT_SECRET`, then restart the deployment. `localhost` shows the disabled setup row before configuration.
  </Accordion>
</AccordionGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.