Let runners sign in with their Endorphins account and, with their consent, access their training data. Endorphins is a standard OAuth 2.0 and OpenID Connect (OIDC) provider, so you can use any off-the-shelf OIDC client library.
Sign in to Endorphins, then go to /developer to register an app. Choose your redirect URIs and the scopes you need, and you'll receive a client_id (plus a client_secret for confidential server-side apps, shown once).
Your redirect URI is your own app's callback URL (for example https://your-app.com/api/integrations/endorphins/callback). Endorphins doesn't dictate the path, but the redirect_uri you send on /oauth/authorize must exactly match one you registered — matching is character-for-character with no wildcards. It must use HTTPS (http is allowed only for localhost) and must not contain a URL fragment.
Everything auto-configures from our OpenID Connect discovery document at https://endorphinsrunning.com/.well-known/openid-configuration, which advertises the authorization, token, userinfo, and JWKS endpoints. id_tokens are signed with RS256; verify them against the published JWKS.
Send the user to /oauth/authorize with response_type=code, your client_id and redirect_uri, the scopes you need, a state value, a PKCE code_challenge (S256), and an optional nonce. The user signs in and approves the requested scopes on the Endorphins consent screen, and we redirect back to your redirect_uri with a code. Exchange the code at /oauth/token for an access_token, a refresh_token, and — when the openid scope was requested — a signed id_token.
Decode the id_token, or call /oauth/userinfo with the access token to get the user's sub, email, and name.
All data scopes are read-only. Request only what your app needs.
With a Bearer access token, call the resource endpoints below (same origin as the auth endpoints). Each requires its matching scope; a token missing that scope gets a 403. The developer docs include a full API reference with a sample request and response body for every endpoint.
GET /gpt/me (safe profile fields)GET /gpt/plan, /gpt/today, /gpt/pace, /gpt/goals, /gpt/coach-memory, /gpt/plan-adjustments, /gpt/plan-history, /gpt/heart-rate-zonesGET /gpt/activities, /gpt/activity-detail/:id, /gpt/week-summaries, /gpt/profile-charts, /gpt/training-load, /gpt/personal-recordsGET /gpt/recovery, /gpt/daily-energy, /gpt/injury-statusPOST /gpt/plan-adjustments and POST /gpt/plan-adjustments/:id/resolve create a proposal the athlete must approve in-app; nothing changes on the live plan until they doParameters: /gpt/activities accepts days (default 120, max 365) or sinceDate (ISO 8601); /gpt/recovery and /gpt/daily-energy accept days (default 30, max 365). To read a user's workouts, call GET https://endorphinsrunning.com/gpt/activities?days=30 with an Authorization: Bearer ACCESS_TOKEN header and the read:workouts scope. It returns { "activities": [ ... ] }, newest first, where each activity includes fields such as name, type, start_date (UTC) and start_date_local, distance (meters) plus distance_miles and distance_kilometers, moving_time and elapsed_time (seconds), average_pace_per_mile and average_pace_per_km, average_heartrate, total_elevation_gain (meters), trainingLoad, activitySource, and workoutId (the linked plan workout, if any). Which fields are present depends on the data source (Strava, Garmin, Apple Health). GPS map data and precise location fields are stripped from this endpoint for privacy.
Use grant_type=refresh_token at /oauth/token for a fresh access token (refresh tokens rotate on each use). Tokens can be revoked at /oauth/revoke, and users can revoke your app anytime from their Endorphins account settings. Handle 401 responses by re-authorizing.
Always use PKCE. Never expose a confidential client_secret in a browser or mobile binary; use a public (PKCE) app there. Validate the state and nonce values you sent, and verify the id_token signature, iss, and aud.
Once you're logged in, head to the developer portal at /developer to register your first app.