Xert Online API Version 1.5

Changes to 1.5:

- Added OAuth Authorization Code flow for third-party applications

Changes to 1.3:

- Added new Training and Fitness Info endpoint

Changes to 1.2:

- Added Progression and Training Status to Activity Details

Changes to 1.1:

- Added Session, Images, Previous Signature and Medal return values to Activity Details

This document specifies the Oauth and REST methods to use to perform the following functions:

  1. Obtain and refreshing a user authorization token

  2. Authorizing a third-party application via the OAuth Authorization Code flow

  3. Getting a list of workouts available for non-authenticated user

  4. Getting a list of workouts for an authenticated user

  5. Retrieving a workout definition

  6. Uploading a FIT file

Example curl messages are exemplary calls that should be followed. Any questions should be sent to support@xertonline.com.

Token authentication

Obtaining token

curl -u xert_public:xert_public -POST "https://www.xertonline.com/oauth/token" -d 'grant_type=password' -d 'username=AzureDiamond' -d 'password=Hunter2' ~> {"access_token":"ee88cf29984d4fe205ca574ec1edd01ae67d74d2","expires_in":604800,"token_type":"Bearer","scope":"basic","refresh_token":"1badfdee0f72b847dc91d1baf9e5c095c774c14a"}

Refreshing token

curl -u xert_public:xert_public -POST "https://www.xertonline.com/oauth/token" -d 'grant_type=refresh_token' -d 'refresh_token=1badfdee0f72b847dc91d1baf9e5c095c774c14a' ~> {"access_token":"7d3fc2e8cb80caffd881f08e764e1b507168cce4","expires_in":604800,"token_type":"Bearer","scope":"basic","refresh_token":"da61ee326213aa9676c6e26faab41050b847bcc3"}

OAuth Authorization Code Flow (Third-Party Applications)

Third-party applications should use the Authorization Code flow: the user authenticates on xertonline.com itself and your application never sees their Xert credentials.

Step 1: Register your application

Client credentials are issued by Xert and are not currently available via self-service. Email support@xertonline.com with your application name and the redirect URI(s) you want to use, and we will issue you a client_id and client_secret.

Step 2: Redirect the user to Xert to authorize your application

Send the user’s browser to the authorize endpoint with the following query parameters:

GET https://www.xertonline.com/oauth/authorize?client_id=your_client_id&redirect_uri=https://yourapp.example.com/callback&response_type=code&state=RANDOM_OPAQUE_STRING

If the user is not already logged in to Xert, they will be prompted to log in first. They are then shown a consent screen naming your application and the data it is requesting access to (Activities, Workouts and Performance data – Xert currently issues a single basic scope covering all of these). If they approve, Xert redirects back to your redirect_uri with an authorization code:

https://yourapp.example.com/callback?code=AUTHORIZATION_CODE&state=RANDOM_OPAQUE_STRING

Step 3: Exchange the code for an access token

curl -u your_client_id:your_client_secret -POST "https://www.xertonline.com/oauth/token" -d 'grant_type=authorization_code' -d 'code=AUTHORIZATION_CODE' -d 'redirect_uri=https://yourapp.example.com/callback' ~> {"access_token":"ee88cf29984d4fe205ca574ec1edd01ae67d74d2","expires_in":604800,"token_type":"Bearer","scope":"basic","refresh_token":"1badfdee0f72b847dc91d1baf9e5c095c774c14a"}

The resulting access_token and refresh_token are used exactly as described above under Token authentication – use the Authorization: Bearer header on subsequent API calls, and the refresh_token grant to obtain a new access token once it expires.

Getting User Information

Retrieve User's Training, Fitness and Status Information

Obtain a user’s current fitness and training information.

curl -X GET "https://www.xertonline.com/oauth/training_info?format=zwo" -H "Authorization: Bearer 7d3fc2e8cb80caffd881f08e764e1b507168cce4"

curl -X get "https://www.xertonline.com/oauth/workout-download/vovdxww5i7fzqbun.zwo" -H "Authorization: Bearer 7d3fc2e8cb80caffd881f08e764e1b507168cce4"

Attributes

Sample Response

success:

Boolean
Indicates that the request is successful/unsuccessful.

signature:

Object
The user’s current fitness signature containing values for ftp, ltp, hie and pp representing Threshold Power, Lower Threshold Power, High Intensity Energy and Peak Power respectively.

status:

String
The user’s current training status: Detraining, Very fresh, Fresh, Tired, Very tired representing the user’s current freshness status

weight:

Number
The user’s weight in kilograms.

tl:

Object
The user’s current training loads containing values for low, high, peak, total representing the training load for each individual system and the total.

targetXSS:

Object
The user’s current training targets containing values for low, high, peak, total representing the training targets recommended by the system for each system and the total.

source:

String

The source of training information.

wotd:

Object
The user’s workout-of-the-day information:

 

type:

Type of workout-of-the-day

None, Forecast, Scheduled representing No workout-of-the-day, workout-of-the-day was assigned to a Forecast AI training placeholder, workout-of-the-day was a planned workout.

 

name:

Name of the workout

 

workoutId:

Id of the workout

 

description:

Description text

 

difficulty:

Difficulty score

 

url:

URL of the workout file for the workout-of-the-day. Note that the URL will need to be requested with the user’s token.

Parameters

format:

String | optional

Value is erg or zwo and indicates the format of the workout file to be returned. If not provided, no workout file will be returned.

{
"success": true,
"weight": 81,
"status": "Fresh",
"signature": {
    "ltp": 243.9623680703351,
    "ftp": 314.6014550178407,
    "hie": 28.25563477900224,
    "pp": 1251.5005813659977
    },
"tl": {
    "low": 123.32987160482055,
    "high": 6.563550862763262,
    "peak": 1.8767393717439016,
    "total": 131.7701618393277
    },
"source": "Workout",
"targetXSS": {
    "low": 74.8839053229011,
    "high": 0,
    "peak": 0,
    "total": 74.8839053229011
    },
"wotd": {
    "name": "AI Generated Workout",
    "workoutId": "vovdxww5i7fzqbun",
    "description": "AI generated workout powered by Xert\n\nThis has 3 declining sets starting with 2 x 07:53 at 242W with 00:00 rest in-between.",
    "type": "Forecast",
    "difficulty": 2.155537419064349,
    "url": "https://www.xertonline.com/oauth/workout-download/vovdxww5i7fzqbun.zwo"
    }
}

 

Getting workouts

List default workouts - no authentication required.

curl -X GET "https://www.xertonline.com/oauth/workout" ~> {"success":true,"workouts":[{"path":"MGLq16v2O6vrILBi","name":"Default Workout","description":"","last_modified":1457118394}]}⏎ 

List a user's workouts.

curl -X GET "https://www.xertonline.com/oauth/workouts" -H "Authorization: Bearer 7d3fc2e8cb80caffd881f08e764e1b507168cce4" ~> {"success":true,"workouts":[{"path":"0GWegmcHSK6JKHth","name":"workout1","description":"Workout description","last_modified":1456263137},{"path":"ISm75NAmocJ7eUHr","name":"This is a workout","description":"Description goes here.","last_modified":1457025192}]}⏎ 

Retrieve a workout

Resolving a workout requires a specific user's fitness signature to calculate power values in watts, as well as interval durations, so a user's token is needed. Presently, the workout itself does not need to belong to the user whose token and signature are used.

The workout is specified by the path parameter given when obtaining a list of workouts. The parameter is added onto the request url.

curl -X GET "https://www.xertonline.com/oauth/workout/ISm75NAmocJ7eUHr" -H "Authorization: Bearer 7d3fc2e8cb80caffd881f08e764e1b507168cce4" ~>  {"success":true,"name":"test workout","description":"Description goes here","workout":[{"name":"Warmup","index":0,"power":213,"duration":300,"interval_count":1}, {"name":"Intervals","index":1,"power":557.5,"duration":48.106355492065,"power_rest":0,"duration_rest":20.821684998231,"interval_count":1}, {"name":"Intervals","index":1,"power":557.5,"duration":13.206337103223,"power_rest":0,"duration_rest":20.821684998231,"interval_count":31}, {"name":"Cooldown","index":2,"power":213,"duration":300,"interval_count":1}]}⏎

The output's "workout" field is an array where each entry represents a set of similar intervals. Each set specifies the name of the interval to which it belongs, its index in the workout designer, as well as the target power in watts and set duration in seconds for both the work and rest interval. As in the above example, one set in the workout designer may be split into multiple set entries in this api call, depending on how mpa and time based intervals get resolved.

Getting Activities

Retrieve User's Activity.

The activity is specified by the path parameter given when obtaining activity details. The parameter is added onto the request url.

curl -X GET "https://www.xertonline.com/oauth/activity/MGLq16v2O6vrILBi?include_session_data=1" -H "Authorization: Bearer 7d3fc2e8cb80caffd881f08e764e1b507168cce4"

Attributes Sample Response
success: Boolean
Indicates that the request is successful.
name: Integer
Name of the activity.
description: String
Description of the activity.
session_data: Object
Per second MPA data object.
summary: Object
Summary details of activity including XSS, XLSS, XHSS, XPSS, XEP, MEP, TWS, SP, SFD, Focus, Specificity, Activity Type, Activity Date, Signature, Difficulty Rating, Distance (km), Duration (minutes) Difficulty, Total grams of Carbs, Total grams of Fat and Street View Image
Parameters
id/path: String | required
The identifier of the activity.
include_session_data: Integer | optional
Values are 1 and 0, default is 0. Set to 1 to include session_data value from the response.

{
"success": true,
"name": "Sample Title",
"description": "Sample Description",
"session_data": [
 {
"power": 37.4,
"unix_time": 1501327986000,
"mpa": 2433.9170179125,
"cad": null,
"alt": 169,
"hr": null,
"spd": 39636,
"tgt": null,
"lat": 43.622447289526,
"lng": -79.792798189446,
"dist": 42605.14,
"tws": 147.53,
"xds": 84.483105789006
},
{
"power": 33.8,
"unix_time": 1501327987000,
"mpa": 2434.0872751323,
"cad": null,
"alt": 169,
"hr": null,
"spd": 39132,
"tgt": null,
"lat": 43.62251887098,
"lng": -79.792894916609,
"dist": 42616.01,
"tws": 147.53,
"xds": 84.444223240528
},
],
"summary":{
"session": {
"max_power": 765,
"avg_power": 234.37876486,
"max_cadence": 99,
"total_elevation_gain": 974,
"total_calories": 3903
},
"xss": 552.19146965358,
"xlss": 465.43228697047,
"xhss": 78.156195599284,
"xpss": 8.602987083827,
"xep": 200.59242389133,
"focus": "Rouleur",
"mep": 173,
"tws": 507736.33294669,
"sp": 0.51640347500268,
"sfd": 398.82093884511,
"specificity": "Mixed",
"difficulty": 256.78248819058,
"difficulty_rating": "5 - Hard",
"distance":30,
"duration":3600
"sig": {
"ftp": 250,
"atc": 41139,
"pp": 1447
},
"medal": 2,
"breakthrough": 1,
"prev_sig": {
"ftp": 300.48137188449,
"atc": 27284.630970804,
"pp": 1361.5247761747,
"ltp": 232.26979445748
},
"activity_type": "Cycling",
"start_date": {
"date": "2018-09-22 09:54:21.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"distance": 93.0061,
"duration": 13209,
"total_grams_carbs": 248.78449606004,
"total_grams_fat": 120.94966162224,
"progression": {
"date": "2018-09-22 09:54:20",
"tl": {
"ftp": 92.706161948713,
"hie": 5.4803348960987,
"pp": 1.5145020294862
},
"rl": {
"ftp": 57.114808191115,
"hie": 4.5317592024292,
"pp": 0.93325134033198
},
"form": 37.121180140421
},
"training_status": 3.7057428249799,
"freshness": "Fresh",
"street_view": "https://xertonline.com/assets/images/maps/street_view/user-2/aortnwffwgptcxfy.png",
"activity_map": "https://xertonline.com/assets/images/maps/activity_map_aortnwffwgptcxfy.png",
"chart_view": "https://xertonline.com/assets/images/charts/aortnwffwgptcxfy"
}

Retrieve User's Activity List.

Activity List are specified by a Date Range, "FROM" and "TO". The parameter is added onto the request url.

curl -X GET "https://www.xertonline.com/oauth/activity?from=1483230813&to=1506731613" -H "Authorization: Bearer 7d3fc2e8cb80caffd881f08e764e1b507168cce4"

Attributes Sample Response
success: Boolean
Indicates that the request is successful.
activities: Object
Activity details including Name, Description, Activity Type, Start Date and Activity Path/ID
Parameters
from: Integer, Timestamp | required
A timestamp where the filtering of activities will Start.
to: Integer, Timestamp | required
A timestamp where the filtering of activities will End.
updated_from: Integer, Timestamp | optional
A timestamp where the filtering of activities will Start based on Updated Time.
{
"success": true,
"activities": [
 {
"name": "Morning Run",
"start_date": {
"date": "2017-08-12 11:08:29.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"description": "Lorem Imposum",
"path": "4j9o4hhg5zrl0pah",
"activity_type": "Run"
},
{
"name": "Evening Ride",
"start_date": {
"date": "2017-08-12 11:08:29.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"description": "Lorem Imposum",
"path": "4j9o4hhg5zrl0pah",
"activity_type": "Cycling"
}
]
}



Uploading .FIT file

The .FIT file needs to be posted to xertonline as multipart/form-data.

The 'name' parameter is optional, and sets the name of the activity. If omitted, the name of the .FIT file will be used.

curl -POST "https://www.xertonline.com/oauth/upload" -H "Authorization: Bearer 7d3fc2e8cb80caffd881f08e764e1b507168cce4" -F 'file=@workout.fit' -F 'name=Morning Ride' ~> {"success":true,"json":{"files":[{"name":"workout.fit","size":55358,"type":"application\/octet-stream","url":"\/activities\/JOWCIbZWuvuLQoE7","deleteType":"DELETE","deleteUrl":"\/activities\/JOWCIbZWuvuLQoE7"}]}}⏎ 

The "json" field of the response is mostly meaningless and will be removed later.