Strava
Strava uses OAuth 2.0. The typical connect flow for a web client is:- Call
GET /strava/oauth/authorize-urlto get a pre-built authorization URL. - Redirect the athlete to that URL.
- Strava redirects back to
/strava/oauth/token(handled internally). - The connection is stored and the athlete’s activity history is backfilled automatically.
Get Strava authorization URL
Returns a fully-formed Strava OAuth authorization URL for initiating the connect flow from a web client.URL to redirect the user to after OAuth completes. Query parameters
provider, status, provider_user_id, and optionally error are appended automatically. When omitted, the redirect falls back to the legacy nr://redirect deep link.200 OK
The Strava authorization URL. Redirect the user’s browser to this URL to begin the OAuth flow. The URL encodes a CSRF-protected state parameter containing the user’s Firebase UID.
Strava OAuth callback
Handles the OAuth 2.0 authorization code exchange. This endpoint is called automatically by Strava as the redirect URI — you do not need to call it directly. When the exchange succeeds, credentials are stored in Astral’s secure credential store and the user’s integration status is updated. An onboarding backfill job is triggered to import the last 2 months of Strava activity history.| Parameter | Description |
|---|---|
code | Authorization code from Strava |
state | Base64-encoded JSON containing firebase_uid and csrf_token |
error | Set to "access_denied" if the user declined |
return_url (or deep link) with provider=strava&status=success&provider_user_id=<strava_id>.
On failure: Redirects with status=error&error=<reason>.
Disconnect Strava
Revokes the Strava connection for the authenticated user. Calls the Strava deauthorization API, removes stored credentials, and updates integration status to disconnected.200 OK
200 with:
Garmin
Garmin uses OAuth 1.0a. The connect flow is:- Your app initiates the flow by directing the user to the Garmin authorization URL (obtained through your integration setup).
- Garmin redirects to
/garmin/oauth/callbackwith anoauth_tokenandoauth_verifier. - The callback exchanges the verifier for permanent access credentials, stores them, and triggers an onboarding backfill.
Garmin OAuth callback
Handles the OAuth 1.0a access token exchange. Called automatically by Garmin after the user authorizes the connection — you do not call this endpoint directly. Credentials are stored in Astral’s secure credential store. Stale Garmin mappings from other users are automatically cleared to prevent routing conflicts. An onboarding backfill job imports the last 2 months of activity history.| Parameter | Description |
|---|---|
oauth_token | The request token from the authorization step |
oauth_verifier | The verifier from Garmin |
return_url (or deep link) with provider=garmin&status=success&provider_user_id=<garmin_id>.
On failure: Redirects with status=error&error=<reason>.
Disconnect Garmin
Removes the Garmin connection for the authenticated user. Clears stored OAuth credentials and marks the integration as disconnected.200 OK
Apple Health
Apple Health data is synced from the mobile app. The sync endpoint accepts a batch of health metrics exported from HealthKit.Sync Apple Health data
Array of HealthKit workout objects to import.
Array of resting heart rate data points.
Array of HRV measurements.
ISO 8601 date of this sync batch (e.g.
"2025-04-23T00:00:00Z").200 OK
Integration status
The integration connection state for each provider is reflected on the user’s profile under theintegrations object. You can read this via the standard user profile endpoint.
Onboarding backfill
When a user connects Strava or Garmin for the first time, Astral automatically triggers a backfill job that imports up to 2 months of historical activities. Only one provider’s backfill runs when both Strava and Garmin are connected, to avoid duplicate activity imports.Error codes
| Status | Meaning |
|---|---|
400 | Validation error — missing required fields |
401 | Missing or invalid authentication |
403 | OAuth error — user denied authorization |
500 | Internal server error or third-party API failure |