PeerTier Partner API

For training platforms that send their riders' finished rides to PeerTier. A rider connects your app once with OAuth 2.0. After that you push each finished ride as a FIT file.

1. Get access

Email hello@peertier.app with your app name and your https redirect URI(s). You receive a client_id and a client_secret. The secret is shown once: keep it on your servers, never in an app or a browser.

2. Connect a rider

Send the rider to:

GET https://peertier.app/oauth/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https://your.app/callback
  &scope=rides:write
  &state=RANDOM_VALUE
  &code_challenge=PKCE_CHALLENGE&code_challenge_method=S256   (recommended)

The rider signs in to PeerTier and allows or cancels. PeerTier then sends the rider back to your redirect URI:

allowed:    https://your.app/callback?code=CODE&state=RANDOM_VALUE
cancelled:  https://your.app/callback?error=access_denied&state=RANDOM_VALUE

Check that state is the value you sent. The code works once and only for a few minutes.

3. Get tokens

POST https://peertier.app/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&client_id=…&client_secret=…
&redirect_uri=https://your.app/callback&code=CODE&code_verifier=PKCE_VERIFIER
{"token_type": "Bearer", "expires_in": 21600, "access_token": "…", "refresh_token": "…"}

An access token lasts 6 hours. Get a new one with the refresh token:

grant_type=refresh_token&refresh_token=…&client_id=…&client_secret=…

Every refresh returns a new refresh token and the old one stops working: store the new one each time. A refresh token expires after 180 days.

4. Push a finished ride

POST https://peertier.app/api/partner/v1/rides
Authorization: Bearer ACCESS_TOKEN
Accept: application/json
Content-Type: multipart/form-data

file         the ride as a FIT file (max 25 MB)
external_id  your own id for this ride: letters, digits, . _ : -  (max 100)

Send each ride once it is finished. Sending the same external_id again is safe: it never makes a second ride.

201 imported{"status": "imported", "ride_id": 123} The ride is stored. If the rider already had an empty copy of the same ride, it is filled instead of doubled.
200 duplicate{"status": "duplicate", "ride_id": 123} Already here, nothing changed.
200 removedThe rider deleted this ride on PeerTier. It stays deleted. Do not retry.
200 ambiguousThe file matches more than one of the rider's rides. Nothing was written. Do not retry.
401The access token expired or was revoked. Refresh it. If the refresh fails, the rider disconnected your app: ask them to connect again.
403Your app is not set up to upload rides. Contact us.
422Unreadable FIT file or a missing field. Do not retry the same request.
429More than 60 pushes a minute for one rider. Retry after the Retry-After header.

5. What happens on PeerTier

Rides ridden before the rider first connected your app count as history: they fill the rider's fitness (training load, FTP), never XP. Rides after that count in full.

Your app can only upload rides. It can never read the rider's rides, fitness or profile, and never change or delete anything.

The rider can disconnect your app at any time in Settings, Connected apps. Your tokens stop working at once.

← PeerTier