Tredict API - OAuth2 documentation

This guide enables you to authorize our users into your application via OAuth2 and upload their activities to our activity endpoint, download a list of activities or upload health data to the user's account. The endpoints described here also cover the Personal API.

Introduction

OAuth2 is an open standard for authorization, commonly used as a way to grant websites or applications limited access to a user's information on another website. Tredict uses OAuth2 to allow third-party applications to access user data securely and with user consent.

This guide will walk you through the process of integrating with Tredict's OAuth2 API, from applying for client credentials to making authenticated requests to our endpoints.

Tredict supports both the standard OAuth2 flow and the OAuth2.1 flow with Proof Key for Code Exchange (PKCE). PKCE is recommended for public clients (e.g., mobile and single-page applications) to enhance security.

For personal use, please create a Personal API token in the Tredict App. The Personal API token requires a manual presence in the Tredict App at least once every 10 days to stay active.

Apply

To apply to the API and get your client credentials, please send the following to support@tredict.com:

  • Name of your app or service
  • A very short description of your service
  • The logo of your service as .svg or .png feasible for a dimension of 124 x 124 pixel.
  • One or more OAuth2 __callback url__s, we redirect the user to, after successful authorization.
  • Link to your privacy statement
  • Your needed scopes. By default you get "activityWrite", "activityRead", "bodyvaluesWrite", "bodyvaluesRead".
  • Do you use it with a public client (PKCE only) or an explicit server flow (enhanced credentials)?
  • With enhanced credentials flow: Your mobile phone number in international format. You will receive a pin on this number to decrypt the KeePass2 vault, we will send you with your client credentials. We won't store this number. Alternativly we will find another way.

The first five points may be displayed to the user.

Please handle the client credentials securely and with care.
In case of a loss or breach of your client credentials, please tell us, so we can generate new ones for you.

Personal API vs OAuth2

Do you need to apply for the OAuth2.1 flow or is the Personal API sufficient for your use case?

Feature Personal API OAuth2 API
Target Users Individual users with small apps Platforms and apps with a larger user base
Multi-User Support No Yes (multiple users)
Scopes Configurable per personal token Configurable per application

We generally do not issue OAuth2 credentials for coaching apps, especially those that do not use the planning and/or analysis features of Tredict. Nor does Tredict issue OAuth2 credentials for apps that simply would fetch the executed activity data or health data solely for their own processing. Tredict is not a data proxy and does not allow automated data forwarding of other parties.

You can always use your Personal API token for your app. You create the token with your Tredict account in the Tredict App. The Personal API token requires a manual presence in the Tredict App at least once every 10 days, otherwise the endpoints return an HTTP 401.
Personal API token

You create a Personal API token with your Tredict account in the Tredict App. When creating it, you select the scopes the token gets (activityRead, activityWrite, bodyvaluesRead, bodyvaluesWrite, see Scopes). Only grant the scopes you actually need. The token is shown only once, so store it securely, f.ex. in an environment variable, and never in source code.

Send the token in the Authorization header of every request, exactly like an OAuth2 access token (see Using tokens in API requests). There is no token refresh, no PKCE and no authorization code.

All endpoints described below work with a Personal API token. Some endpoints and parameters only work with a Personal API token, they are marked accordingly.

Status codes specific to Personal API tokens:

  • 401: The token is missing, mistyped or deleted, or it is inactive because of missing manual presence in the Tredict App. It is reactivated automatically with your next visit of the Tredict App.
  • 402: The account is read-only. Write access can be purchased in the Tredict App.
  • 403: The token lacks the scope required by the endpoint, or the account is disabled.

Integrate

Tredict enables you to authorize and gain access to selected functionality of our users and system via the OAuth2 principles.

Register account for testing

Register a Tredict account for testing purposes at https://www.tredict.com/register/

Request authorization code

Redirect your user to the following url, so the user can authenticate as a Tredict user and gives an authorization grant.

Authorization dialog URL:

https://www.tredict.com/authorization/

Parameter

Name Required Description
client_id Yes Your client_id you received from us.
state No Some optional url encoded state you can attach and we will send you back as a parameter on the callback url.
You can track back the authorization code to your user by using one time keys.
Please do not attach sensitive information here!
redirect_uri No Redirect URI that overrides the default callback URL registered for your client. Must match one of the registered redirect URIs for your client.
code_challenge No Code challenge for PKCE (Proof Key for Code Exchange) flow. Required when using PKCE.
code_challenge_method No Code challenge method for PKCE. Must be "S256" when using PKCE.

Example

https://www.tredict.com/authorization/?client_id=${your_client_id_here}&state=my_user_locale%3Des%26my_oauth_user_id%3D843434

Example with PKCE and custom redirect_uri

https://www.tredict.com/authorization/?client_id=${your_client_id_here}&redirect_uri=https%3A%2F%2Fyourapp.com%2Foauth%2Fcallback&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSs9XNc&code_challenge_method=S256&state=my_user_locale%3Des%26my_oauth_user_id%3D843434

On successfull authorization by the user, we will redirect the user to your given callback url with the authorization code and state attached. You can use the authorization code to obtain the user access token then. The authorization code is only valid once and it expires after 10 minutes.

Redirect on success

https://www.yourapplication.that.does.not.exist.com/?code=92682f9544255285736fb6d2d8a556da&state=my_user_locale%3Des%26my_oauth_user_id%3D843434

On error we will call your callback url just with an error parameter.

Redirect on error

https://www.yourapplication.that.does.not.exist.com/?error=server_error
Error code Description
server_error The authorization server itself encountered an unexpected condition.

Localization

Tredict is also available in German. To display a German authorization dialog, just use https://www.tredict.de/ accordingly.

Example of using the German authorization dialog:

https://www.tredict.de/authorization/?client_id=${your_client_id_here}&state=my_user_locale%3Des%26my_oauth_user_id%3D843434

Request user access token

You can request an user access token either with a freshly received authorization code or a long live refresh token.

Access token url:

POST https://www.tredict.com/user/oauth/v2/token

Request headers:

  • Authorization: Basic auth with base64 encoded string client_id:client_secret. For example, if your client id is 12345 and client secret is verySecret, then you need to base64 encode the string 12345:verySecret, resulting in MTIzNDU6dmVyeVNlY3JldA==. Value of authorization header in that case should be Basic MTIzNDU6dmVyeVNlY3JldA==. Omit if PKCE is used!
  • Content-Type: application/x-www-form-urlencoded
  • Accept: application/json;charset=UTF-8

Example request using curl without PKCE:

curl -X POST "https://www.tredict.com/user/oauth/v2/token" \
  -H "Authorization: Basic MTIzNDU6dmVyeVNlY3JldA==" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "Accept: application/json;charset=UTF-8" \
  -d "grant_type=authorization_code&code=92682f9544255285736fb6d2d8a556da"

Example request using curl with PKCE:

code_verifier=$(openssl rand -base64 32 | tr -d '+/' | tr -d '=' | tr -d ' ')
code_challenge=$(echo -n "$code_verifier" | openssl dgst -sha256 -binary | openssl enc -base64 -A | tr -d '=' | tr '/+' '_-' | tr -d ' ')

curl -X POST "https://www.tredict.com/user/oauth/v2/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "Accept: application/json;charset=UTF-8" \ -d "grant_type=authorization_code&code=92682f9544255285736fb6d2d8a556da&code_verifier=$code_verifier&client_id=${your_client_id_here}"

Request POST data:

Name Required Description
grant_type Yes This can be "authorization_code" or "refresh_token".
code (Yes) If the grant_type is "authorization_code" this field is required and must be a valid authorization code.
refresh_token (Yes) If the grant_type is "refresh_token" this must be the refresh token you received already by an earlier request.
code_verifier (No) Required if PKCE was used in the authorization request. This must be the same code verifier that was used to generate the code challenge.
client_id (No) Required if PKCE was used in the authorization request.

Status codes:

  • 200: Request could be processed successfully.
  • 400: You provided invalid post data.
  • 401: Your client credentials are invalid.
  • 403: You provided an invalid code. Maybe it is expired.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Response JSON object entries:

  • access_token: This is the user resource access bearer token encoded as JWT.
    It is valid for two days, before it expires.
    You must request a new one if expired, with the "refresh_token" grant.
    You can do this at any time, but only if necessary.

  • expires_at: Lifetime of the token in seconds. This is not a unix timestamp.

  • refresh_token: A long living refresh token, which never expires, but can be invalidated with PKCE token rotation, if PKCE is used. Please store this securely and tell us if something went wrong, so we can invalidate it. This is returned if the "grant_type" is "authorization_code" or in combination with PKCE on each access token refresh, where the old refresh token gets rotated then.

PKCE Flow

Tredict supports the OAuth2.1 flow with Proof Key for Code Exchange (PKCE). PKCE is recommended for public clients (e.g., mobile and single-page applications) to enhance security. PKCE helps prevent authorization code interception attacks.

Create a code verifier
code_verifier=$(openssl rand -base64 32 | tr -d '+/' | tr -d '=' | tr -d '
')
echo "Code Verifier: $code_verifier"
Create a code challenge
code_challenge=$(echo -n "$code_verifier" | openssl dgst -sha256 -binary | openssl enc -base64 -A | tr -d '=' | tr '/+' '_-' | tr -d '
')
echo "Code Challenge: $code_challenge"
Redirect the user with code challenge
https://www.tredict.com/authorization/?client_id=${your_client_id_here}&state=my_user_locale%3Des%26my_oauth_user_id%3D843434&code_challenge=$code_challenge&code_challenge_method=S256
Exchange the authorization code for an access token with code verifier
curl -X POST "https://www.tredict.com/user/oauth/v2/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "Accept: application/json;charset=UTF-8" \
  -d "grant_type=authorization_code&code=92682f9544255285736fb6d2d8a556da&code_verifier=$code_verifier&client_id=${your_client_id_here}"

Scopes

Currently you need to contact us to extend or reduce your scopes. As default "activityWrite" is set. Scopes are applied on the client. It is currently not possible for the user to change scopes on its own.

Available scopes:

  • activityWrite: Upload activity files to Tredict.
  • activityRead: Download the activity list and activity files from Tredict.
  • bodyvaluesWrite: Upload health data, capacity values and body values to Tredict.
  • bodyvaluesRead: Download health data, capacity values and body values from Tredict.

Using tokens in API requests

Once you have obtained an access token, you can use it to make authenticated requests to the Tredict API. Include the access token in the Authorization header of your requests.

Example of using an access token:

curl -X GET "https://www.tredict.com/api/oauth/v2/activityList" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Accept: application/json;charset=UTF-8"

In this example, replace eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... with your actual access token.

Guides & Skills

These topic-specific guides cover the most common use cases and are a good starting point before diving into the full endpoint reference below.

  • Activities - List, fetch, upload, update, and download activities.
  • Structured workouts - Build workouts and training plans with steps, repetitions, and target zones.

Endpoints

Activity list - Download

You can fetch a list of activities from the activitiy list endpoint.

GET https://www.tredict.com/api/oauth/v2/activityList

Request parameters:

Name Required Description
startDate No If omitted, the first list entry will be the newest activity. Must be an UTC ISO date string. F.ex. '2019-07-10T14:14:46.257Z'
pageSize No Defaults to 500. Amount of activities a request should return at maximum. Must be at least 50 and not more then 1000.
extendedSummary No If set to 1, the entries container an extendedSummary with way more metrics, like pace and so on. Strength trainings additionally carry the strength keys like the number of sets, the repetitions and the tonnage, see the activity download below. Activities the athlete rated additionally carry 'enjoyment' and 'perceivedEffort', also described at the activity download.

Examples:

Start with newest activities: https://www.tredict.com/api/oauth/v2/activityList?pageSize=600
Start at date: https://www.tredict.com/api/oauth/v2/activityList?startDate=2019-07-10T14:14:46.257Z&pageSize=600

Request headers:

  • Authorization: Bearer ${access_token}
  • Accept: application/json;charset=UTF-8

Response JSON object:

next will be undefined if it is the last page. For performance reasons there is no prev key, so this is an one-way traversal, starting with the newest activities.

{
  "_links": {
    "self": {
      "href": "https://www.tredict.com/api/oauth/v2/activityList?startDate=2020-02-27T18:20:33.000Z&pageSize=100"
    },
    "next": {
      "href": "https://www.tredict.com/api/oauth/v2/activityList?startDate=2019-07-10T14:14:46.257Z&pageSize=100"
    }
  },
  "count": 100,
  "_embedded": {
    "activityList": [
      {
        "_links": {
          "self": {
            "href": "https://www.tredict.com/api/oauth/v2/activity/wvCNvSYES8kd6mQDuf27kc"
          }
        },
        "id": "wvCNvSYES8kd6mQDuf27kc",
        "date": "2020-02-27T18:20:33.000Z",
        "sportType": "running"
      },
      {
        "_links": {
          "self": {
            "href": "https://www.tredict.com/api/oauth/v2/activity/oU6waMM9aPXdfFZw3DVRFf"
          }
        },
        "id": "oU6waMM9aPXdfFZw3DVRFf",
        "date": "2020-02-23T12:22:18.000Z",
        "sportType": "running",
        "isFromGarminApi": true,
        "attribution": "Garmin epix gen2"
      },
      ...
    ],
  }
}

isFromGarminApi and attribution are only present on activities that Tredict received through the Garmin API.

Status codes:

  • 200: Request could be processed successfully.
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "activityRead" scope enabled by us.

Activity - Download

You can fetch the details of an activity from the activity endpoint.

GET https://www.tredict.com/api/oauth/v2/activity/${activityId}

Request headers:

  • Authorization: Bearer ${access_token}
  • Accept: application/json;charset=UTF-8

Request parameters:

Name Required Description
extraValues No If set to '1', additional aggregated values are added to the 'summary'. These are Tredict specific metrics like the efficiency scores 'powerAerobicFactor', 'speedIndex', 'speedAerobicFactor', 'runningEffectiveness' and 'formPowerFactor', the 'effort' and 'vo2max', running dynamics like 'groundContactTime', 'flightTime' and 'stepLength', as well as the 'intensityDistribution' and 'zonesDistribution' of the whole activity. See the list of additional summary keys below.
allSeries No If set to '1', every time series that the recording device delivered is returned in 'seriesSampled', instead of the standard set of series. This adds running dynamics like 'stepLength', 'verticalOscillation', 'verticalRatio', 'flightTime', 'formPower', 'airPower', 'legSpringStiffness', cycling pedal metrics like torque effectiveness, pedal smoothness and power phase angles, and sensor series like 'coreTemperature', 'glucose' or 'saturatedHemoglobin'. An activity can carry dozens of additional series, so expect a multiple of the response size. This parameter only works with a Personal API token.
withLaps No If set to '1', the laps of the activity are returned in the 'laps' array. Each lap carries its own aggregated metrics for that part of the activity, using the same units and field names as the summary. An activity can contain many laps, for example one per kilometer or one per interval, so the response grows accordingly.

On a successfull request you will receive a JSON object with details of the activity like the summary and time series data of the heart rate, speed and so on.

Response JSON object:

{
  "_id": "i7QwbprV4VveXFSRHXcq1R",
  "date": "2020-02-21T14:09:01.000Z",
  "createdAt": "2020-02-21T14:40:55.008Z",
  "sportType": "running",
  "subSportType": "generic",
  "title": "Very short speed run",
  "notes": "",
  "enjoyment": 2.6,
  "perceivedEffort": 7,
  "summary": {
    "durationTotal": 1281,
    "duration": 1234,
    "distance": 3442.44,
    "altitude": {
      "ascent": 137,
      "descent": 157,
      "elevation": 35
    },
    "positionLat": 53.6718,
    "positionLong": 10.0099,
    "walkingDuration": 118,
    "calories": 202,
    "speed": 2.7890526315789383,
    "speedMax": 3.947,
    "heartrate": 152,
    "heartrateMax": 182,
    "power": 192,
    "powerMax": 278,
    "pace": 358,
    "paceMax": 253,
    "cadence": 163,
    "cadenceMax": 178
  },
  "timezone": "Europe/Berlin",
  "isFromGarminApi": true,
  "attribution": "Garmin epix gen2",
  "equipment": [
    {
      "id": "suuid-string",
      "type": "shoes",
      "name": "Nike Vaporfly",
      "usageRange": { "start": "2023-01-01T00:00:00.000Z", "end": null },
      "count": 12,
      "duration": 9600,
      "distance": 120.5
    }
  ],
  "seriesSampled": {
    "sampleSize": 1,
    "endOffset": 0,
    "data": {
      "positionLat": [ ... ],
      "positionLong": [ ... ],
      "speed": [ ... ],
      "distance": [ ... ],
      "altitude": [ ... ],
      "heartrate": [ ... ],
      "cadence": [ ... ],
      "power": [ ... ]
    }
  }
}

All units are metric.
SportTypes are running, cycling, swimming and misc.
The series sampleSize is the number of seconds each data point in the series represents.

isFromGarminApi and attribution are only present on activities that Tredict received through the Garmin API.

enjoyment and perceivedEffort are the athlete's own rating of the activity, entered in Tredict after the training. Both are only present if the athlete rated the activity. enjoyment ranges from 1.0 to 3.0 in steps of 0.1: below 1.2 "very miserable", below 1.6 "miserable", below 1.9 "rather miserable", below 2.2 "balanced", below 2.5 "rather joyful", below 2.9 "joyful", from 2.9 "very joyful". perceivedEffort is the perceived exertion (RPE) from 1 to 10: 1 "extremely light", 2 "very light", 3 "easy", 4 "slightly strenuous", 5 "strenuous", 6 "demanding", 7 "very demanding", 8 "exhausting", 9 "extremely exhausting", 10 "excessively exhausting".

Possible keys in the summary:
durationTotal, originalDurationTotal, duration, originalDuration, distance, originalDistance, altitude, positionLat, positionLong, walkingDuration, calories, speed, speedMax, heartrate, heartrateMax, respirationRate, respirationRateMax, minuteVentilation, minuteVentilationMax, tidalVolume, tidalVolumeMax, power, powerMax, pace, paceMax, cadence, cadenceMax, temperature, strokes, poolLength, exerciseSetCount, exerciseCount, repetitions, volume, weightMax, durationSets, durationRest

originalDistance, originalDuration and originalDurationTotal are only present if the user corrected the value manually in Tredict. They hold the originally recorded value, while distance, duration and durationTotal hold the corrected value that is displayed in Tredict.
The time series and the laps are not adjusted by such a correction. The last value of the distance series or the sum of the lap distances can therefore deviate from the summary distance.

The strength keys are only present on activities with recorded sets, f.ex. a strength training of a Garmin watch. exerciseSetCount is the number of active sets and exerciseCount the number of distinct exercises. repetitions is the sum of all repetitions, volume the sum of repetitions times weight in kilograms (tonnage) and weightMax the highest weight of a single set in kilograms. durationSets is the summed duration of the active sets and durationRest the summed duration of the rests between the sets, both in seconds. The rests are part of duration and not paused time.

minuteVentilation and tidalVolume are only present if a breathing sensor, f.ex. Tymewear, recorded the activity. They are given in the sensor's own units, not in liters, so they can only be compared between recordings of the same sensor.

Additional possible keys in the summary with the extraValues=1 parameter. A key is only present if it could be calculated for the activity:
firstSplitDuration, groundContactTime, groundContactTimeBalance, flightTime, stepLength, steps, intensityDistribution, zonesDistribution, effort, powerAerobicFactor, speedIndex, runningEffectiveness, speedAerobicFactor, vo2max, formPowerFactor, impactLoadingRateStryd

Possible keys in seriesSampled:
positionLat, positionLong, speed, distance, altitude, heartrate, cadence, power, respirationRate, temperature, groundContactTime, groundContactTimeBalance, legSpringStiffness, impactLoadingRateStryd

Additional possible keys in seriesSampled with the allSeries=1 parameter. A series is only present if the recording device or an external pod, f.ex. a Stryd, delivered it - This parameter only works with a Personal API token.:
stepLength, verticalOscillation, verticalOscillationBalanceStryd, verticalRatio, flightTime, legSpringStiffnessBalance, impactLoadingRateBalanceStryd, bodyElasticity, formPower, airPower, leftRightBalance, grade, verticalSpeed, speedGradeAdjusted, distanceNoWalking, performanceCondition, trailScoreGarmin, grit, flow, coreTemperature, glucose, totalHemoglobin, saturatedHemoglobin, minuteVentilation, tidalVolume, heading, accelerationMagnitude, gpsAccuracy, batteryLevel, humidityStryd, temperatureStryd, leftTorqueEffectiveness, rightTorqueEffectiveness, leftPedalSmoothness, rightPedalSmoothness, leftPlatformCenterOffset, rightPlatformCenterOffset, leftPowerPhaseStart, leftPowerPhaseEnd, rightPowerPhaseStart, rightPowerPhaseEnd, leftPowerPhasePeakStart, leftPowerPhasePeakEnd, rightPowerPhasePeakStart, rightPowerPhasePeakEnd, altitudeStryd, distanceStryd, speedStryd, powerOEM, groundContactTimeOEM, verticalOscillationOEM

A key with the suffix OEM, f.ex. verticalOscillationOEM, is the value of the watch itself, while the key without the suffix comes from an external pod.
A key with the suffix Stryd, f.ex. speedStryd, is the raw value of the Stryd pod, while the key without the suffix is the value Tredict works with.

The laps of the activity are only included when the withLaps=1 parameter is set. They are returned as a chronological laps array, each lap holding its own aggregated metrics for that part of the activity, using the same units as the summary. Zone and intensity distributions are not part of a lap. If you need time in zone per lap, request the full time series with allSeries=1 and compute it from there.

Response of a single lap with the withLaps=1 parameter:

{
  "startTime": "2026-06-14T08:28:08.000Z",
  "duration": 316,
  "distance": 1000.09,
  "lapTrigger": "distance",
  "speed": 3.18,
  "pace": 316,
  "heartrate": 172,
  "heartrateMax": 177,
  "power": 221,
  "cadence": 171,
  "altitude": {
    "ascent": 0,
    "descent": 5,
    "elevation": -5
  }
}

startTime is the UTC start of the lap, duration its length in seconds and lapTrigger describes why the lap ended, f.ex. distance or manual.
A series is only present if the recording device or an external pod delivered it, so the running dynamics and efficiency keys are omitted when they are not available.

Possible keys in a lap:
startTime, duration, distance, lapTrigger, speed, speedMax, pace, paceMax, heartrate, heartrateMax, respirationRate, respirationRateMax, minuteVentilation, minuteVentilationMax, tidalVolume, tidalVolumeMax, power, powerMax, cadence, cadenceMax, steps, kiloJoule, altitude, verticalSpeed, flightTime, flightTimeMax, groundContactTime, groundContactTimeMin, groundContactTimeBalance, leftRightBalance, formPower, airPower, impactLoadingRateStryd, stepLength, stepLengthMax, legSpringStiffness, legSpringStiffnessMax, verticalOscillation, verticalRatio, bodyElasticity, powerAerobicFactor, speedIndex, runningEffectiveness, speedAerobicFactor, vo2max, vo2maxRelative, formPowerFactor, strokes, swolf, strokeLength, poolLength

The altitude object of a lap carries the keys ascent, descent and elevation.

Strength trainings always include their sets in the exerciseSets array, no parameter is needed. Activities without recorded sets have no exerciseSets key. The array is in chronological order and holds the active sets with setType = active and the rests between the sets with setType = rest.

Response of an active set and the following rest in exerciseSets:

[
  {
    "startTime": "2026-08-14T20:52:00.000Z",
    "duration": 148.813,
    "setType": "active",
    "exerciseCategory": "squat",
    "exerciseName": "weighted_step_up",
    "repetitions": 8,
    "weight": 9.0625,
    "volume": 72.5,
    "estimatedOneRepMax": 11.48,
    "heartrate": 87,
    "heartrateMax": 106,
    "calories": 10
  },
  {
    "startTime": "2026-08-14T20:54:29.000Z",
    "duration": 60,
    "setType": "rest",
    "heartrate": 88,
    "heartrateMax": 108,
    "heartrateRecovery": 16,
    "calories": 4
  }
]

startTime is the UTC start of the entry and duration its length in seconds. exerciseCategory and exerciseName use the naming of the recording device and are missing if the device could not determine the exercise. weight is in kilograms, 0 means no additional weight, f.ex. a body weight exercise. volume is repetitions times weight in kilograms. estimatedOneRepMax is the estimated one repetition maximum in kilograms after Epley and only present on sets with up to 12 repetitions. heartrateRecovery is only present on rests and holds the drop of the heart rate from its maximum to the end of the rest in beats per minute. calories are kilocalories.

Possible keys in an exercise set:
startTime, duration, setType, exerciseCategory, exerciseName, repetitions, weight, volume, estimatedOneRepMax, heartrate, heartrateMax, heartrateRecovery, calories

Status codes:

  • 200: Request could be processed successfully.
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "activityRead" scope enabled by us.

Activity - Update

Change fields like title and notes in an existing executed activity or planned training.

POST https://www.tredict.com/api/oauth/v2/activity/update/${activityId}

Request headers:

  • Authorization: Bearer ${access_token}
  • Content-Type: application/json

POST body example with possible fields:

title and notes are optional fields.

{
  "training": {
    "title": "A new updated title with maximal 255 characters",
    "notes": "Updated notes with maximal 2048 characters",
  }
}

Status codes:

  • 200: Request could be processed successfully.
  • 400: The bodyvalues object is invalid or contains invalid data
  • 401: Invalid authorization header.
  • 402: Payment required.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "activityWrite" scope enabled by us.

Activity file - Download

This endpoint only works with a Personal API token.

Download the activity as a raw file in .fit- or .tcx-format. The file extension depends on the original source.

GET https://www.tredict.com/api/oauth/v2/activity/file/${activityId}

Request headers:

  • Authorization: Bearer ${access_token}
  • Accept: application/json;charset=UTF-8

On a successfull request you will receive a binary download with a content-type of application/octet-stream. The filename is included in the content-disposition header.

Status codes:

  • 200: Request could be processed successfully.
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "activityRead" scope enabled by us.

Activity file - Upload

The activity endpoint takes .fit or .tcx workout files. .fit is preferable.

POST https://www.tredict.com/api/oauth/v2/activity/upload

Request headers:

  • Authorization: Bearer ${access_token}
  • Content-Type: multipart/form-data
  • Accept: application/json;charset=UTF-8

Request POST data:

The POST data should be formed as multipart form-data for file uploads, thus it should include a content disposition with a file field and filename definition.

Content-Disposition: form-data; name="file"; filename="activity-file.fit"

The mime-type does not matter, we check for the file type by extension and header checksums on our own.

Content-Disposition: form-data; name="name"

Optionally you can define the workout name as a "name" field, like "Strength training", which will show up in the activity list of the user then.

Content-Disposition: form-data; name="notes"

You also have the possibility to add an optional description via a "notes" field. This is constrainted to a maximum of 1024 characters.

Response JSON object entries:

On a successfull upload with HTTP response status 200, you will receive a JSON object with details of the upload.

The response object has a success-key with a list of workouts the file included. Normally this is only one, but could be more on a multi sport file.

{
  success: [
    {
      date: "2020-07-29T12:07:04.627Z", // This is the activity date
      trainingId: "eHvBqhSYoSiCKh4dK8mdv3"
    }
  ]
}

On an error with the file, the response object contains an error-key including an error string.

{
  error: "FILE_EXISTS",
  meta: [
    {
      date: "2020-07-29T12:07:04.627Z",
      trainingId: "eHvBqhSYoSiCKh4dK8mdv3"
    }
  ]
}
{
  error: "FILE_EXTENSION_UNSUPPORTED"
}
{
  error: "SPORT_TYPE_UNSUPPORTED"
}

Status codes:

  • 200: Request could be processed successfully.
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Planned training list - Download

You can fetch a list of planned training sessions from the planned training list endpoint.

GET https://www.tredict.com/api/oauth/v2/plannedTrainingList

Request parameters:

Name Required Description
startDate No If given, the planned training list will fetch from this date on towards the future, otherwise from "today". The date can be at maximum 2 years in the past. Must be an UTC ISO date string. F.ex. '2019-07-10T14:14:46.257Z'
endDate No If given, the list does not included planned workouts newer then this date. Must be an UTC ISO date string. F.ex. '2019-07-16T14:14:46.257Z'
sportType No Filters the list by the given single sportType. Possible values are 'running', 'cycling', 'swimming', 'misc'.

Examples:

Start from today: https://www.tredict.com/api/oauth/v2/plannedTrainingList
Start at date and filter by cycling: https://www.tredict.com/api/oauth/v2/plannedTrainingList?startDate=2019-07-10T14:14:46.257Z&sportType=cycling

Request headers:

  • Authorization: Bearer ${access_token}
  • Accept: application/json;charset=UTF-8

Response JSON object:

The response includes all planned training sessions that were found.

Name Optional Description
id No The unique id of the training session. Alias: trainingId
date No Scheduled date of the training session.
updatedAt No Last time the user updated the training session. You can compare on this date for updates.
timezone No Timezone in hours. '2' means GTM+2.
sportType No The sportType can be 'running', 'cycling', 'swimming', 'misc'.
title Yes If the user manually defined a title this is given here.
notes Yes If the user manually defined a description or notes this is given here.
distance Yes The summed up distance in meters of the planned training segments.
duration Yes The summed up duration in seconds of the planned training segments. Do not calculate a pace from distance and duration. There could be segments with neither both.
executedTrainingId Yes Set with the trainingId of an executed training, if this planned training session has a executed training attached.

{
  "count": 3,
  "_embedded": {
    "plannedWorkoutList": [
      {
        "id": "rKrytkDyXyDj81WbzraiNv",
        "date": "2021-11-13T09:35:32.465Z",
        "updatedAt": "2021-11-12T09:35:39.351Z",
        "timezone": -8,
        "sportType": "running"
      },
      {
        "id": "fa769DTeUzqjyegTqN9jJ9",
        "date": "2021-11-12T09:35:32.465Z",
        "updatedAt": "2021-11-12T09:35:39.367Z",
        "timezone": -8,
        "sportType": "misc"
      },
      {
        "id": "eh4GbpTxTGrPrdgBm3otum",
        "date": "2021-11-02T09:35:32.465Z",
        "updatedAt": "2021-11-12T09:35:39.380Z",
        "timezone": 2,
        "sportType": "cycling",
        "title": "This workout is in the past",
        "notes": "A slightly short description"
      }
    ]
  }
}

Status codes:

  • 200: Request could be processed successfully.
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "activityRead" scope enabled by us.

Planned training - Download

You can fetch the details of an planned training session from the planned training download endpoint.

GET /api/oauth/v2/plannedTraining/file/json/${trainingId}?language=de&extraValues=1

Request headers:

  • Authorization: Bearer ${access_token}
  • Accept: application/json;charset=UTF-8

Request parameters:

Name Required Description
language No If the user did not specified a title, the default 'workoutName' will be in this language. Possible values: 'en', 'de'. Default: 'en'
extraValues No If set to '1', all in Tredict available zone values will be added as extra values in each workout step. This is not specified in the Garmin Training API and a Tredict own addition. Extra value property names are: 'targetProgressionType', 'extraValueCadence', 'extraValueHeartrate', 'extraValueSpeed' and 'extraValuePower'. See below for an output example.

On a successfull request you will receive a JSON object with details of the planned training session.
The format and specification of the json object is adopted by the Garmin Training API for scheduled workouts.

Response JSON object:

{
  "workoutName": "Cycling workout",
  "description": "Description",
  "sport": "CYCLING",
  "steps": [{
    "type": "WorkoutRepeatStep",
    "stepOrder": 3,
    "repeatType": "REPEAT_UNTIL_STEPS_CMPLT",
    "repeatValue": 2,
    "steps": [{
      "type": "WorkoutStep",
      "stepOrder": 0,
      "intensity": "INTERVAL",
      "description": "Ramp-up",
      "durationType": "DISTANCE",
      "durationValue": 1000,
      "targetType": "POWER",
      "targetValueLow": 188,
      "targetValueHigh": 228
    }, {
      "type": "WorkoutStep",
      "stepOrder": 1,
      "intensity": "INTERVAL",
      "durationType": "DISTANCE",
      "durationValue": 1000,
      "targetType": "POWER",
      "targetValueLow": 217,
      "targetValueHigh": 257
    }, {
      "type": "WorkoutStep",
      "stepOrder": 2,
      "intensity": "INTERVAL",
      "durationType": "DISTANCE",
      "durationValue": 1000,
      "targetType": "POWER",
      "targetValueLow": 245,
      "targetValueHigh": 285
    }]
  }, {
    "type": "WorkoutStep",
    "stepOrder": 4,
    "intensity": "RECOVERY",
    "durationType": "TIME",
    "durationValue": 600,
    "targetType": "OPEN"
  }, {
    "type": "WorkoutRepeatStep",
    "stepOrder": 7,
    "repeatType": "REPEAT_UNTIL_STEPS_CMPLT",
    "repeatValue": 2,
    "steps": [{
      "type": "WorkoutStep",
      "stepOrder": 5,
      "intensity": "INTERVAL",
      "description": "Full out interval!",
      "durationType": "DISTANCE",
      "durationValue": 200,
      "targetType": "POWER",
      "targetValueLow": 300,
      "targetValueHigh": 340
    }, {
      "type": "WorkoutStep",
      "stepOrder": 6,
      "intensity": "REST",
      "durationType": "DISTANCE",
      "durationValue": 200,
      "targetType": "OPEN"
    }]
  }, {
    "type": "WorkoutRepeatStep",
    "stepOrder": 9,
    "repeatType": "REPEAT_UNTIL_STEPS_CMPLT",
    "repeatValue": 6,
    "steps": [{
      "type": "WorkoutStep",
      "stepOrder": 8,
      "intensity": "INTERVAL",
      "durationType": "DISTANCE",
      "durationValue": 100,
      "targetType": "POWER",
      "targetValueLow": 188,
      "targetValueHigh": 228
    }]
  }]
}

Response JSON object with provided extraValues=1 parameter:

{
  workoutName: 'Example running with Tredict extra values',
  sport: 'RUNNING',
  steps: [{
    type: 'WorkoutStep',
    stepOrder: 0,
    intensity: 'INTERVAL',
    durationType: 'TIME',
    durationValue: 600,
    targetType: 'POWER',
    targetValueLow: 209,
    targetValueHigh: 235,
    extraValuePower: 222
  },
  {
    type: 'WorkoutStep',
    stepOrder: 1,
    intensity: 'INTERVAL',
    durationType: 'DISTANCE',
    durationValue: 2280,
    targetProgressionType: 'ramp',
    targetType: 'SPEED',
    targetValueLow: 2.4691358024691357,
    targetValueHigh: 2.5974025974025974,
    extraValueCadence: 81,
    extraValueHeartrate: 155,
    extraValueSpeed: 2.5316455696202533,
    extraValuePower: 235
  }]
}

Status codes:

  • 200: Request could be processed successfully.
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "activityRead" scope enabled by us.

Planned training file - Download

Download the planned training session as a Garmin workout .fit file.
This file can be used on the users device for a planned training session or could be parsed for further processing.

GET /api/oauth/v2/plannedTraining/file/fit/${trainingId}?language=de

Request headers:

  • Authorization: Bearer ${access_token}
  • Accept: application/json;charset=UTF-8

On a successfull request you will receive a binary download with a content-type of application/octet-stream. The filename is included in the content-disposition header.

Status codes:

  • 200: Request could be processed successfully.
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "activityRead" scope enabled by us.

Planned training - Change date

Reschedule an existing planned training session to a new date in the future.
Only the date/time of the planned training is changed. All other fields like title, notes, structure, etc. stay intact.

POST https://www.tredict.com/api/oauth/v2/plannedTraining/changeDate

Request headers:

  • Authorization: Bearer ${access_token}
  • Content-Type: application/json

POST body:

{
  "trainingId": "eHvBqhSYoSiCKh4dK8mdv3",
  "date": "2026-05-11T06:30:00.000Z"
}
Name Required Description
trainingId Yes The id of the planned training to reschedule. You can obtain it from the planned training list endpoint.
date Yes The new date of the planned training as a full ISO-8601 datetime string. Must be in the future.

Status codes:

  • 200: Request could be processed successfully.
  • 400: The request body is invalid, the trainingId is invalid, the date is missing or malformed, or the date is not in the future.
  • 401: Invalid authorization header.
  • 402: Payment required (the user's account is read-only).
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 404: No planned training was found for the given trainingId.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "activityWrite" scope enabled by us.

Training efforts - Download

Download the calculated training efforts sorted by date.

It returns records in a trainingEfforts object with YYYYMMDD as date key, an array with length by the number of trainings, where each entry is a tuple [single activity effort, optional metadata]. The metadata can contain an 'isRace' and an 'unconfirmed' indicator. 'unconfirmed' marks a planned training that has not been executed yet. Planned trainings are listed from today on, missed planned trainings in the past are left out.

GET https://www.tredict.com/api/oauth/v2/efforts

Request headers:

  • Authorization: Bearer ${access_token}
  • Content-Type: application/json

Request parameters:

Name Required Description
startDate No startDate sets the newest date of the effort entries. If omitted, it defaults to one month from now to include planned trainings. Must be an UTC ISO date string. F.ex. '2025-07-10T14:14:46.257Z'
endDate No endDate sets the oldest date of the effort entries. If omitted, it defaults to "1 year back from startDate". Must be an UTC ISO date string. F.ex. '2024-07-10T14:14:46.257Z'

Response JSON object:

{
  "trainingEfforts": {
    "20191024": [
      [77],
      [80]
    ],
    "20191124": [
      [123, { "isRace": true }]
    ],
    "20191201": [
      [95, { "unconfirmed": true }]
    ]
  },
  "filterStartDate": "2019-12-24T14:14:46.257Z",
  "filterEndDate": "2019-10-24T14:14:46.257Z",
}

Status codes:

  • 200: Request could be processed successfully.
  • 400: The request is invalid or contains invalid data
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "activityRead" scope enabled by us.

Health data and body values - Download

Download health data and body values in JSON format from this endpoint.

GET https://www.tredict.com/api/oauth/v2/bodyvalues

Request headers:

  • Authorization: Bearer ${access_token}
  • Content-Type: application/json

Response JSON object:

{
  "bodyvalues": [
    {
      "timestamp": "2019-12-19T22:43:08.000Z",
      "timezoneOffsetInSeconds": 3600,
      "restingHeartrate": 53
    },
    {
      "timestamp": "2020-03-21T18:33:42.806Z",
      "timezoneOffsetInSeconds": 3600,
      "restingHeartrate": 54
    },
    {
      "timestamp": "2021-01-13T15:12:35.911Z",
      "timezoneOffsetInSeconds": 3600,
      "restingHeartrate": 53,
      "bodyHeightInCentimeter": 177,
      "weightInKilograms": 69.6,
      "bodyFatInPercent": 15.7,
      "bodyWaterInPercent": 61.6,
      "muscleMassInPercent": 44.5
    }
  ]
}

Status codes:

  • 200: Request could be processed successfully.
  • 400: The request is invalid or contains invalid data
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "bodyvaluesRead" scope enabled by us.

Health data and body values - Upload

The health data and body values endpoint takes an JSON-object with an array of bodyvalue objects to be processed in Tredict.
You can define as many values in the object as you need.

POST https://www.tredict.com/api/oauth/v2/bodyvalues

Request headers:

  • Authorization: Bearer ${access_token}
  • Content-Type: application/json

POST body example with possible fields:

timestamp and timezoneOffsetInSeconds are mandatory fields, all other fields are optional.

{
  "bodyvalues": [
    {
      "timestamp": "2021-07-18T06:00:00Z",
      "timezoneOffsetInSeconds": 7200, // GMT+2
      "restingHeartrate": 48,
      "hrRestDynamic": 55,
      "hrMaxDynamic": 144,
      "weightInKilograms": 68.2,
      "bodyHeightInCentimeter": 177.2,
      "bodyFatInPercent": 9.8,
      "bodyWaterInPercent": 63.2,
      "muscleMassInPercent": 45.6
    },
    {
      "timestamp": "2021-07-18T08:00:00Z",
      "timezoneOffsetInSeconds": 7200,
      "restingHeartrate": 50,
      "weightInKilograms": 69.2
    },
    {
      "timestamp": "2021-08-09T14:00:00Z",
      "timezoneOffsetInSeconds": 7200,
      "weightInKilograms": 70.2
    }
  ]
}

Entries are merged by timestamp. Posting an entry with the same timestamp as an existing revision updates the fields you send and keeps all other fields of that revision, so a field cannot be removed this way. An entry with a new timestamp creates a new revision. To correct a previous value, post it again with the exact timestamp returned by the download endpoint.

Status codes:

  • 200: Request could be processed successfully.
  • 400: The bodyvalues object is invalid or contains invalid data
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "bodyvaluesWrite" scope enabled by us.

Capacity values - Download

Download capacity values, like FTP or hrMax, in JSON format from this endpoint.

Each sport type has its own array of revisions, the newest revision is the currently active one. hrLth is the heart rate at the lactate threshold, ftp is in watts and ftpa is the functional threshold pace in seconds. Capacity values are read-only via the API.

GET https://www.tredict.com/api/oauth/v2/capacity

Request headers:

  • Authorization: Bearer ${access_token}
  • Content-Type: application/json

Request parameters:

Name Required Description
sportType No Filters the capacity object by the given single sportType. Possible values are 'running', 'cycling', 'swimming', 'misc'.

Response JSON object:

{
  "capacity": {
    "running": [{
      "timestamp": "2020-11-25T16:28:30.150Z",
      "timezoneOffset": 3600,
      "hrMax": 192,
      "hrLth": 166,
      "ftp": 235,
      "ftpa": 305
    }, {
      "timestamp": "2020-11-30T10:14:36.542Z",
      "timezoneOffset": 3600,
      "hrMax": 192,
      "hrLth": 168
    }],
    "cycling": [{
      "timestamp": "2019-12-05T11:52:47.000Z",
      "timezoneOffset": 3600,
      "hrMax": 191,
      "hrLth": 172,
      "ftp": 245,
      "ftpa": 92
    }, {
      "timestamp": "2020-12-02T15:11:54.738Z",
      "timezoneOffset": 3600,
      "ftp": 253,
      "ftpa": 91
    }],
    "swimming": [{
      "timestamp": "2020-01-19T22:45:06.695Z",
      "timezoneOffset": 3600,
      "hrMax": 179,
      "ftp": 246,
      "ftpa": 120
    }],
    "misc": [{
      "timestamp": "2020-06-02T23:55:10.099Z",
      "timezoneOffset": 7200,
      "hrMax": 191,
      "ftp": 246,
      "ftpa": 282
    }]
  }
}

Status codes:

  • 200: Request could be processed successfully.
  • 400: The request is invalid or contains invalid data
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "bodyvaluesRead" scope enabled by us.

HRV data - Download

Download HRV data, especially the "RMSSD Night" alongside the "baseline" from this endpoint.

It returns records in a hrv object with YYYYMMDD as date key and a tuple [RMSSD night, Baseline] as value.

The date key is the calendar day of the measurement in the local time where it was taken, not in the user's current timezone. For entries uploaded via the HRV upload endpoint, it is the date of the timestamp shifted by its timezoneOffsetInSeconds. An entry with the timestamp '2025-12-15T21:30:00.000Z' and a timezoneOffsetInSeconds of 32400 (GMT+9, 06:30 local time) is therefore listed under '20251216'.

GET https://www.tredict.com/api/oauth/v2/hrv

Request headers:

  • Authorization: Bearer ${access_token}
  • Content-Type: application/json

Request parameters:

Name Required Description
startDate No startDate sets the newest date of the hrv entries. If omitted, the list includes all entries up to the newest one. Must be an UTC ISO date string. F.ex. '2025-07-10T14:14:46.257Z'
endDate No endDate sets the oldest date of the hrv entries. If omitted, it defaults to 1 year back from startDate, or from now if startDate is omitted. Must be an UTC ISO date string. F.ex. '2024-07-10T14:14:46.257Z'

Response JSON object:

{
  "hrv": {
    "20240223": [60,72],
    "20240224": [72,72],
    "20240225": [72,72],
    "20240226": [74,72],
    "20240227": [66,73]
  },
  "filterStartDate": "2024-02-28T14:14:46.257Z",
  "filterEndDate": "2023-02-28T14:14:46.257Z",
}

Status codes:

  • 200: Request could be processed successfully.
  • 400: The request is invalid or contains invalid data
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "bodyvaluesRead" scope enabled by us.

HRV data - Upload

The HRV upload endpoint takes a JSON object with an array of HRV objects to be processed in Tredict.
You can define as many entries in the array as you need.

POST https://www.tredict.com/api/oauth/v2/hrv

Request headers:

  • Authorization: Bearer ${access_token}
  • Content-Type: application/json

POST body example with possible fields:

timestamp, timezoneOffsetInSeconds and rmssd are mandatory fields.

{
  "hrv": [
    {
      "timestamp": "2024-02-23T06:00:00Z",
      "timezoneOffsetInSeconds": 3600,
      "rmssd": 60
    },
    {
      "timestamp": "2024-02-24T06:00:00Z",
      "timezoneOffsetInSeconds": 3600,
      "rmssd": 72
    }
  ]
}
Name Required Description
timestamp Yes The revision timestamp of the HRV measurement. Must be a valid UTC ISO date string. If an entry with the same timestamp already exists, it will be updated.
timezoneOffsetInSeconds Yes The timezone offset in seconds. For example, 3600 for GMT+1.
rmssd Yes The RMSSD value (Root Mean Square of Successive Differences) in milliseconds. Must be between 1 and 500.

Status codes:

  • 200: Request could be processed successfully.
  • 400: The hrv object is invalid or contains invalid data
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "bodyvaluesWrite" scope enabled by us.

HRV data - Delete

Deletes all HRV entries that were uploaded via the HRV upload endpoint and whose timestamp lies within the given range.
HRV data from other sources, such as connected devices and services, is not affected.

Use this endpoint to clean up a faulty upload and upload the corrected entries afterwards. You do not need to know the original timestamp of the entries.

DELETE https://www.tredict.com/api/oauth/v2/hrv

Request headers:

  • Authorization: Bearer ${access_token}

Request parameters:

Name Required Description
startDate Yes One end of the range. Must be an UTC ISO date string. F.ex. '2025-10-01T00:00:00.000Z'
endDate Yes The other end of the range. Must be an UTC ISO date string. F.ex. '2026-10-11T00:00:00.000Z'. The order of startDate and endDate does not matter, both ends are inclusive.

The range refers to the uploaded timestamp, not to the date keys of the download endpoint.

Response JSON object:

{
  "deletedCount": 2
}

Status codes:

  • 200: Request could be processed successfully.
  • 400: startDate or endDate is missing or invalid
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "bodyvaluesWrite" scope enabled by us.

Sleep data - Download

Download sleep data, especially the "total sleep" alongside the "baseline" from this endpoint.

It returns records in a sleep object with YYYYMMDD as date key and a tuple [total sleep in seconds, Baseline] as value.

The date key is the day the night ended, in the local time where it was measured, not in the user's current timezone. For entries uploaded via the sleep upload endpoint, it is the date of the endTimestamp shifted by its timezoneOffsetInSeconds. A night with the endTimestamp '2025-12-15T21:30:00.000Z' and a timezoneOffsetInSeconds of 32400 (GMT+9, 06:30 local time) is therefore listed under '20251216'.

GET https://www.tredict.com/api/oauth/v2/sleep

Request headers:

  • Authorization: Bearer ${access_token}
  • Content-Type: application/json

Request parameters:

Name Required Description
startDate No startDate sets the newest date of the sleep entries. If omitted, the list includes all entries up to the newest one. Must be an UTC ISO date string. F.ex. '2025-07-10T14:14:46.257Z'
endDate No endDate sets the oldest date of the sleep entries. If omitted, it defaults to 1 year back from startDate, or from now if startDate is omitted. Must be an UTC ISO date string. F.ex. '2024-07-10T14:14:46.257Z'

Response JSON object:

{
  "sleep": {
    "20240223": [30000,32000],
    "20240224": [32000,32000],
    "20240225": [48000,32000],
    "20240226": [15000,32000],
    "20240227": [23000,32000]
  },
  "filterStartDate": "2024-02-28T14:14:46.257Z",
  "filterEndDate": "2023-02-28T14:14:46.257Z",
}

Status codes:

  • 200: Request could be processed successfully.
  • 400: The request is invalid or contains invalid data
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "bodyvaluesRead" scope enabled by us.

Sleep data - Upload

The sleep upload endpoint takes a JSON object with an array of sleep objects to be processed in Tredict.
You can define as many entries in the array as you need.

POST https://www.tredict.com/api/oauth/v2/sleep

Request headers:

  • Authorization: Bearer ${access_token}
  • Content-Type: application/json

POST body example with possible fields:

startTimestamp, endTimestamp, timezoneOffsetInSeconds and sleepDurationInSeconds are mandatory fields.

{
  "sleep": [
    {
      "startTimestamp": "2024-02-23T23:00:00Z",
      "endTimestamp": "2024-02-24T07:00:00Z",
      "timezoneOffsetInSeconds": 3600,
      "sleepDurationInSeconds": 27000
    },
    {
      "startTimestamp": "2024-02-24T22:30:00Z",
      "endTimestamp": "2024-02-25T06:30:00Z",
      "timezoneOffsetInSeconds": 3600,
      "sleepDurationInSeconds": 25200
    }
  ]
}
Name Required Description
startTimestamp Yes The start time of the sleep period. Must be a valid UTC ISO date string. If an entry with the same startTimestamp already exists, it will be updated.
endTimestamp Yes The end time of the sleep period. Must be a valid UTC ISO date string and must be after startTimestamp.
timezoneOffsetInSeconds Yes The timezone offset in seconds. For example, 3600 for GMT+1.
sleepDurationInSeconds Yes The total sleep duration in seconds. This is the actual time spent sleeping (excluding awake time during the sleep period). Must be between 1 and 86400.

Status codes:

  • 200: Request could be processed successfully.
  • 400: The sleep object is invalid or contains invalid data
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "bodyvaluesWrite" scope enabled by us.

Sleep data - Delete

Deletes all sleep entries that were uploaded via the sleep upload endpoint and whose endTimestamp lies within the given range.
Sleep data from other sources, such as connected devices and services, is not affected.

Use this endpoint to clean up a faulty upload and upload the corrected entries afterwards. You do not need to know the original startTimestamp of the entries.

DELETE https://www.tredict.com/api/oauth/v2/sleep

Request headers:

  • Authorization: Bearer ${access_token}

Request parameters:

Name Required Description
startDate Yes One end of the range. Must be an UTC ISO date string. F.ex. '2025-10-01T00:00:00.000Z'
endDate Yes The other end of the range. Must be an UTC ISO date string. F.ex. '2026-10-11T00:00:00.000Z'. The order of startDate and endDate does not matter, both ends are inclusive.

The range refers to the uploaded endTimestamp, not to the date keys of the download endpoint.

Response JSON object:

{
  "deletedCount": 2
}

Status codes:

  • 200: Request could be processed successfully.
  • 400: startDate or endDate is missing or invalid
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "bodyvaluesWrite" scope enabled by us.

Zones - Download

Download the user defined zones in JSON format from this endpoint.

Each sport type has the zone groups heartrate, pace, power and cadence. Each group is keyed by revision timestamps, and each timestamp maps to an array of zones with name, from, to and intensity (0 low, 1 medium, 2 high). A from or to of -1 marks an open bound. Zones are read-only via the API.

GET https://www.tredict.com/api/oauth/v2/zones

Request headers:

  • Authorization: Bearer ${access_token}
  • Content-Type: application/json

Request parameters:

Name Required Description
sportType No Filters the zones object by the given single sportType. Possible values are 'running', 'cycling', 'swimming', 'misc'.

Response JSON object:

{
  "zones": {
    "running": {
      "heartrate": {
        '2022-10-10T08:11:04.229Z': [{
            "name": 'Relaxed',
            "from": -1,
            "to": 135,
            "intensity": 0
          },
          {
            "name": 'Extensive endurance',
            "from": 136,
            "to": 151,
            "intensity": 0
          },
          {
            "name": 'Avoidance I',
            "from": 152,
            "to": 160,
            "intensity": 1
          },
          {
            "name": 'Intensive endurance',
            "from": 161,
            "to": 166,
            "intensity": 1
          },
          {
            "name": 'Lactate threshold',
            "from": 167,
            "to": 173,
            "intensity": 2
          },
          {
            "name": 'Avoidance II',
            "from": 174,
            "to": 179,
            "intensity": 2
          },
          {
            "name": 'Oxygen capacity',
            "from": 180,
            "to": -1,
            "intensity": 2
          }
        ]
      },
      "pace": {},
      "power": {},
      "cadence": {}
    },
    "cycling": {
      "heartrate": {},
      "pace": {},
      "power": {
        '2021-09-13T19:38:43.000Z': [{
            "name": 'Sehr leicht',
            "from": -1,
            "to": 148,
            "intensity": 0
          },
          {
            "name": 'Leicht',
            "from": 149,
            "to": 167,
            "intensity": 0
          },
          {
            "name": 'Ausdauer',
            "from": 168,
            "to": 188,
            "intensity": 1
          },
          {
            "name": 'Schwellwert',
            "from": 189,
            "to": 209,
            "intensity": 1
          },
          {
            "name": 'Intervalle',
            "from": 210,
            "to": 251,
            "intensity": 2
          },
          {
            "name": 'Kraft und Effizienz',
            "from": 252,
            "to": -1,
            "intensity": 2
          }
        ],
        '2023-01-09T14:35:53.000Z': [{
            "name": 'Very easy',
            "from": -1,
            "to": 166,
            "intensity": 0
          },
          {
            "name": 'Easy',
            "from": 167,
            "to": 187,
            "intensity": 0
          },
          {
            "name": 'Endurance',
            "from": 188,
            "to": 211,
            "intensity": 0
          },
          {
            "name": 'Threshold',
            "from": 212,
            "to": 234,
            "intensity": 1
          },
          {
            "name": 'Intervals',
            "from": 235,
            "to": 281,
            "intensity": 2
          },
          {
            "name": 'Power and efficiency',
            "from": 282,
            "to": -1,
            "intensity": 2
          }
        ]
      },
      "cadence": {}
    },
    "swimming": {
      "heartrate": {},
      "pace": {},
      "power": {},
      "cadence": {}
    },
    "misc": {
      "heartrate": {},
      "pace": {},
      "power": {},
      "cadence": {}
    }
  }
}

Status codes:

  • 200: Request could be processed successfully.
  • 400: The request is invalid or contains invalid data
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "bodyvaluesRead" scope enabled by us.

Equipment list - Download

You can fetch the full list of the user's equipment (bikes, shoes, swimming gear, misc).

GET https://www.tredict.com/api/oauth/v2/equipmentList

Request headers:

  • Authorization: Bearer ${access_token}
  • Accept: application/json;charset=UTF-8

Response JSON object:

{
  "equipment": [
    {
      "id": "suuid-string",
      "type": "bikes",
      "name": "Trek Domane",
      "usageRange": {
        "start": "2023-01-01T00:00:00.000Z",
        "end": null
      },
      "weight": "8200",
      "notes": "Carbon road bike",
      "previousDistance": 0,
      "trainingIds": ["trainingId1", "trainingId2"],
      "count": 25,
      "duration": 18900,
      "distance": 487.5,
      "distanceAvg": 19.5,
      "distanceMax": 42.2,
      "paceAvg": 358,
      "paceMax": 253,
      "cadenceAvg": 85,
      "cadenceMax": 110,
      "heartrateAvg": 142,
      "heartrateMax": 182,
      "powerAvg": 192,
      "powerMax": 310,
      "effortAvg": 72
    }
  ]
}

Equipment types are bikes, shoes, swimming and misc.
Depending on the type, additional fields may be present (e.g. size, drop for shoes, itemtype for swimming and misc).
The aggregated stats (count, duration, distance, etc.) are computed from the assigned trainings.

Status codes:

  • 200: Request could be processed successfully.
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "activityRead" scope enabled by us.

Plan - Create

Create a reusable training plan in the user's 'My Own Training Plans' category.

POST https://www.tredict.com/api/oauth/v2/plan

Request headers:

  • Authorization: Bearer ${access_token}
  • Content-Type: application/json

POST body example:

{
  "plan": {
    "title": "Marathon Training Plan",
    "description": "A 12-week marathon training plan for intermediate runners",
    "categories": ["base", "building"],
    "targetgroups": ["intermediate"],
    "zonetypes": ["heartrate", "pace"],
    "language": "en"
  },
  "planTrainings": [
    {
      "day": 1,
      "time": 1020, // Minutes from midnight
      "structuredWorkout": {
        "title": "Easy Run",
        "notes": "Start with an easy 30-minute run",
        "trainingType": "planned",
        "sportType": "running",
        "subSportType": "generic",
        "steps": [
          {
            "note": "Warm up",
            "intensityType": "warmup",
            "duration": 600,
            "durationType": "time",
            "targetZoneType": "heartrate",
            "targets": {
              "hrMax": {
                "from": 0.6,
                "to": 0.7
              }
            }
          },
          {
            "note": "Main set",
            "intensityType": "active",
            "duration": 1200,
            "durationType": "time",
            "targetZoneType": "heartrate",
            "targets": {
              "hrMax": {
                "from": 0.7,
                "to": 0.8
              }
            }
          }
        ]
      }
    },
    {
      "day": 2,
      "structuredWorkout": {
        "title": "Rest Day",
        "notes": "Complete rest day - no training",
        "trainingType": "note",
        "sportType": "misc",
        "subSportType": "rest_day"
      }
    },
    {
      "day": 3,
      "time": 1020,
      "structuredWorkout": {
        "title": "Interval Training",
        "notes": "4x800m intervals with recovery",
        "trainingType": "planned",
        "sportType": "running",
        "subSportType": "generic",
        "steps": [
          {
            "note": "Warm up",
            "intensityType": "warmup",
            "duration": 900,
            "durationType": "time",
            "targetZoneType": "heartrate",
            "targets": {
              "hrMax": {"from": 0.6, "to": 0.7}
            }
          },
          {
            "repetitions": 4,
            "steps": [
              {
                "note": "800m fast",
                "intensityType": "active",
                "distance": 800,
                "durationType": "distance",
                "targetZoneType": "heartrate",
                "targets": {
                  "hrMax": {"from": 0.85, "to": 0.92}
                }
              },
              {
                "note": "400m recovery",
                "intensityType": "recover",
                "distance": 400,
                "durationType": "distance",
                "targetZoneType": "heartrate",
                "targets": {
                  "hrMax": {"from": 0.6, "to": 0.7}
                }
              }
            ]
          },
          {
            "note": "Cool down",
            "intensityType": "cooldown",
            "duration": 600,
            "durationType": "time",
            "targetZoneType": "heartrate",
            "targets": {
              "hrMax": {"from": 0.6, "to": 0.7}
            }
          }
        ]
      }
    }
  ]
}

Request parameters in plan object:

The plan object contains the main plan metadata:

Name Required Description
title Yes Expressive title of the plan (5-255 characters)
description Yes Detailed description of the training plan (10-10240 characters)
categories Yes Array of training principle categories. Possible values: 'general', 'base', 'building', 'intensity', 'race_specific', 'race_phase', 'recovery', 'special'
targetgroups Yes Array of performance levels. Possible values: 'general', 'beginner', 'intermediate', 'profi'
zonetypes Yes Array of zone target types. Possible values: 'heartrate', 'power', 'pace'
language Yes Language code for default titles. Possible values: 'de', 'en'

Request parameters in planTrainings array:

The optional planTrainings array contains structured workouts for the plan:

Name Required Description
day Yes The day number the training should happen (1-1024). The plan starts at day 1.
time No Time of day in minutes from midnight (0-1439). Defaults to 1020 (5pm).
structuredWorkout Yes Object containing the workout structure (see below for details)

Structured Workout Types:

There are two types of structured workouts:

1. Note Workout (for notes or rest days):

{
  "title": "Rest Day",
  "notes": "Optional description",
  "trainingType": "note",
  "sportType": "misc",
  "subSportType": "rest_day" // "rest_day" or "note"
}

2. Planned Workout (for actual training sessions):

{
  "title": "Interval Training",
  "notes": "Optional description",
  "trainingType": "planned",
  "sportType": "running",
  "subSportType": "generic",
  "poolLength": 50, // Only for swimming with lap_swimming
  "steps": [...] // Array of workout steps
}

Workout Steps:

Steps can be either Base Steps or Repetition Steps:

Base Step:

{
  "note": "Warm up",
  "intensityType": "warmup",
  "duration": 600, // seconds 
  "distance": 1000, // meters
  "durationType": "time", // "time", "distance", "open" or "repetitions" (strength training only)
  "progressionType": "steady", // "steady" or "ramp"
  "targetMode": "range", // "range" or "padding"
  "targetZoneType": "heartrate", // "cadence", "heartrate", "pace", "power"
  "targets": {    
    "hrMax": {"from": 0.6, "to": 0.8}, // for targetMode "range"
    "hrLth": {"from": 0.8, "to": 0.9}, // for targetMode "range", instead of hrMax
    "ftp": {"from": 0.6, "to": 0.8}, // for targetMode "range"
    "ftpa": {"from": 0.6, "to": 0.8}, // for targetMode "range"
    "cadence": {"value": 165, "padding": 10}, // for targetMode "padding"
    "heartrate": {"value": 140, "padding": 10}, // for targetMode "padding"
    "pace": {"value": 300, "padding": 10}, // for targetMode "padding"
    "power": {"value": 170, "padding": 10} // for targetMode "padding"
  }
}

Repetition Step:

{
  "repetitions": 4,
  "steps": [...] // Array of base steps to repeat
}

Strength Training Step (only with sportType "misc" and subSportType "strength_training"):

{
  "intensityType": "active",
  "durationType": "repetitions", // The step ends after exerciseRepetitions
  "exerciseRepetitions": 10,
  "duration": 40, // Estimated duration of the set in seconds, optional
  "exerciseCategory": "bench_press",
  "exerciseName": "barbell_bench_press",
  "exerciseWeight": 60, // Kilograms, omit for body weight exercises
  "targets": { "heartrate": { "value": 120 } } // Expected average heart rate of the set, see below
}

Several sets of an exercise are a Repetition Step with the number of sets as repetitions, containing the exercise step and a step with the intensityType rest. Exercises also work with the durationType time, f.ex. for a plank, or open, f.ex. for push-ups until failure. Without a duration Tredict estimates 3 seconds per repetition.

Give each set targets.heartrate with the expected average heart rate of the set in bpm as value, without targetMode, targetZoneType and padding. This creates no target zone on the watch, it is the only basis for the effort Tredict calculates for the set. A set without it adds no effort. Leave the rests between the sets without targets.

exerciseCategory and exerciseName are the snake_case names of the FIT profile types exercise_category and <category>_exercise_name of the Garmin FIT SDK 21.205, f.ex. squat and barbell_back_squat. The Garmin spelling like SQUAT or _3_WAY_CALF_RAISE is accepted as well. exerciseName is optional, but needs its exerciseCategory.

The training is rejected with an error message in these cases

  • The category or the exercise name is unknown. The message lists similar names of the category.
  • A rest step carries an exercise, repetitions, a weight or the durationType repetitions.
  • The durationType repetitions comes without exerciseRepetitions, or exerciseRepetitions without the durationType repetitions.
  • exerciseRepetitions is not a whole number above 0, or exerciseWeight is not above 0 and at most 655.34 kilograms.

In all other sports the exercise fields are ignored and the durationType repetitions falls back to open.

Supported Sport Types and Sub Types:

  • running: generic, treadmill, street, trail, orienteering, track, obstacle, running_abc, transition, virtual_activity
  • cycling: generic, virtual_activity, spin, indoor_cycling, road, mountain, gravel_cycling, downhill, bmx, recumbent, hand_cycling, track_cycling, commuting
  • swimming: generic, open_water, lap_swimming, virtual_activity
  • misc: generic, virtual_activity, walking, speed_walking, hiking, strength_training, core_training, pilates, flexibility_training, yoga, cardio_training, jump_rope, elliptical, backcountry, cross_country_skiing, roller_skiing, indoor_skiing, alpine_skiing, snowboarding, mountaineering, rock_climbing, ice_climbing, indoor_rowing, rowing, kayaking, paddling, stand_up_paddleboarding, surfing, windsurfing, kitesurfing, inline_skating, ice_skating, stair_climbing, sailing, golf, horseback_riding, volleyball, basketball, handball, soccer, american_football, tennis, badminton, squash

Intensity Types: warmup, active, recover, rest, cooldown, misc

Target Zone Types: cadence, heartrate, pace, power

Target Modes:

  • range: Uses relative percentages of capacity values (ftp, ftpa, hrMax or hrLth). A step can contain either hrMax or hrLth, not both.
  • padding: Uses absolute values with optional padding
Name Required Description
title Yes Expressive title of the plan (5-255 characters)
description Yes Detailed description of the training plan (10-10240 characters)
categories Yes Array of training principle categories. Possible values: 'general', 'base', 'building', 'intensity', 'race_specific', 'race_phase', 'recovery', 'special'
targetgroups Yes Array of performance levels. Possible values: 'general', 'beginner', 'intermediate', 'profi'
zonetypes Yes Array of zone target types. Possible values: 'heartrate', 'power', 'pace'
language Yes Language code for default titles. Possible values: 'de', 'en'

Status codes:

  • 200: Request could be processed successfully. Returns a JSON object with the planId.
  • 400: The plan object is invalid or contains invalid data
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Response JSON object on success:

{
  "planId": "ABC1234567"
}

Note: In order to make request on this endpoint, your client credentials needs to have the "activityWrite" scope enabled by us.

Plan - Add Training

Add a structured workout or training session to an existing plan.

If you like to add trainings to an existing plan more atomically later on, use can use this endpoint, instead of create the plan with all trainings at once. It is up to you, but you loose idempotency here. :-)

POST https://www.tredict.com/api/oauth/v2/plan/training

Request headers:

  • Authorization: Bearer ${access_token}
  • Content-Type: application/json

POST body example:

{
  "planId": "ABC1234567",
  "planTraining": {
    "day": 5,
    "time": 1020,
    "structuredWorkout": {
      "title": "Long Run",
      "notes": "90-minute endurance run at easy pace",
      "trainingType": "planned",
      "sportType": "running",
      "subSportType": "generic",
      "steps": [
        {
          "note": "Easy endurance run",
          "intensityType": "active",
          "duration": 5400,
          "durationType": "time",
          "targetZoneType": "heartrate",
          "targets": {
            "hrMax": {
              "from": 0.65,
              "to": 0.75
            }
          }
        }
      ]
    }
  }
}

Request parameters:

Name Required Description
planId Yes The ID of the plan to add the training to (received from Plan Create endpoint)
planTraining Yes Object containing the training structure (same format as planTrainings in Plan Create)

The planTraining object uses the same structure as described in the Plan Create endpoint documentation, including:

  • day: Day number (1-1024)
  • time: Time of day in minutes from midnight (optional, defaults to 1020/5pm)
  • structuredWorkout: Workout structure with title, notes, trainingType, sportType, etc.
  • Workout steps can be base steps or repetition steps as documented above

Status codes:

  • 200: Request could be processed successfully.
  • 400: The planTraining object is invalid or contains invalid data
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired or it is out of scope.
  • 404: Plan not found or invalid planId.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Note: In order to make request on this endpoint, your client credentials needs to have the "activityWrite" scope enabled by us.

Deregistration

Please call the deregistration endpoint if the user wishes to disconnect via your application. Access token and refresh token of the according user will be deleted then.

If the user deregisters via our application, you can safely assume a deregistration by a refresh token request that returns 403.

DELETE https://www.tredict.com/user/oauth/v2/token

Request headers:

  • Authorization: Bearer ${access_token}

Status codes:

  • 200: Request could be processed successfully.
  • 401: Invalid authorization header.
  • 403: You provided an invalid access token. Maybe it is expired.
  • 429: Too many requests.
  • 500: Something went wrong on our side.
  • 503: Sorry, we went to the pub.

Rate limits

⚠️ Important: Some endpoints are rate limited by IP address. Please ensure that you have installed a proper retry or queue management on your side to handle rate limits gracefully.

  • Activity upload endpoint: Maximum of 8 requests per second.
  • Activity download endpoint: Maximum of 10 requests per second.
  • Token endpoint: Maximum of 20 requests per second.

If you exceed the rate limit, the API will return a 429 Too Many Requests status code. In this case, you should wait before retrying the request.

Security notes

When working with OAuth2 and the Tredict API, it's important to follow security best practices:

  • Store client credentials securely: Ensure that your client ID and client secret are stored securely and are not exposed in your application code or version control system.
  • Use HTTPS: Always use HTTPS for all requests to the Tredict API to ensure that data is encrypted in transit.
  • Handle tokens securely: Access tokens and refresh tokens should be stored securely and not exposed in URLs or logs.
  • Validate redirects: Always validate the state parameter in the redirect URL to ensure that the authorization response is intended for your application.
  • Monitor for abuse: Monitor your application for unusual activity that could indicate abuse or unauthorized access.

Garmin attribution

Activities that Tredict received through the Garmin API carry isFromGarminApi: true and an attribution string with the Garmin device model, f.ex. "Garmin epix gen2", or simply "Garmin" if the model is unknown.

If you display the data of such an activity, or results derived from it, you must show this attribution visibly.

Media & Logos

For the visual integration on your side, you can find the Tredict logo and other media files on the media page.

LLMs & Coding Assistants

Tredict provides machine-readable descriptions of the API for LLMs and coding assistants:

  • llms.txt - Overview with links to all available resources.

The Guides & Skills listed above are also optimized for LLM consumption.

If you have any further questions, do not hesitate to contact us.


composed at 1/29/2026, 11:28:39 AM by Felix Gertz