Skip to content

Connecting Users

HealthSync uses a secure, link-based OAuth connect flow to establish provider connections for your users. Your backend creates a one-time launch link, your user opens it in a browser, approves the provider, and data starts flowing.

The connect flow is designed so that:

  • Your backend never handles OAuth tokens — HealthSync manages them.
  • The user’s browser session is short-lived and single-use.
  • PKCE is supported for providers that require it (e.g., Fitbit).
  • Your user is redirected back to your app with a success or error status, plus your optional opaque nonce when supplied.
  1. Create a connect link

    Your backend calls HealthSync API with the user’s identity and a redirect URI:

    Terminal window
    curl -X POST https://healthsync.example/api/connect-links \
    -H "Content-Type: application/json" \
    -d '{
    "apiKey": "your-tenant-api-key",
    "externalUserRef": "your-user-id-123",
    "redirectUri": "https://yourapp.com/connect/result",
    "nonce": "optional-opaque-value"
    }'

    Response:

    {
    "launchUrl": "https://healthsync.example/api/connect/launch?token=abc123...",
    "expiresAt": "2026-03-20T01:00:00.000Z"
    }

    The launchUrl is a one-time link. It expires after a short window. nonce is optional, must be a non-empty string up to 128 characters, and is echoed unchanged on the final redirect. If your app already knows which tracker the user chose, include "provider": "fitbit" (or another provider ID) in the create-link request to skip the provider picker.

    Skip provider selection

    When your app has already collected the user’s tracker choice, pass that provider ID when creating the link:

    Terminal window
    curl -X POST https://healthsync.example/api/connect-links \
    -H "Content-Type: application/json" \
    -d '{
    "apiKey": "your-tenant-api-key",
    "externalUserRef": "your-user-id-123",
    "redirectUri": "https://yourapp.com/connect/result",
    "provider": "fitbit"
    }'

    The returned launchUrl still redeems only once. Opening it skips the HealthSync provider list and redirects the browser straight to the selected provider’s OAuth authorization page.

  2. User opens the launch URL

    Redirect your user’s browser to the launchUrl. HealthSync:

    • Validates the one-time token.
    • For provider-picker links, sets a secure, HttpOnly browser session cookie.
    • Redirects either to the connect UI where the user picks a provider, or directly to the predefined provider’s OAuth authorization URL.
  3. User selects a provider

    For provider-picker links, the connect UI shows available providers (e.g., Fitbit, Garmin, Oura). Your tenant’s privacy policy URL is displayed for transparency. Predefined-provider links skip this step.

    Behind the scenes, HealthSync:

    • Creates a pending connection attempt with an OAuth state token.
    • For PKCE providers, generates a code_verifier server-side (never exposed to the browser).
    • Returns the provider’s authorization URL.
  4. User approves on the provider

    The user is redirected to the provider’s OAuth consent screen (e.g., Fitbit’s “Allow access to your data” page). After approval, the provider redirects back to HealthSync’s callback endpoint.

  5. HealthSync completes the connection

    The callback handler:

    • Verifies the OAuth state token.
    • Exchanges the authorization code for tokens (with PKCE code_verifier if applicable).
    • Encrypts and stores the tokens.
    • Creates or updates the connection and binds it to your user.
    • Redirects the user to your redirectUri with result parameters.

    Success redirect:

    https://yourapp.com/connect/result?status=success&connection_id=conn_abc123&nonce=optional-opaque-value

    Error redirect:

    https://yourapp.com/connect/result?status=error&error_code=access_denied&nonce=optional-opaque-value

    The nonce parameter is omitted when no nonce was supplied on connect-link creation.

The externalUserRef is your user identifier — whatever ID you use in your system. HealthSync maps it to an internal tenant_user_id, and outbound webhooks echo your value as external_user_ref alongside the internal user_id.

nonce is an optional opaque correlation value for the connect attempt. HealthSync stores it only on that attempt and echoes it unchanged as nonce on the final success or error redirect. Do not put secrets in it.

  • A connection is a link between HealthSync and a provider account (e.g., “Fitbit user ABC”).
  • A binding ties a connection to one of your users within your tenant.
  • One provider connection can be bound to multiple users (rare but supported).

If a provider user has already connected via another tenant, HealthSync reuses the same connection. Your user gets a new binding to the existing connection. Token management is shared — HealthSync handles refresh and revocation centrally.

Once connected, data flows automatically:

  1. The provider sends change notifications to HealthSync.
  2. HealthSync fetches the updated data using the stored OAuth tokens.
  3. Data is normalized into the canonical event format.
  4. Events are delivered to your webhook subscriptions.

Before data can be delivered, register a webhook subscription for the event types you want:

Terminal window
curl -X POST https://healthsync.example/api/subscriptions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-tenant-api-key" \
-d '{
"tenantId": "your-tenant-id",
"eventType": "daily_summary",
"endpointUrl": "https://yourapp.com/webhooks/fitness"
}'

You can subscribe to any event type — one subscription per event type per endpoint.

To stop receiving a given event type, remove the webhook subscription:

Terminal window
curl -X DELETE https://healthsync.example/api/admin/subscriptions/subscription-uuid \
-H "CF-Access-JWT-Assertion: <access-jwt>"

This disables event delivery for that subscription.

| Scenario | Behavior | |----------|----------| | Launch token expired or reused | 403 on launch, user must request a new link. | | User cancels on provider | Redirected to your redirectUri with status=error. | | Token exchange fails | Redirected with status=error. Connection attempt marked failed. | | Provider connection already exists | Reused. New binding created for your user. |