Uploading planned workouts to Intervals.icu

This guide explains how to upload calendar events (e.g. workouts) from an external application to an athlete’s Intervals.icu calendar using the API. For more information about the API including OAuth support please see this post.

You need to request CALENDAR:WRITE scope to make changes to an athlete’s calendar. All the calendar related endpoints are documented here.

Intervals.icu calendar events have an id (primary key) and optional external_id (your primary key). You do not need to use external_id but if you do, you might be able to avoid having to store an Intervals.icu event id in your system. This guide uses external_id.

To create and/or update calendar events (upsert) call this endpoint with an array of events (workouts):

POST https://intervals.icu/api/v1/athlete/{id}/events/bulk?upsert=true
[{
  "category": "WORKOUT",
  "start_date_local": "2024-03-30T00:00:00",
  "filename": "Woddle_Toddle.fit",
  "file_contents_base64": "DiCNCG8AAAAuRklUKz9AAAEAAAMBAoQAAQAIDgcAAP8FSW50ZXJ2YWxzLmljdQBAAAEAGgMEAQAIDgcGAoQAAVdvZGRsZSBUb2RkbGUAAAFAAAEAGwcBAQACBIYDAQAFBIYGBIYHAQD+AoQAAAA27oABAAAA+AAAAQAEAAAe/A==",
  "external_id": "1234"
},
{
  "category": "WORKOUT",
  "start_date_local": "2024-04-01T00:00:00",
  "filename": "Woddle_Toddle2.fit",
  "file_contents_base64": "DiCNCG8AAAAuRklUKz9AAAEAAAMBAoQAAQAIDgcAAP8FSW50ZXJ2YWxzLmljdQBAAAEAGgMEAQAIDgcGAoQAAVdvZGRsZSBUb2RkbGUAAAFAAAEAGwcBAQACBIYDAQAFBIYGBIYHAQD+AoQAAAA27oABAAAA+AAAAQAEAAAe/A==",
  "external_id": "1235"
},
{
  "category": "NOTE",
  "start_date_local": "2024-04-01T00:00:00",
  "name": "Build",
  "description": "Start of build phase",
  "color": "green",
  "external_id": "1236"
}]

The athlete {id} in the path can be zero to use athlete associated with the bearer token or API key.

POST https://intervals.icu/api/v1/athlete/0/events/bulk?upsert=true

Use your primary key in the external_id field. Events that do not exist will be created. Those that already exist are updated. The external_id is only matched against events created by your application.

For planned workouts you can supply a filename and base 64 encode the file in file_contents_base64 (zwo, fit, mrc and erg are accepted) or you can use “description” and supply native Intervals.icu workout text. ZWO workouts are also accepted in “file_contents” as is with no base 64 encoding.

A full representation of each updated or created event is returned:

[{
  "id": 33375903,
  "start_date_local": "2024-03-30T00:00:00",
  ...
  "type": "Run",
  "calendar_id": 1,
  "uid": "e2597b1c-9ca8-4156-8737-4251e6bbb313",
  "athlete_id": "2049151",
  "category": "WORKOUT",
  ...

To delete calendar events call this endpoint with an array of objects each with external_id or id of an event to delete:

PUT https://intervals.icu/api/v1/athlete/{id}/events/bulk-delete
[
    {"external_id": "1234"},
    {"id": 33375896}
]

Events that do not exist are ignored. The number of events actually deleted is returned.

You can list all events on an athlete’s calendar for a date range using the list endpoint. This returns all events, not just the ones created by your application. You can filter for your client id using the oauth_client_id field.

4 Likes

delete

I am trying to upload a planned training by using this endpoint:
https://intervals.icu/api-docs.html#post-/api/v1/athlete/-id-/events

Since I have no idea how to send the training description i tried uploading a base64 encoded fitfile describing the trainingplan, so this is my POST request:

POST /api/v1/athlete/{id}/events
{
  "category": "PLAN",
  "start_date_local": "2025-11-13T00:00:00",
  "name": "Woddle_Toddle2 Training",
  "filename": "Woddle_Toddle2.fit",
  "file_contents_base64": "DiCNCG8AAAAuRklUKz9AAAEAAAMBAoQAAQAIDgcAAP8FSW50ZXJ2YWxzLmljdQBAAAEAGgMEAQAIDgcGAoQAAVdvZGRsZSBUb2RkbGUAAAFAAAEAGwcBAQACBIYDAQAFBIYGBIYHAQD+AoQAAAA27oABAAAA+AAAAQAEAAAe/A==",
  "external_id": "1235"
}

However this way the FIT File is not interpreted as a trainingplan. So I think I have to use another field for that? May “description”? But how to describe the Training in there?

ChatGPT suggests this, however the filed “icu_workout” is not documented:

POST /api/v1/athlete/{id}/events
{
  "name": "5x5min Threshold",
  "type": "Workout",
  "category": "PLAN",
  "start_date_local": "2025-11-15T09:00:00",
  "icu_workout": {
    "steps": [
      { "type": "Warmup", "duration": 600, "target": {"type": "power", "zone": 2}},
      { "type": "Interval", "reps": 5, "duration": 300, "target": {"type": "power", "zone": 4}},
      { "type": "Cooldown", "duration": 600, "target": {"type": "power", "zone": 2}}
    ]
  }
}

Please send some help @david :slight_smile:

Meanwhile I am a few steps further. When I Upload the Plan as FIT File it appears in the Calendar als a Planned Training for that day and also when I Upload the Plan in the intervals.icu description language it appears the same way.

However it is not synced to my connected Garmin account. However if I create the Training for that day on my own within intervals.icu it is synced directly.

Any hints on that?

Hi David,

I’m trying to upload Weight Training workouts via API to athletes that I guide. Since this is not a standard activity it is always uploaded as a ride or run. I would like to schedule them as Weight Training, but I don’t want my athletes to have to go to settings and add a new activity type or settings for weight training. When I do it manually I can add weight trainings (but every athlete seems to have a unique id for weight training). Any idea how I can solve this?

Thanks for all the awesome work!

Did you try to set it directly
"type": "WeightTraining",

@R2Tom thanks! I don’t know why this did not work earlier, perhaps I was stuck on “Weight Training” with a space in between but it works!

1 Like

A post was merged into an existing topic: API access to Intervals.icu

@Romulus1689 I would highly appreciate if you could share some working example of uploaded workout using description. Thanks in advance!

meanwhile by reverse engineering i have a working example - for anyone who is playing around with API

{
  "category": "WORKOUT",
  "start_date_local": "2026-02-03T17:00:00",
  "type": "Ride",
  "name": "Intervals: 3x VO2 Max + Anaerobic (Fixed)",
  "description": "- 15m 55% Warmup \n\n3x\n- 1m 150%\n- 1m 50%\n\n- 5m 50% \n- 5m 120%\n- 15m 55%",  
  "moving_time": 2760,
  "target": "POWER",
  "icu_training_load": 56,
  "workout_doc": {}
}