Skip to main content
Astral integrates with Strava, Garmin Connect, and Apple Health to pull in completed activities automatically. Once a connection is established, new activities are pushed to Astral in near real-time via platform webhooks. All integration endpoints require a valid user bearer token.

Strava

Strava uses OAuth 2.0. The typical connect flow for a web client is:
  1. Call GET /strava/oauth/authorize-url to get a pre-built authorization URL.
  2. Redirect the athlete to that URL.
  3. Strava redirects back to /strava/oauth/token (handled internally).
  4. 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.
GET /api/v1/strava/oauth/authorize-url
Authentication: Bearer token
return_url
string
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.
curl "https://api.astral.run/api/v1/strava/oauth/authorize-url?return_url=https%3A%2F%2Fapp.astral.run%2Fsettings%2Fconnections" \
  -H "Authorization: Bearer <token>"
Response 200 OK
{
  "status": "ok",
  "data": {
    "authorize_url": "https://www.strava.com/oauth/authorize?client_id=12345&redirect_uri=https%3A%2F%2Fapi.astral.run%2Fapi%2Fv1%2Fstrava%2Foauth%2Ftoken&response_type=code&approval_prompt=auto&scope=activity%3Awrite%2Cactivity%3Aread_all%2Cprofile%3Aread_all%2Cprofile%3Awrite&state=..."
  }
}
data.authorize_url
string
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.
GET /api/v1/strava/oauth/token
Query parameters (Strava-supplied)
ParameterDescription
codeAuthorization code from Strava
stateBase64-encoded JSON containing firebase_uid and csrf_token
errorSet to "access_denied" if the user declined
On success: Redirects to the 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.
POST /api/v1/strava/deauth
Authentication: Bearer token
curl -X POST https://api.astral.run/api/v1/strava/deauth \
  -H "Authorization: Bearer <token>"
Response 200 OK
{
  "status": "ok",
  "message": "Successfully disconnected Strava account"
}
If the user has no Strava connection, the endpoint returns 200 with:
{
  "status": "ok",
  "message": "No Strava connection found for this user"
}

Garmin

Garmin uses OAuth 1.0a. The connect flow is:
  1. Your app initiates the flow by directing the user to the Garmin authorization URL (obtained through your integration setup).
  2. Garmin redirects to /garmin/oauth/callback with an oauth_token and oauth_verifier.
  3. 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.
GET /api/v1/garmin/oauth/callback
Query parameters (Garmin-supplied)
ParameterDescription
oauth_tokenThe request token from the authorization step
oauth_verifierThe verifier from Garmin
On success: Redirects to 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.
DELETE /api/v1/garmin/deauth
Authentication: Bearer token
curl -X DELETE https://api.astral.run/api/v1/garmin/deauth \
  -H "Authorization: Bearer <token>"
Response 200 OK
{
  "status": "ok",
  "message": "Successfully disconnected Garmin account"
}

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

POST /api/v1/apple-health/sync
Authentication: Bearer token
workouts
array
Array of HealthKit workout objects to import.
heart_rate_samples
array
Array of resting heart rate data points.
hrv_samples
array
Array of HRV measurements.
sync_date
string
ISO 8601 date of this sync batch (e.g. "2025-04-23T00:00:00Z").
curl -X POST https://api.astral.run/api/v1/apple-health/sync \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "workouts": [
      {
        "workout_type": "running",
        "start_date": "2025-04-22T07:00:00Z",
        "end_date": "2025-04-22T07:55:00Z",
        "total_distance": 12400,
        "total_energy_burned": 740,
        "heart_rate_avg": 152
      }
    ],
    "heart_rate_samples": [
      { "date": "2025-04-22T06:00:00Z", "value": 48 }
    ],
    "sync_date": "2025-04-23T00:00:00Z"
  }'
Response 200 OK
{
  "status": "ok",
  "message": "Apple Health data synced successfully",
  "imported_workouts": 1
}

Integration status

The integration connection state for each provider is reflected on the user’s profile under the integrations object. You can read this via the standard user profile endpoint.
{
  "integrations": {
    "strava": {
      "connected": true,
      "athlete_id": "12345678",
      "last_sync_at": "2025-04-23T08:15:00Z"
    },
    "garmin": {
      "connected": true,
      "user_id": "garmin_user_9876",
      "last_sync_at": "2025-04-22T20:30:00Z"
    }
  }
}

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

StatusMeaning
400Validation error — missing required fields
401Missing or invalid authentication
403OAuth error — user denied authorization
500Internal server error or third-party API failure