NAV Navbar
shell

Introduction

Welcome to the Wahoo Fitness OAuth2 API! You can use our API to access Wahoo Fitness OAuth2 API endpoints, which can get information on your user profile and workout history stored in our cloud.

We have language bindings in Shell! You can view code examples in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right.

Data Model

This diagram shows how the data records are related within the API

UserWorkout SummaryWorkout

Registration

Create a user account and register your app in our developer portal (https://developers.wahooligan.com). Your app can be set to 'Sandbox' or 'Production'. With 'Sandbox' apps you will be able to test and see your changes immediately but with very limited throughput. Production apps will have to go through a review process with Wahoo Fitness, and when approved your app can be released with a higher rate limit.

Rate Limiting

The following chart shows how your app will be rate limited. If you require a higher throughput, please contact support to request an increase.

Interval Sandbox Apps Production Apps
Requests every 5 Min: 25 200
Requests per Hour: 100 1000
Requests per Day: 250 5000

Authentication

Workflow

OAuth2 Uses the following authentication workflow. The goal is to obtain an access_token for a user that can be used for accessing Wahoo's API on the user's behalf.

Step 1. Send the user to Wahoo in order to login and grant access to your app using the following url:

https://<base_url>/oauth/authorize?client_id=<client_id>&redirect_uri=<redirect_uri>&scope=<scopes>&response_type=code

Step 2. After successful authorization the user will be redirected back to your redirect_uri with a code.

<redirect_uri>?code=<code>

Step 3. Send an HTTP POST to Wahoo with the code and your app's OAuth2 credentials and receive the access_token and refresh_token

https://<base_url>/oauth/token?client_secret=<client_secret>&code=<code>&redirect_uri=<redirect_uri>&grant_type=authorization_code&client_id=<client_id>

Step 4. When the access_token expires you can use the refresh_token to get a new access_token

https://<base_url>/oauth/token?client_secret=<client_secret>&client_id=<client_id>&grant_type=refresh_token&refresh_token=<refresh_token>

Step 5. You are now ready to send requests to our API by putting the access_token within the following HTTP Header:

"Authorization": "Bearer <access_token>"

Attribute Notes
base_url: api.wahooligan.com
client_id: You can get your app's client_id from the developer portal.
client_secret: You can get your app's client_secret from the developer portal.
redirect_uri: You can set the redirect_uri for your app in the developer portal.
scopes: See Authorization Scopes
access_token: Returned in Step 3; Used for API calls to view/manage user data.
refresh_token: Returned in Step 3; Used to refresh the access_token.
expires_in: Returned in Step 3; Indicates when the access_token will expire.

Authorization

# With shell, you can append the requested scopes as a space separated list of values
curl "<base_url>/oauth/authorize?scope=user_read+user_write"
  -H "Authorization: Bearer <Token>"

Applications must only request permission for resources they intend to access or modify. You can choose which permission scopes your app will request either by setting the scopes for your app in the developer portal or by sending a space separated list of scopes as a parameter in the oauth/authorize request.

We support the following OAuth 2.0 permission scopes:

Scope Description
email Access your email address
user_read Access your user data
user_write Update your user data
workouts_read Access your workout history
workouts_write Update your workout history
offline_data Receive data even when the app is closed

Deauthorize

# With shell, you can revoke access to an application
curl -X "DELETE" "<base_url>/v1/permissions"
  -H "Authorization: Bearer <Token>"

Application access can be revoked by sending a request to delete all permissions.

HTTP Request

DELETE https://api.wahooligan.com/v1/permissions

Users

Applications can get and update the attributes of the authenticated user.

Get Authenticated User

Requires user_read scope

curl --header "Authorization: Bearer users-token-goes-here" https://api.wahooligan.com/v1/user

Sample Response:

{
  "id": 60462,
  "height": "2.0",
  "weight": "80.0",
  "first": "Bob",
  "last": "Smith",
  "email": "sample@test-domain.com",
  "birth": "1980-10-02",
  "gender": 1,
  "created_at": "2018-10-23T15:38:23.000Z",
  "updated_at": "2018-10-24T20:46:40.000Z"
}

Returns the authenticated user along with email if email scope is included.

HTTP Request

GET https://api.wahooligan.com/v1/user

Update User

Requires the user_write scope

curl --header "Authorization: Bearer users-token-goes-here"
     -X PUT -d user[gender]=1 https://api.wahooligan.com/v1/user

Sample Response:

{
  "id": 60462,
  "height": "2.0",
  "weight": "80.0",
  "first": "Bob",
  "last": "Smith",
  "email": "bob.s@wahoofitness.com",
  "birth": "1980-10-02",
  "gender": 1,
  "created_at": "2018-10-23T15:38:23.000Z",
  "updated_at": "2018-10-24T20:46:40.000Z"
}

Updates the current authenticated user.

HTTP Request

PUT https://api.wahooligan.com/v1/user

Parameters

Parameter Type Required Description
user[email] String no Well formed email address, must be unique in the system
user[first] String no First name
user[last] String no Last name
user[height] Decimal no Height in meters
user[weight] Decimal no Weight in kilograms
user[birth ] Date no Date of birth formatted as YYYY-MM-DD
user[gender] 0,1,2,3 no Use 0 for male, 1 for female, 2 for other, and 3 for prefer not to say

Workouts

Create a Workout

Requires the workouts_write scope

Creates a workout for the authenticated user.

curl --header "Authorization: Bearer 414c6a44a5f8b918830b370db05"  -X POST
  -d 'workout[name]="Friday fun"&
      workout[workout_token]=123&
      workout[workout_type_id]=40&
      workout[starts]=2015-08-12T09:00:00.000Z&
      workout[minutes]=12'  api.lvh.me:3000/v1/workouts

Sample Response:

{
    "id": 56519,
    "starts": "2015-08-12T09:00:00.000Z",
    "minutes": 12,
    "name": "Friday Fun",
    "created_at": "2018-10-23T20:41:55.000Z",
    "updated_at": "2018-10-23T20:41:55.000Z",
    "plan_id": null,
    "workout_token": "123",
    "workout_type_id": 40
}

HTTP Request

POST https://api.wahooligan.com/v1/workouts

Parameters

Parameter Type Required Default Description
workout[name] String yes The name of the workout
workout[workout_type_id] Number yes The type of the workout - Workout Types
workout[starts] Time yes Start time
workout[minutes] Number yes Duration of the workout in minutes
workout[workout_token] String no Can be used by the application to identify the workout
workout[plan_id] Number no null Id of the plan used in this workout
workout[workout_summary] Object no Include summary results - Workout Summary

Get a Workout

Requires the workouts_read scope

Returns a workout for the current authenticated user.

curl --header "Authorization: Bearer users-token-goes-here" https://api.wahooligan.com/v1/workouts/56519

Sample Response:

{
  "id": 56519,
  "starts": "2015-08-12T09:00:00.000Z",
  "minutes": 12,
  "name": "Friday Fun",
  "plan_id": null,
  "workout_token": "123",
  "workout_type_id": 40,
  "workout_summary": null,
  "created_at": "2018-10-23T20:41:55.000Z",
  "updated_at": "2018-10-23T20:41:55.000Z"
}

HTTP Request

GET https://api.wahooligan.com/v1/workouts/:id

Update a Workout

Requires the workouts_write scope

Updates a workout for the authenticated user.

curl --header "Authorization: Bearer 414c6a44a5f8b918830b370db05" -X PUT
  -d workout[name]="Friday afternoon" api.lvh.me:3000/v1/workouts/56519

Sample Response:

{
  "id": 56519,
  "starts": "2015-08-12T09:00:00.000Z",
  "minutes": 12,
  "name": "Friday afternoon",
  "plan_id": null,
  "workout_token": "123",
  "workout_type_id": 40,
  "workout_summary": null,
  "created_at": "2018-10-23T20:41:55.000Z",
  "updated_at": "2018-10-23T20:41:55.000Z"
}

HTTP Request

POST http://api.wahooligan.com/cloud-api/workouts

Query Parameters

Parameter Type Required Description
workout[name] String no The name of the workout
workout[workout_token] String no Can be used by the application to identify the workout
workout[starts] Time no Start time
workout[minutes] Number no Duration of the workout in minutes
workout[plan_id] Number no Id of the plan used in this workout
workout[workout_type_id] Number no The type of the workout - Workout Types
workout[workout_summary] Object no Include summary results - Workout Summary

Get all Workouts

Requires the workouts_read scope

Returns all workouts for the authenticated user. The workout are always sorted by the starts field in descending order. By default the most recent 30 workouts are returned. The per_page parameter can be used to adjust the number of workouts returned.

In the response the total attribute is the total number of workouts for the current user.

curl --header "Authorization: Bearer users-token-goes-here" https://api.wahooligan.com/v1/workouts

Sample Response:

{
  "workouts": [
    {
      "id": 56519,
      "starts": "2015-08-12T09:00:00.000Z",
      "minutes": 12,
      "name": "Friday afternoon",
      "plan_id": null,
      "workout_token": "123",
      "workout_type_id": 40,
      "workout_summary": null,
      "created_at": "2018-10-23T20:41:55.000Z",
      "updated_at": "2018-10-23T20:41:55.000Z"
    }
  ],
  "total": 1,
  "page": 1,
  "per_page": 30,
  "order": "descending",
  "sort": "starts"
}

HTTP Request

GET http://api.wahooligan.com/v1/workouts

Query Parameters

Parameter Type Required Default Description
page integer no 1 Used for pagination.
per_page integer no 30 Limits the number of workouts returned.

Delete a Workout

Requires the workouts_write scope

Deletes a workout that is owned by the authenticated user.

curl -X DELETE --header "Authorization: Bearer users-token-goes-here" https://api.wahooligan.com/v1/workouts/:id

HTTP Request

DELETE https://api.wahooligan.com/v1/workouts/:id

Workout Summaries

Workout Summary records contain the results of a workout.

Get a Workout Summary

Requires the workouts_read scope

Returns the workout summary object for a workout. When a workout is created, the workout summary will be empty until it is updated.

curl --header "Authorization: Bearer users-token-goes-here" https://api.wahooligan.com/v1/workouts/:id/workout_summary

Sample Response:

{
    "id": 8297,
    "ascent_accum": 450.00,
    "calories_accum": 1500.00,
    "cadence_avg": 50.00,
    "distance_accum": 24909.71,
    "duration_active_accum": 179,
    "duration_paused_accum": 95,
    "duration_total_accum": 275,
    "heart_rate_avg": 124,
    "power_avg": 94.59,
    "power_bike_np_last": 150,
    "power_bike_tss_last": 304.90,
    "speed_avg": 10.75,
    "work_accum": 104148000,
    "file": {
        "url": "https://wahoo-cloud-web.s3.amazonaws.com/development/uploads/workout_file/file/EjA4DJCoIaG-f2fB2MLLLg/4_Mile_Segment_.fit"
    },
    "created_at": "2018-10-23T20:43:50.000Z",
    "updated_at": "2018-10-23T20:43:50.000Z"
}

HTTP Request

GET https://api.wahooligan.com/v1/workouts/:id/workout_summary

Create a Workout Summary

Requires the workouts_write scope

Creates a workout summary and associates it with a workout. If a workout summary already exists for the workout then the workout summary is updated.

curl --header "Authorization: Bearer 414c6a44a5f8b918830b370db05" -X POST
  -d workout_summary[data]="Friday afternoon workout" https://api.wahooligan.com/v1/workouts/56519/workout_summary

Sample Response:

{
    "id": 8297,
    "ascent_accum": 450.00,
    "cadence_avg": 50.00,
    "calories_accum": 1500.00,
    "distance_accum": 24909.71,
    "duration_active_accum": 179,
    "duration_paused_accum": 95,
    "duration_total_accum": 275,
    "heart_rate_avg": 124,
    "power_bike_np_last": 150,
    "power_bike_tss_last": 304.90,
    "power_avg": 94.59,
    "speed_avg": 10.75,
    "work_accum": 104148000,
    "created_at": "2018-10-23T20:43:50.000Z",
    "updated_at": "2018-10-23T20:43:50.000Z",
    "file": {
        "url": "https://wahoo-cloud-web.s3.amazonaws.com/development/uploads/workout_file/file/EjA4DJCoIaG-f2fB2MLLLg/4_Mile_Segment_.fit"
    }
}

HTTP Request

POST https://api.wahooligan.com/v1/workouts/:id/workout_summary

Parameters

Parameter Type Description
workout_summary[ascent_accum] decimal Ascent in meters
workout_summary[cadence_avg] number Average rotations per minute
workout_summary[calories_accum] decimal Calories (kCal)
workout_summary[distance_accum] decimal Meters
workout_summary[duration_active_accum] number Seconds
workout_summary[duration_paused_accum] number Seconds
workout_summary[duration_total_accum] number Seconds
workout_summary[heart_rate_avg] number bpm
workout_summary[power_avg] decimal Watts
workout_summary[power_bike_np_last] number Watts
workout_summary[power_bike_tss_last] decimal unitless
workout_summary[speed_avg] decimal Meters/Sec
workout_summary[work_accum] number joules
workout_summary[file] File Fit file

Webhooks

Receive API calls from Wahoo to your webhook url.

If we do not receive an HTTP 200 response code we will retry the api call on the following retry schedule:

  1. After 30 minutes
  2. After 4 hours
  3. After 24 hours
  4. After 72 hours

API Calls will be sent via HTTP POST with the HTTP header Content-Type: application/json

Settings

Setting Notes
webhook_enabled: Toggle the webhook api calls on/off.
webhook_url: Receive an HTTP POST with a JSON payload to this url.
webhook_token: Specify a token that you would like us to send. Any request that doesn't include this token should be ignored.

Workout Summary

Requires the offline_data scope

Sample Workout Summary Webhook Message

{
  "event_type": "workout_summary",
  "webhook_token": <webhook_token>,
  "user": {
    "id": 60462
  },
  "workout_summary": {
    "id": 8297,
    "ascent_accum": 450.00,
    "cadence_avg": 50.00,
    "calories_accum": 1500.00,
    "distance_accum": 24909.71,
    "duration_active_accum": 179,
    "duration_paused_accum": 95,
    "duration_total_accum": 275,
    "heart_rate_avg": 124,
    "power_bike_np_last": 150,
    "power_bike_tss_last": 304.90,
    "power_avg": 94.59,
    "speed_avg": 10.75,
    "work_accum": 104148000,
    "created_at": "2018-10-23T20:43:50.000Z",
    "updated_at": "2018-10-23T20:43:50.000Z",
    "file": {
      "url": "https://wahoo-cloud-web.s3.amazonaws.com/development/uploads/workout_file/file/EjA4DJCoIaG-f2fB2MLLLg/4_Mile_Segment_.fit"
    },
    "workout": {
      "id": 56519,
      "starts": "2015-08-12T09:00:00.000Z",
      "minutes": 12,
      "name": "Friday Fun",
      "created_at": "2018-10-23T20:41:55.000Z",
      "updated_at": "2018-10-23T20:41:55.000Z",
      "plan_id": null,
      "workout_token": "123",
      "workout_type_id": 40
    }
  }     
}

Workout Types

The Wahoo Fitness API supports the following workout types:

Id Description
0 BIKING
1 RUNNING
2 FE
3 RUNNING_TRACK
4 RUNNING_TRAIL
5 RUNNING_TREADMILL
6 WALKING
7 WALKING_SPEED
8 WALKING_NORDIC
56 WALKING_TREADMILL
9 HIKING
10 MOUNTAINEERING
11 BIKING_CYCLECROSS
12 BIKING_INDOOR
61 BIKING_INDOOR_TRAINER
13 BIKING_MOUNTAIN
14 BIKING_RECUMBENT
15 BIKING_ROAD
16 BIKING_TRACK
17 BIKING_MOTOCYCLING
49 BIKING_INDOOR_CYCLING_CLASS
18 FE_GENERAL
19 FE_TREADMILL
20 FE_ELLIPTICAL
21 FE_BIKE
22 FE_ROWER
23 FE_CLIMBER
57 FE_STEPPER
58 FE_STEP_MILL
59 FE_TREAD_CLIMBER
60 FE_TOTAL_BODY
24 SWIMMING
25 SWIMMING_LAP
26 SWIMMING_OPEN_WATER
27 SNOWBOARDING
28 SKIING
29 SKIING_DOWNHILL
30 SKIINGCROSS_COUNTRY
31 SKATING
32 SKATING_ICE
33 SKATING_INLINE
34 LONG_BOARDING
35 SAILING
36 WINDSURFING
37 CANOEING
38 KAYAKING
39 ROWING
40 KITEBOARDING
41 STAND_UP_PADDLE_BOARD
42 WORKOUT
43 CARDIO_CLASS
44 STAIR_CLIMBER
45 WHEELCHAIR
46 GOLFING
47 OTHER

Errors

The Wahoo Fitness API uses the following error codes:

Code Meaning
400 Bad Request -- Your request is invalid.
401 Unauthorized -- Your API key is wrong.
403 Forbidden -- You do not have access to the specified resource.
404 Not Found -- The specified resource could not be found.
405 Method Not Allowed -- You tried to access a resource with an invalid method.
406 Not Acceptable -- You requested a format that isn't json.
410 Gone -- The resource requested has been removed from our servers.
422 Unprocessable Entity -- One or more parameters supplied are missing or invalid or your app is not approved.
429 Too Many Requests -- You are sending too many requests in short period of time.
500 Internal Server Error -- We had a problem with our server. Try again later.
503 Service Unavailable -- We're temporarily offline for maintenance. Please try again later.