Intervals.icu API Integration Cookbook

This tells you everything you need to know to get completed activities into Intervals.icu, to download planned workouts and to read and update wellness data. Example calls use curl so you can easily try them out.

Sources:

Authorization

Apps need to use an access_token obtained via OAuth. If you are just working with your own data call the API using your personal API key (available towards the bottom of the settings page) and basic authentication.

Uploading completed activities

This is done by POSTing multipart/form-data. Example using an access_token from OAuth (ACTIVITY:WRITE scope is required):

$ curl -F file=@i55610271.fit \
    'https://intervals.icu/api/v1/athlete/0/activities?name=Foo&description=Bar' \
    -H 'Authorization: Bearer d842c1fc25f241e5ae440d09756448a9'

Same example using a personal API key:

$ curl -F file=@i55610271.fit \
    'https://intervals.icu/api/v1/athlete/0/activities?name=Foo&description=Bar' \
    -u API_KEY:6dzmsans11bsyifjxchr87oum

The name and description URL parameters are optional. The file can be fit, tcx or gpx or a zip or gz of the same. There is also an optional external_id parameter which you can set to your own activity ID. You can configure an activity URL template for your application and Intervals.icu will use this and the external_id to link back to your application from the activity pages.

Note that the athlete id in the path is ā€˜0ā€™. This indicates that the athlete ID that the access_token or API key belongs to should be used.

Downloading planned workouts

You can download planned workouts from an athleteā€™s calendar in zwo (Zwift), fit, mrc and erg format (CALENDAR:READ scope required):

$ curl 'https://intervals.icu/api/v1/athlete/0/events?category=WORKOUT&ext=zwo&resolve=true' \
    -H 'Authorization: Bearer d842c1fc25f241e5ae440d09756448a9'

The optional category parameter specifies what sort of events to return (planned workouts in this case). The ext parameter the format (leave out if you donā€™t need the file). The resolve=true parameter converts power (and heart rate etc.) from units like % of FTP into watts. The default date range is a week from today (use oldest and newest parameters in ISO-8601 format to change).

Example abridged response:

[
  {
    "id": 46696127,
    "start_date_local": "2024-11-21T07:00:00",
    "type": "Ride",
    "category": "WORKOUT",
    "name": "Big Gear Grinds",
    "description": "...",
    ...
    "workout_doc": {
      "description": "A great session for building cycling specific strength both in and out of the saddle.",
      "target": "POWER",
      "steps": [
        {
          "text": "Welcome to the ...",
          "duration": 180,
          "warmup": true,
          "ramp": true,
          "power": { "start": 35.0,  "end": 55.0, "units": "%ftp" },
          "cadence": { "value": 85.0,  "units": "rpm" },
          "_power": { "value": 126.0,  "start": 98.0, "end": 154.0 }
        },
      ],
      ...
    },
    ...
    "workout_filename": "Big_Gear_Grinds.zwo",
    "workout_file_base64": "PD94bWwgdmVy..."
  }
]

The workout_file_base64 field contains the file in the specified format (zwo in this case) encoded as base 64 text.

The workout_doc field contains the workout in native Intervals.icu format. If you donā€™t already have a parser for one of the other formats then this might be easier to work with.

See this guide for instructions on how to upload planned workouts.

Updating wellness data

Push weight, resting HR, HRV, steps etc. using the bulk wellness upload endpoint (WELLNESS:WRITE scope required):

curl -X PUT 'https://intervals.icu/api/v1/athlete/0/wellness-bulk' \
    -H 'Authorization: Bearer d842c1fc25f241e5ae440d09756448a9' \
    -H 'Content-Type: application/json' \
    -d '[{"id":"2024-11-20","weight": 69.1},{"id":"2024-11-19","weight": 69.3}]'

Each object in the array must have an id (the ISO-8601 local date of the record) and whatever other fields you want to update. See the full docs for a list of possible fields. All units are metric.

Downloading wellness data

Call the list wellness endpoint (WELLNESS:READ scope required):

curl 'https://intervals.icu/api/v1/athlete/0/wellness?oldest=2024-11-18&newest=2024-11-20' \
    -H 'Authorization: Bearer d842c1fc25f241e5ae440d09756448a9'

Downloading completed activities

Call the list activities endpoint (ACTIVITY:READ scope is required):

curl 'https://intervals.icu/api/v1/athlete/0/activities?oldest=2024-11-19&newest=2024-11-20' \
    -H 'Authorization: Bearer d842c1fc25f241e5ae440d09756448a9'

This returns summary data for each activity. Example abridged response:

[{
  "id": "i55751783",
  "start_date_local": "2024-11-20T07:35:18",
  "type": "Ride",
  ...
  "file_type": "fit",
  ...

To download the original activity file (fit, gpx or tcx) gzip compressed:

curl 'https://intervals.icu/api/v1/activity/i55751783/file' \
    -H 'Authorization: Bearer d842c1fc25f241e5ae440d09756448a9' \
    > activity.fit.gz

To download the Intervals.icu generated fit file (with edits to activity power etc. and laps matching the intervals):

curl 'https://intervals.icu/api/v1/activity/i55751783/fit-file' \
    -H 'Authorization: Bearer d842c1fc25f241e5ae440d09756448a9' \
    > activity.fit.gz

Note that this is always a fit file even if the original file was a gpx or tcx.

Webhooks

Configure webhooks using the management page for your app. Look for your app in /settings and click ā€œManage Appā€.

Note that activity webhooks are not delivered for Strava activities.

The CALENDAR_EVENT_UPDATED and CALENDAR_EVENT_DELETED webhooks are legacy. Please use the CALENDAR_UPDATED webhook instead.

The webhook payload includes a secret that you can use to verify that the webhook came from Intervals.icu.

{
  "secret": "ooKeodacie8I",
  "events": [
    {
      "athlete_id": "...",
      "type": "ACTIVITY_UPLOADED",
      "timestamp": "2024-12-06T06:40:47.011+00:00",
      "activity": {...}
    }
  ]
}

Some webhooks (e.g. ACTIVITY_ANALYZED) are sent after a 60s delay so multiple events for the same activity can be consolidated into one webhook.

You need to store the Intervals.icu athlete_id obtained via OAuth flow so you can map the webhook back to the athlete in your system.

CALENDAR_UPDATED webhooks include oauth_client_id (that created the event if any) and external_id (ID supplied when the event was created, if any). You can use this to only look at events created by your app (oauth_client_id matches your client ID) and quickly update them using the external_id.

{
  "secret": "...",
  "events": [
    {
      "athlete_id": "2049151",
      "type": "CALENDAR_UPDATED",
      "timestamp": "2024-12-18T10:22:16.427+00:00",
      "events": [
        {
          "id": 48566307,
          "start_date_local": "2024-12-20T00:00:00",
          "icu_training_load": 118,
          "type": "Ride",
          ...
        },
        {
          "id": 48566308,
          "start_date_local": "2024-12-21T00:00:00",
          "icu_training_load": 82,
          ...
        }
      ],
      "deleted_events": []
    }
  ]
}
17 Likes