Xhale Athlete API documentation

Introduction

Welcome to the API

The Xhale API is a REST web service and all responses are in JSON.

Request examples below are written for curl for experimenting on the command line, but you can of course use any language. Send your data with the Content-Type: application/x-www-form-urlencoded header.

All requests should be made over HTTPS. Calls made with plain HTTP may be redirected to the secure protocol, but you should not rely on this.

Accessing the API

The Xhale API is a work in progress. If you would like to use it, please contact us at support@trainxhale.com so we can work with you to improve it for your requirements. Modifications to the API will occur in the future and while you can consider the API stable with a long lifetime before anything will break, it is still important that we can keep you informed and provide advise on updating your code.

To gain access to the API and receive your access credentials, please email our support@trainxhale.com address.

Two versions

The API is written for two audiences, either a single user or a coaching company. There is some crossover, but coaching companies have access to different data than a single user. This section is for a single user and requires each user to authenticate separately.

Authentication

Authentication to our API is carried out using the OAuth 2.0 protocol which will give you an access token to use when accessing the below endpoints. For more details please read the Xhale OAuth documentation.

Throttling

We currently have quite relaxed throttling so you probably won't encounter any problems, but if you encounter a limit please contact us. You will get a 429 error if you hit a limit with API requests, in which case you should ease off your requests a little.

Errors and responses

Status codes

200 - OK
Standard successful response.
201 - Created
The resource has been created.
204 - No Content
Returned after a sucessful deletion.
400 - Bad request
An error occurred to do with the request.
401 - Unauthorized
A valid authentication token was not provided. Your access token may have expired, in which case you should make use of the user's refresh token.
403 - Forbidden
Authenticated but you do not have access to this resource.
415 - Unsupported Media Type
You've uploaded a file type we don't recognise.
429 - Too Many Requests
You've reached a limit on API usage. See the body of the response for an explanation.
500 - Internal Server Error
There is some kind of error on Xhale's servers.

Users

Get current user

Show details for the currently authenticated user

Example Request

curl -X GET https://trainxhale.com/api/users/current/ \
    -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
        

Example Response

{
  "id":5,
  "email":"mentor@example.com",
  "season_dates":["2020-11-01","2021-10-31"],
  "seasons":[["2020-11-01","2021-10-31"]],
  "discipline_options": [
    { "category": "swim", "id": 1, "title": "Swim Tech" },
    { "category": "swim", "id": 2, "title": "Swim Intervals" },
    { "category": "swim", "id": 3, "title": "Swim Endurance" },
    { "category": "bike", "id": 4, "title": "Bike Intervals" },
    { "category": "bike", "id": 5, "title": "Turbo Intervals" },
    { "category": "bike", "id": 6, "title": "Bike Endurance" },
    { "category": "run", "id": 7, "title": "Run Intervals" },
    { "category": "run", "id": 8, "title": "Run Endurance" },
    { "category": "run", "id": 9, "title": "Run Tempo" },
    { "category": "brick", "id": 10, "title": "Brick Intervals" },
    { "category": "brick", "id": 11, "title": "Brick Endurance" },
    { "category": "gym", "id": 12, "title": "Gym Core/Specifics" },
    { "category": "gym", "id": 13, "title": "Gym Lifting" },
    { "category": "rest", "id": 14, "title": "Rest day" },
    { "category": "other", "id": 15, "title": "Other" },
    { "category": "diary", "id": 17, "title": "Diary" }
  ],
  "distance_unit_preference":"kilometers",
  "pool_unit_preference":"meters",
  "can_create_future_sessions":true,
  "first_name":"Mentor",
  "last_name":"User",
  "beta_access_list":[ "beta_feature_name" ],
  "diary_type":"mentor",
  "account_type":"coached",
  "zones": {
    "run": {
      "max_heart_rate":180,
      "zones":[
        {
          "name": "Easy",
          "number":1,
          "heart_rate_min": 117,
          "heart_rate_max":134,
          "power_min":null,
          "power_max":null,
          "meters_per_second_fastest":3.4602076124567476,
          "meters_per_second_slowest":3.3557046979865772
        },
        {
          "name":"Steady",
          "number":2,
          "heart_rate_min":135,
          "heart_rate_max":147,
          "power_min":null,
          "power_max":null,
          "meters_per_second_fastest":3.7037037037037033,
          "meters_per_second_slowest":3.4722222222222223
        },
        {
          "name":"Moderate",
          "number":3,
          "heart_rate_min":148,
          "heart_rate_max":159,
          "power_min":null,
          "power_max":null,
          "meters_per_second_fastest":3.9682539682539684,
          "meters_per_second_slowest":3.717472118959108
        },
        {
          "name":"Hard",
          "number":4,
          "heart_rate_min":160,
          "heart_rate_max":168,
          "power_min":null,
          "power_max":null,
          "meters_per_second_fastest":4.291845493562231,
          "meters_per_second_slowest":3.9840637450199203
        },
        {
          "name":"All out",
          "number":5,
          "heart_rate_min":169,
          "heart_rate_max":180,
          "power_min":null,
          "power_max":null,
          "meters_per_second_fastest":5.649717514124294,
          "meters_per_second_slowest":4.310344827586206
        }
      ]},
    "bike":{
      "max_heart_rate":175,
      "zones":[
        {
          "name":"Easy",
          "number":1,
          "heart_rate_min":114,
          "heart_rate_max":130,
          "power_min":175,
          "power_max":190
        },
        {
          "name":"Steady",
          "number":2,
          "heart_rate_min":131,
          "heart_rate_max":143,
          "power_min":191,
          "power_max":210
        },
        {
          "name":"Moderate",
          "number":3,
          "heart_rate_min":144,
          "heart_rate_max":155,
          "power_min":211,
          "power_max":240
        },
        {
          "name":"Hard",
          "number":4,
          "heart_rate_min":156,
          "heart_rate_max":164,
          "power_min":241,
          "power_max":275
        },
        {
          "name":"All out",
          "number":5,
          "heart_rate_min":165,
          "heart_rate_max":175,
          "power_min":276,
          "power_max":350
        }
      ]
    }
  }
}

        

Training Sessions

The training session object

discipline_category can be one of 'swim', 'bike', 'run', 'brick', 'gym', 'other', 'rest', 'diary'

List sessions

List all training sessions for the current season for the authenticated user.

GET Parameters:

parameter value type example value
updated_since_datetime ISO 8601 datetime string 2015-06-17T03:00:00
start_date ISO 8601 date string 2015-06-17
end_date ISO 8601 date string 2016-06-16

Example Request

curl -X GET https://trainxhale.com/api/sessions/?updated_since_datetime=2015-06-17T03%3A00%3A00 \
    -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
        

Example Response

[
  {
    "id":4,
    "athlete":5,
    "date":"2014-01-10",
    "order":"2",
    "datetime_added":"2015-06-18T02:03:14.513292",
    "datetime_updated":"2015-06-18T02:03:14.513292",
    "session_plan_editable": true,
    "brief_description":"",
    "deleted": false,
    "discipline_id":4,
    "discipline_title":"Bike Intervals",
    "discipline_category":"bike",
    "intensity":5,
    "prescribed_minutes":70,
    "prescribed_minutes_swim":null,
    "prescribed_minutes_bike":null,
    "prescribed_minutes_run":null,
    "prescribed_km":1.5,
    "prescribed_km_swim":null,
    "prescribed_km_bike":null,
    "prescribed_km_run":null,
    "completed_minutes":70,
    "completed_minutes_swim":null,
    "completed_minutes_bike":null,
    "completed_minutes_run":null,
    "completed_km":1.5,
    "completed_km_swim":null,
    "completed_km_bike":null,
    "completed_km_run":null,
    "training_plan":"Lorem ipsum dolor sit amet.",
    "athlete_feedback":"Lorem ipsum dolor sit amet.",
    "coach_comments":"Lorem ipsum dolor sit amet."
    "bike_average_speed_kph":null,
    "bike_average_power_watts":null,
    "bike_average_cadence_rpm":null,
    "bike_average_heart_rate_bpm":null,
    "bike_meters_climbed":null,
    "run_average_heart_rate_bpm":null,
    "run_meters_climbed":null,
    "missed_session":false,
    "key_session":false,
    "has_uploaded_training": false,
    "url":"https://trainxhale.com/session/4/",
    "direct_access_token":"a65249d688c78e0654a9d78726187c83"
  }
],
[
  {
    "id":5,
    "athlete":5,
    "date":"2014-01-10",
    "datetime_added":"2015-06-18T02:03:14.513292",
    "datetime_updated":"2015-06-18T02:03:14.513292",
    "session_plan_editable": true,
    "brief_description":"",
    "deleted": false,
    "discipline_id":4,
    "discipline_title":"Bike Intervals",
    "discipline_category":"bike",
    "intensity":5,
    "prescribed_minutes":70,
    "prescribed_minutes_swim":null,
    "prescribed_minutes_bike":null,
    "prescribed_minutes_run":null,
    "prescribed_km":1.5,
    "prescribed_km_swim":null,
    "prescribed_km_bike":null,
    "prescribed_km_run":null,
    "completed_minutes":70,
    "completed_minutes_swim":null,
    "completed_minutes_bike":null,
    "completed_minutes_run":null,
    "completed_km":1.5,
    "completed_km_swim":null,
    "completed_km_bike":null,
    "completed_km_run":null,
    "training_plan":"Lorem ipsum dolor sit amet.",
    "athlete_feedback":"Lorem ipsum dolor sit amet.",
    "coach_comments":"Lorem ipsum dolor sit amet."
    "bike_average_speed_kph":16.8,
    "bike_average_power_watts":260,
    "bike_average_cadence_rpm":88,
    "bike_average_heart_rate_bpm":162,
    "bike_meters_climbed": 1225,
    "run_average_heart_rate_bpm":166,
    "run_meters_climbed":null,
    "missed_session":false,
    "key_session":false,
    "has_uploaded_training": false,
    "url":"https://trainxhale.com/session/5/",
    "direct_access_token":"dc3656494250eed5c3644be8094f342c"
  }
]
        

Retrieve a session

Retreive a single training session

Example Request

curl -X GET https://trainxhale.com/api/sessions/63/ \
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
        

Example Response

{
    "id":4,
    "athlete":5,
    "date":"2014-01-10",
    "datetime_added":"2015-06-18T02:03:14.513292",
    "datetime_updated":"2015-06-18T02:03:14.513292",
    "session_plan_editable": true,
    "deleted": false,
    "discipline_id":4,
    "discipline_title":"Bike Intervals",
    "discipline_category":"bike",
    "brief_description":"",
    "intensity":5,
    "prescribed_minutes":70,
    "prescribed_minutes_swim":null,
    "prescribed_minutes_bike":null,
    "prescribed_minutes_run":null,
    "prescribed_km":1.5,
    "prescribed_km_swim":null,
    "prescribed_km_bike":null,
    "prescribed_km_run":null,
    "completed_minutes":70,
    "completed_minutes_swim":null,
    "completed_minutes_bike":null,
    "completed_minutes_run":null,
    "completed_km":1.5,
    "completed_km_swim":null,
    "completed_km_bike":null,
    "completed_km_run":null,
    "training_plan":"Lorem ipsum dolor sit amet.",
    "athlete_feedback":"Lorem ipsum dolor sit amet.",
    "coach_comments":"Lorem ipsum dolor sit amet."
    "bike_average_speed_kph":16.8,
    "bike_average_power_watts":260,
    "bike_average_cadence_rpm":88,
    "bike_average_heart_rate_bpm":162,
    "bike_meters_climbed": 1225,
    "run_average_heart_rate_bpm":166,
    "run_meters_climbed": 1225,
    "missed_session":false,
    "has_uploaded_training":false,
    "key_session":false
    "url":"https://trainxhale.com/session/53/",
    "direct_access_token":"e2da0ec3f86f71f61a13964f698e9705"
}
        

Create a session

Create a new training session

parameter value type example value
date ISO 8601 date string 2015-06-17 required field
order integer 2 The order value determines which order the training appears on a given day. If not set the order will automatically be set higher than any other session on this day. This means it should appear as the day's last session.
discipline_id integer 24288 required field. Session plan field.
intensity integer 4 An integer between 0 and 5
brief_description string Skiing Relevant for diary and other training disciplines. Session plan field.
session_plan_editable boolean True Read only. Can session plan fields be edited?
deleted boolean False Read only. If true this session has been deleted and should be removed from local copies.
key_session boolean False Session plan field
training_plan string Session plan field
prescribed_minutes integer Session plan field
prescribed_minutes_swim integer Session plan field
prescribed_minutes_bike integer Session plan field
prescribed_minutes_run integer Session plan field
prescribed_km float Session plan field
prescribed_km_swim float Session plan field
prescribed_km_bike float Session plan field
prescribed_km_run float Session plan field
training_log_files_to_add list of integers A list of training file IDs to add to this training session
direct_access_token string dc3656494250eed5c3644be8094f342c A token that allows access to session charts page when logged out. E.g. https://trainxhale.com/session/5/?direct_access_token=dc3656494250eed5c3644be8094f342c

Example Request

curl -X POST https://trainxhale.com/api/sessions/
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
  -d date='2015-01-14'
  -d intensity=3
  -d discipline_id=6
        

Update a session

Update an existing training session

Use PATCH if you don't want to update required fields. Otherwise you can use PUT which will demand you resend the required fields.

Example Request

curl -X PATCH https://trainxhale.com/api/sessions/44/ \
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
  -d discipline_id=6
        

Delete a session

Successful deletion responds with 204 No Content

Example Request

curl -X DELETE https://trainxhale.com/api/sessions/44/ \
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
        

Races

List races

List all races for the current season for the authenticated user.

GET Parameters:

parameter value type example value
updated_since_datetime ISO 8601 datetime string 2015-06-17T03:00:00
start_date ISO 8601 date string 2015-06-17
end_date ISO 8601 date string 2016-06-16

Example Request

curl -X GET https://trainxhale.com/api/races/?updated_since_datetime=2015-06-17T03%3A00%3A00 \
    -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
        

Example Response

[
  {
    "id":2,
    "athlete":5,
    "date":"2014-01-31",
    "deleted": false,
    "datetime_added":"2015-06-20T22:46:28.696444",
    "datetime_updated":"2015-06-20T22:47:18.129069",
    "race_name":"Triathlon",
    "distance":3,
    "taper_days":5,
    "attendance_probability":1,
    "athletes_plan": "Lorem",
    "coach_plan":"",
    "athlete_feedback":"Lorem",
    "coach_comments":"",
    "event_url":"http://example.com",
    "position_overall":15,
    "position_age_group":3,
    "race_priority":"b",
    "has_uploaded_training": false
  }
]
        

Retrieve a race

Retreive a single race

Example Request

curl -X GET https://trainxhale.com/api/races/2/ \
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
        

Example Response

{
  "id":2,
  "athlete":5,
  "date":"2014-01-31",
  "deleted": false,
  "datetime_added":"2015-06-20T22:46:28.696444",
  "datetime_updated":"2015-06-20T22:47:18.129069",
  "race_name":"Triathlon",
  "distance":3,
  "taper_days":5,
  "attendance_probability":1,
  "athletes_plan": "Lorem",
  "coach_plan":"",
  "athlete_feedback":"Lorem",
  "coach_comments":"",
  "event_url":"http://example.com",
  "position_overall":15,
  "position_age_group":3,
  "race_priority":"b",
  "has_uploaded_training": false
}
        

Create a race

Create a new race

date and race_name are required fields

Example Request

curl -X POST https://trainxhale.com/api/races/
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
  -d date='2015-01-14'
  -d race_name='Rubicon 70.3'
        

Update a race

Update an existing race

Use PATCH if you don't want to update required fields. Otherwise you can use PUT which will demand you resend the required fields.

Example Request

curl -X PATCH https://trainxhale.com/api/races/12/ \
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
  -d taper_days=5
        

Delete a race

Successful deletion responds with 204 No Content

Example Request

curl -X DELETE https://trainxhale.com/api/races/12/ \
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
        

Training files

Upload training files

Any file that can be uploaded manually to Xhale can also be accepted via the API. However, it is preferable to use FIT activity files and failing that TCX as these are the best supported.

To use this endpoint, you will need to authorise access with the 'upload' and 'write' scopes during the OAuth 2.0 flow.

Example Request

curl -X POST https://trainxhale.com/api/uploads/
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
  -F file=@/path/to/file.fit
        

Responses

If the file is created we will respond with status code 201. A status code of 200 means an identical file has already been uploaded. 415 is returned when we receive files we don't recognise the file type of. 403 means the user has not been authorised with the correct scopes (you need write+upload). 500 errors are a problem on our part. We should be automatically informed of these problems and work on a fix, but if you see a lot please contact us at our support@trainxhale.com email address.

Delete training file

Successful deletion responds with 204 No Content

Example Request

curl -X DELETE https://trainxhale.com/api/uploads/4332/
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
        

List training files that need importing

Example Request

curl -X GET https://trainxhale.com/api/uploads/pending/
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
        

Add training files to training sessions or races

To add a training file to a session or a race, use the endpoints above for saving training sessions and races, and include the training_log_files_to_add field. The distance/time completed data will be updated in the training session so make sure to refresh this data after saving.

Example Request

curl -X PATCH https://trainxhale.com/api/sessions/44/ \
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
  -d training_log_files_to_add=632
        

Planned workouts

Download planned workouts

Planned workouts are structured training plans which can be understood by devices such as a bike computer and can be used to control workout equipment such as a smart bike trainer.

List workouts

To obtain the file data for the planned workouts, first fetch a list of planned workouts to see what is available. You can then use this data to fetch the workouts themselves.

Due to limitations in what type of workouts the ZWO format supports, you should filter for that file type if that is the format you are downloading. Users need to have FTP set in their profile and it needs to be a cycling workout.

You can filter the list with the following parameters:

parameter value type example value
sport string cycling
updated_since_datetime ISO 8601 datetime string 2015-06-17T03:00:00
scheduled_date_earliest ISO 8601 date string 2015-06-17
scheduled_date_latest ISO 8601 date string 2016-06-16
file_type string zwo

Example Request

curl -X GET -G https://trainxhale.com/api/workouts/
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
  -d scheduled_date_earliest='2018-08-17'
  -d scheduled_date_latest='2018-09-30'
  -d sport='cycling'
  -d updated_since_datetime='2018-09-01T12:12:00'
        

Example Response

[
  {
    "workout_id":328,
    "sport":"cycling",
    "last_updated_datetime":"2018-09-02T10:11:27.623172",
    "training_sessions":[
      {
        "id":8923323,
        "date":"2120-07-11",
        "name":"Bike Endurance"
      }
    ],
  }
]
        

Download workout

Workout file data is either FIT or ZWO file formats (ask if you are interested in ERG). To obtain the file, you will need the workout_id and the training session it is attached to. Use this to construct the URL /api/sessions/{training_session_id}/workouts/{workout_id}.fit (or .zwo) and perform a GET request for the data.

Example Request

curl -X GET https://trainxhale.com/api/sessions/8923323/workouts/328.fit
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
  --output workout328.fit
        

Messaging

Send private messages between coach and athletes

Messaging allows athletes and coaches to chat with one another.

List contacts

Get a list of people that are available to contact over the app. For coaches this will be a list of athletes and for athletes this will be their coach if they have one.

Example Request

curl -X GET -G https://trainxhale.com/api/contacts/
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
        

Example Response

{
  "id": 3225,
  "first_name": "Jack",
  "last_name": "White",
  "unseen_messages_count": 5
}
        

List messages for contact

List all messages between user and contact with this ID.

Example Request

curl -X GET -G https://trainxhale.com/api/contacts/3/messages/
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
        

Example Response

{
  "id": 3225,
  "content": "The contents of the message"
  "sender_user_id": 3242,
  "recipient_user_id": 5222,
  "datetime_sent":"2020-06-18T02:03:14.513292",
  "datetime_updated":"2020-06-19T09:04:14.523192",
  "datetime_seen":"2020-06-18T11:14:13.523299",
}
        

Get message

Example Request

curl -X GET -G https://trainxhale.com/api/contacts/3/messages/17/
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
        

Send a new message

Example Request

curl -X GET -G https://trainxhale.com/api/contacts/3/messages/
  -H 'Authorization: Bearer db015b7280da9ebc7c0c4fc48a5d6a2f14780ecf'
  -d content='the content of the message'