---
title: Integrations
subtitle: Your users connect a provider account once; Sahha syncs their data continuously — server-side, no SDK to ship.
---


## Introduction

Integrations pull health data straight from a provider's cloud — **Garmin**, **Oura**, **WHOOP** — into Sahha, with **no SDK on the device**. The user connects their provider account once; Sahha syncs their data continuously onto the same profile. It's the third way to get data into Sahha, alongside the [SDK](/docs/connect/sdk) and [REST API](/docs/connect/api).

{% callout title="Public beta" type="info" %}
Integrations are in public beta and are **Sandbox only**. We're still refining the API surface as it matures and will call out any breaking changes here as they land.
{% /callout %}

---

## Supported providers

{% providers view="list" /%}

The **slug** is the value you pass as `{provider}` throughout the API (see [Connect a user](#connect-a-user)).

{% callout title="Need another provider?" %}
Request one in the [Slack Community](https://join.slack.com/t/sahhacommunity/shared_invite/zt-1w0fmfbvk-qUwQ83tJgXyjT9XSxJvKIw) — tell us the provider and the data you need. Demand drives our priority.
{% /callout %}

---

## Data coverage

What you get depends on the provider, the user's device, and the scopes you enable. Sahha normalizes every provider into the same [data logs](/docs/products/logs), [biomarkers](/docs/products/biomarkers), [scores](/docs/products/scores) and [archetypes](/docs/products/archetypes) — so once data is in, you consume it the same way regardless of source.

The matrix below is **indicative**. See each provider's page for the authoritative, scope-by-scope list.

{% providers view="coverage" /%}

{% callout title="Scores are computed from whatever you collect" %}
Sahha [Scores](/docs/products/scores) (Sleep, Activity, Readiness, Wellbeing, Mental) are derived from the underlying data — so a connected provider contributes to scores automatically, even if it doesn't expose a matching raw metric. More data sources generally mean more complete scores.
{% /callout %}

---

## Data freshness & history

Every provider is **webhook-driven**: the provider notifies Sahha over webhooks as new data is recorded, so data usually lands within minutes of the user's device syncing to the provider's cloud. Sahha does not poll on a fixed schedule.

When a user first connects, Sahha **backfills the last 30 days** of available history, then syncs continuously from then on.

{% providers view="sync" /%}

{% callout title="Latency depends on the device" type="info" %}
End-to-end freshness is bounded by when the user's device syncs to the provider — e.g. an Oura ring that syncs once each morning delivers the night's data at that point, not in real time. Sahha forwards it within minutes of receiving it.
{% /callout %}

{% callout title="Garmin historical permission" %}
If a Garmin user does not grant historical-data permission during authorization, the 30-day backfill is skipped and only data recorded **after** connection flows in. Going-forward sync is unaffected.
{% /callout %}

---

## How it works

Integrations use three concepts. The same vocabulary appears in the dashboard and the API:

| Term            | What it is                                                                                                      |
| --------------- | --------------------------------------------------------------------------------------------------------------- |
| **Provider**    | A data source you can connect — Garmin, Oura, WHOOP.                                                            |
| **Integration** | _Your_ configured app for a provider — the OAuth credentials and scopes you set up once in the Sahha Dashboard. |
| **Connection**  | _One end-user's_ authorized link to a provider, created when they complete the OAuth flow.                      |

Setting up a provider is three steps:

1. **Create an app with the provider** — register in the provider's developer console to obtain OAuth credentials.
2. **Create an integration in Sahha** — add those credentials to your project in the [Sahha Dashboard](https://app.sahha.ai/dashboard).
3. **Connect an end user** — start the OAuth flow for a user via the Sahha API or SDK. Once they authorize, Sahha begins syncing automatically.

Follow the provider-specific guide above for exact console steps and scopes.

---

## Connect a user

Once an integration is configured, you trigger the connection flow per end user. There are two ways: the **API** (server-driven or web flows) and the **SDK** (native mobile).

### Before you start

You need:

- A configured **integration** for the provider (the three steps above).
- A **profile token** for the end user — this scopes the connection to that specific user. See [Authentication](/docs/connect/api#authentication) for how tokens work and [Getting Started](/docs/connect/api#getting-started) to obtain one.
- The end user to have an account with the provider (e.g. a Garmin Connect or Oura account).

Examples use the **sandbox** environment (`sandbox-api.sahha.ai`). Swap to `api.sahha.ai` for production — see [Environments](/docs/connect/api#environments).

### Option A — Sahha API

The connection is a standard OAuth redirect round-trip:

1. **You** call Sahha's `authorize` endpoint with the user's profile token. Sahha returns an `authorizationUrl`.
2. **You** redirect the user's browser to that `authorizationUrl` (the provider's consent screen).
3. **The user** approves access on the provider's site.
4. **The provider** redirects back to Sahha's callback; Sahha stores the connection and starts syncing.
5. **Sahha** redirects the user to your `redirectUrl` so your app can show a success state.

The optional `redirectUrl` must match an entry in the integration's Redirect URL Whitelist; if omitted, Sahha uses the first whitelisted URL.

```shell-session {% title="Start a connection" %}
curl -X GET "https://sandbox-api.sahha.ai/api/v1/integration/oauth/{provider}/authorize?redirectUrl=https://yourapp.com/integrations/callback" \
  -H "Authorization: profile {your-profile-token}"
```

```json {% title="Response" %}
{
	"authorizationUrl": "https://connect.garmin.com/oauthConfirm?..."
}
```

Replace `{provider}` with the provider's slug from the [Supported providers](#supported-providers) table (e.g. `garmin`). Each provider's page has the full request and language-specific snippets.

Once a user is connected, you read their data the same way as any other Sahha data — via the [REST API](/docs/connect/api) or [Webhooks](/docs/connect/webhooks). There's nothing integration-specific about consuming it.

### Handling the redirect

After the OAuth flow, Sahha redirects the user to your `redirectUrl` with query parameters describing the outcome — handle them however your app needs.

**Success**

| Parameter        | Reason                                                  |
| ---------------- | ------------------------------------------------------- |
| `status=success` | Authorization completed and the connection was created. |

**Errors**

Error redirects include `status=error` and an `error` message. This covers a user **denying access** on the provider's consent screen, which is returned as `provider denied authorization`:

| `error` value                                     | Reason                                                                                                                             |
| ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `provider denied authorization: <provider_error>` | The user denied consent, or the provider returned an error. `<provider_error>` is the provider's own value (e.g. `access_denied`). |
| `authorization code is missing`                   | The provider redirected back without an authorization code.                                                                        |
| `integration not found`                           | No integration is configured for this account and provider.                                                                        |
| `provider not supported`                          | The provider is not supported.                                                                                                     |
| `failed to process integration credentials`       | The stored client credentials could not be processed.                                                                              |
| `failed to exchange authorization code`           | Exchanging the authorization code for a token failed.                                                                              |
| `failed to resolve provider user identity`        | The provider user could not be identified from the token.                                                                          |
| `failed to store provider user`                   | Persisting the provider user failed.                                                                                               |
| `failed to create connection`                     | Creating the connection record failed.                                                                                             |

{% callout type="info" %}
Parameter values are URL-encoded — e.g. `error=authorization code is missing` comes back as `error=authorization+code+is+missing`.
{% /callout %}

{% callout type="info" %}
The user has **10 minutes** to authorize after the `authorizationUrl` is generated; after that the session expires and they'll need a fresh `authorizationUrl` to try again. If the `state` parameter is missing, expired, or already used, the callback responds with HTTP `400 Bad Request` instead of redirecting — there's no trusted `redirectUrl` to return to in that case.
{% /callout %}

### Option B — Sahha SDK

{% callout title="Coming soon" %}
SDK support for Integrations is in development. The connection flow will be initiated from the native SDK; the API above is available today.
{% /callout %}

---

## Managing connections

A connection moves through a simple lifecycle: **connected → (token expires) needs re-auth → disconnected**. You can see and manage every connection in the [Sahha Dashboard](https://app.sahha.ai/dashboard) under **Integrations → Connections**.

### Deleting a connection

Deleting a connection revokes a single end-user's link to a provider — Sahha stops ingesting their data immediately. A connection can be deleted in two ways:

- **You**, from the [Sahha Dashboard](https://app.sahha.ai/dashboard) under **Integrations → Connections**.
- **The user**, from their own provider account settings.

### Deleting an integration

Deleting an integration removes your configured app for a provider — its OAuth credentials and scopes. Do this from the [Sahha Dashboard](https://app.sahha.ai/dashboard) under **Integrations → Integrations**.

{% callout title="This deletes every connection" %}
Deleting an integration deletes **every** connection created under it, so their data stops syncing. If you recreate an integration with the same credentials, those users will need to reauthenticate to reconnect.
{% /callout %}

---

## Troubleshooting

{% faq %}

{% faq-item question="A user connected but no data is arriving" %}
A successful connection can still fail to deliver data for a few reasons:

- **The user denied certain scopes.** They approved the connection but declined specific data permissions during authorization, so those categories never sync.
- **The webhook URL is misconfigured.** Check that the webhook URL from your Sahha integration is set correctly in the provider's developer console — see the provider's page.
- **A push/pull provider isn't set to push.** Providers that offer both push and pull delivery (e.g. Garmin) must have their endpoints set to **push** — pull-mode endpoints deliver nothing.
- **Rate limiting is throttling delivery.** Hitting the provider's rate limits for your app can delay or drop data; request higher limits from the provider if you need them. (Sahha will add alerts for excessive rate limiting in a future release.)
{% /faq-item %}

{% faq-item question="The OAuth flow fails immediately" %}
The most common cause is a redirect URL mismatch. Make sure the Sahha callback URL is registered in your provider app's allowed redirect URIs, and that any `redirectUrl` you pass matches an entry in your integration's Redirect URL Whitelist.
{% /faq-item %}

{% faq-item question="How far back does data go when a user first connects?" %}
The historical backfill window depends on the provider. See the provider page for specifics; new data flows continuously once connected.
{% /faq-item %}

{% /faq %}

---

## Support

Need help? Contact [support@sahha.ai](mailto:support@sahha.ai) or join the [Slack Community](https://join.slack.com/t/sahhacommunity/shared_invite/zt-1w0fmfbvk-qUwQ83tJgXyjT9XSxJvKIw).
</content>
</invoke>
