Error creating a new workout plan via API due to JSON parsing

Hi!

I am trying to create a new workout plan using the API (/api/v1/athlete/{id}/folders) but I receive a bad request (400 status code) due to JSON parsing.

I checked the JSON that I am passing as the body of the request with the one documented on the API docs (Intervals.icu API docs) and I didn’t find any difference between them.

What am I missing? I attach my JSON below.

Thanks in advance! :slight_smile:

{
    "hours_per_week_min": 3,
    "auto_rollout_day": 1,
    "name": "Plan de prueba numero 9354802150",
    "start_date_local": "2025-09-05T13:13:04",
    "workout_targets": [
        "POWER"
    ],
    "rollout_weeks": 1,
    "owner": {
        "sex": "",
        "website": "",
        "timezone": "",
        "email": "",
        "city": "",
        "name": "Xavi",
        "country": "",
        "state": "",
        "profileMedium": "",
        "id": "i349268",
        "bio": ""
    },
    "read_only_workouts": false,
    "id": 9354802150,
    "copy_folder_id": 9354802150,
    "children": [
        {
            "icu_intensity": 70,
            "icu_training_load": 100,
            "hide_from_athlete": false,
            "attachments": [],
            "description": "Warmup\n- 30m 56-70% maximum 130ppm\n\nMain set\n- 10m 88-93% Sweet Spot\n- 5m recovery at maximum 56%\n- 10m 88-93% Sweet Spot\n- 5m recovery at maximum 56%\n- 45m 65-70%\n- 10m 88-93% Sweet Spot\n- 5m recovery at maximum 56%\n- 10m 88-93% Sweet Spot\n- 5m recovery at maximum 56%\n- 30m recovery at maximum 56%\n- 1h 65-70%\n\nCooldown\n- 15m maximum at 56% and 120ppm",
            "time": "",
            "targets": [],
            "target": "",
            "tags": [],
            "joules": 0,
            "color": "0xFFFFFF",
            "moving_time": 7200,
            "for_week": false,
            "id": 2914145939,
            "days": 0,
            "updated": "2025-09-18T05:52:07",
            "distance": 0,
            "athlete_id": "i349268",
            "name": "Endurance 7",
            "indoor": false,
            "carbs_per_hour": 0,
            "joules_above_ftp": 0,
            "plan_applied": "",
            "folder_id": 9354802150,
            "sub_type": "Ride",
            "type": "Ride",
            "day": 15,
            "workout_doc": {
                "steps": [],
                "polarization_index": 0,
                "zoneTimes": [],
                "distance": 0,
                "strain_score": {
                    "ss_w_prime": 0,
                    "strain_score": 0,
                    "ss_cp": 0,
                    "ss_p_max": 0
                },
                "duration": 7200,
                "variability_index": 1.1,
                "normalized_power": 210,
                "average_watts": 200
            }
        },
        {
            "icu_intensity": 70,
            "hide_from_athlete": false,
            "icu_training_load": 100,
            "attachments": [],
            "description": "Warmup\n- 25m 56-70% maximum 135ppm\n\nMain set\n6x\n- 4m 110-115%\n- 4m recovery at maximum 56% or descent\n\n6x\n- 30s Sprint all out minimum 150%\n- 4m recovery at maximum 56%\n\nCooldown\n- 20m maximum at 56% and 120ppm",
            "time": "",
            "targets": [],
            "target": "",
            "tags": [],
            "joules": 0,
            "color": "0xFFFFFF",
            "moving_time": 7200,
            "for_week": false,
            "id": 445984663,
            "days": 0,
            "updated": "2025-09-12T06:13:30",
            "distance": 0,
            "athlete_id": "i349268",
            "name": "VO2MAX 1",
            "indoor": false,
            "joules_above_ftp": 0,
            "carbs_per_hour": 0,
            "plan_applied": "",
            "folder_id": 9354802150,
            "sub_type": "Ride",
            "type": "Ride",
            "day": 15,
            "workout_doc": {
                "steps": [],
                "polarization_index": 0,
                "zoneTimes": [],
                "distance": 0,
                "strain_score": {
                    "ss_w_prime": 0,
                    "strain_score": 0,
                    "ss_cp": 0,
                    "ss_p_max": 0
                },
                "duration": 7200,
                "variability_index": 1.1,
                "normalized_power": 210,
                "average_watts": 200
            }
        }
    ],
    "visibility": "PRIVATE",
    "blurb": "fsda",
    "canEdit": true,
    "shareToken": "",
    "description": "fds",
    "type": "PLAN",
    "num_workouts": 2,
    "hours_per_week_max": 6,
    "sharedWithCount": 0,
    "duration_weeks": 1,
    "starting_atl": 90,
    "athlete_id": "i349268",
    "starting_ctl": 90,
    "activity_types": [
        "Ride"
    ]
}

Try it without

\n

in the description. I’ve tried your code with replaced description and no error was shown.

Write your workout steps in the “steps” section instead. Something like this:

"description": "New Description",
…
            "workout_doc": {
                "steps": [{
          "ramp": true,
          "power": {
            "end": 70,
            "start": 42,
            "units": "%ftp"
          },
          "warmup": true,
          "duration": 300
        },
        {
          "power": {
            "units": "%ftp",
            "value": 50
          },
          "duration": 120
        },
        {
          "text": "Steady Power",
          "power": {
            "units": "%ftp",
            "value": 70
          },
          "duration": 180
        },
],

Ok, I’ll try it.

Thanks Tom!

I tried it but I’m still getting an error. The request body now looks like this:

{"description":"New description","read_only_workouts":false,"activity_types":["Ride"],"visibility":"PRIVATE","id":2292424521,"auto_rollout_day":1,"blurb":null,"start_date_local":"2025-09-05T20:09:54","workout_targets":["POWER"],"rollout_weeks":1,"num_workouts":3,"name":"Plan de prueba numero 2292424521","starting_ctl":90,"shareToken":"","hours_per_week_max":9,"hours_per_week_min":4,"canEdit":true,"starting_atl":90,"sharedWithCount":0,"owner":null,"copy_folder_id":2292424521,"athlete_id":"i349268","duration_weeks":1,"children":[{"name":"VO2MAX 1","updated":"2025-09-15T08:51:21","folder_id":2292424521,"type":"Ride","athlete_id":"i349268","id":7131881222,"description":"New description","workout_doc":{"steps":[{"warmup":true,"heartrate":{"units":"bpm","value":135},"duration":1500,"cooldown":false,"ramp":true,"power":{"units":"%ftp","start":56,"end":70}},{"warmup":false,"cooldown":false,"duration":240,"ramp":true,"power":{"end":115,"units":"%ftp","start":110}},{"warmup":false,"cooldown":false,"duration":240,"power":{"units":"%ftp","value":56}},{"warmup":false,"cooldown":false,"power":{"units":"%ftp","value":150}},{"warmup":false,"cooldown":false,"duration":240,"power":{"value":56,"units":"%ftp"}},{"warmup":false,"heartrate":{"value":120,"units":"bpm"},"duration":1200,"cooldown":true,"power":{"value":56,"units":"%ftp"}}],"duration":3420},"icu_training_load":100},{"name":"Endurance 6","updated":"2025-09-11T17:16:24","folder_id":2292424521,"type":"Ride","athlete_id":"i349268","id":6025818183,"description":"New description","workout_doc":{"steps":[{"power":{"start":56,"end":75,"units":"%ftp"},"warmup":true,"cooldown":false,"heartrate":{"value":140,"units":"bpm"},"duration":900,"ramp":true},{"power":{"units":"%ftp","end":93,"start":88},"warmup":false,"cooldown":false,"ramp":true,"duration":3600},{"power":{"units":"%ftp","start":56,"end":75},"warmup":false,"cooldown":false,"ramp":true,"duration":1200},{"power":{"value":100,"units":"%ftp"},"warmup":false,"cooldown":false,"duration":1200},{"power":{"value":56,"units":"%ftp"},"warmup":false,"cooldown":false,"duration":600},{"power":{"units":"%ftp","value":100},"warmup":false,"cooldown":false,"duration":1200},{"power":{"value":56,"units":"%ftp"},"warmup":false,"cooldown":false,"duration":600},{"power":{"units":"%ftp","end":75,"start":56},"warmup":false,"cooldown":false,"ramp":true,"duration":1800},{"power":{"units":"%ftp","value":110},"warmup":false,"cooldown":false,"duration":180},{"power":{"value":56,"units":"%ftp"},"warmup":false,"cooldown":false,"duration":300},{"power":{"units":"%ftp","value":110},"warmup":false,"cooldown":false,"duration":180},{"warmup":false,"power":{"value":56,"units":"%ftp"},"duration":300,"cooldown":false},{"warmup":false,"power":{"value":110,"units":"%ftp"},"duration":180,"cooldown":false},{"warmup":false,"power":{"units":"%ftp","value":56},"duration":300,"cooldown":false},{"warmup":false,"power":{"value":110,"units":"%ftp"},"duration":180,"cooldown":false},{"warmup":false,"power":{"units":"%ftp","value":56},"duration":300,"cooldown":false},{"warmup":false,"power":{"units":"%ftp","value":110},"duration":180,"cooldown":false},{"warmup":false,"power":{"units":"%ftp","value":56},"duration":300,"cooldown":false},{"warmup":false,"power":{"value":110,"units":"%ftp"},"duration":180,"cooldown":false},{"warmup":false,"power":{"units":"%ftp","value":56},"duration":300,"cooldown":false},{"warmup":false,"power":{"start":56,"units":"%ftp","end":75},"duration":900,"cooldown":true,"heartrate":{"value":135,"units":"bpm"},"ramp":true}],"duration":14880},"icu_training_load":100},{"name":"Tempo 2","updated":"2025-09-14T20:01:43","folder_id":2292424521,"type":"Ride","athlete_id":"i349268","id":51758539,"description":"New description","workout_doc":{"steps":[{"power":{"units":"%ftp","value":55},"duration":600,"warmup":true,"cooldown":false},{"power":{"value":70,"units":"%ftp"},"duration":600,"warmup":true,"cooldown":false},{"power":{"value":80,"units":"%ftp"},"duration":600,"warmup":true,"cooldown":false},{"power":{"units":"%ftp","value":100},"duration":600,"warmup":false,"cooldown":false},{"power":{"units":"%ftp","value":90},"duration":600,"warmup":false,"cooldown":false},{"power":{"units":"%ftp","value":80},"duration":600,"warmup":false,"cooldown":false},{"power":{"units":"%ftp","value":70},"duration":600,"warmup":false,"cooldown":false},{"power":{"units":"%ftp","value":60},"duration":600,"warmup":false,"cooldown":false},{"power":{"value":100,"units":"%ftp"},"warmup":false,"cooldown":false},{"power":{"units":"%ftp","value":90},"warmup":false,"cooldown":false},{"power":{"units":"%ftp","value":80},"warmup":false,"cooldown":false},{"power":{"value":70,"units":"%ftp"},"warmup":false,"cooldown":false},{"power":{"units":"%ftp","value":55},"duration":600,"warmup":false,"cooldown":true}],"duration":5400},"icu_training_load":100}],"type":"PLAN"}


Do you see anything that could be wrong?

Thanks for your help.

I don’t know how to read it.
I got it to work one time but need to go back through my logs but then I made a change and it messed up.

I build an gpt with instructions on how to build a zwo file. Now I just use that to build the file then import it into manually.

I know it’s not exactly write acces but it got me close enough in the workflow and stopped me from stressing over my schema errors.

Where is the workout_doc schema definition?
I cannot find it

Also, if I have workouts defined, can I just link them to the Plan?

If I remember correctly, the workout doc scheme is for reading, if you want to upload a workout use the description, and fill it like in the workout builder. That works without problems.

You can also upload to a plan and apply that plan.
There’s a huge API documentation where you also can play around with.

1 Like