Hi Clive,
How are you today ? C’est à nouveau moi 
I asked my friend Chad to explain you in english the error i frequently meet when i try to push suggested workouts from chatgpt to Intervals. Thanks again for you amazing work 
Issue: Planned workouts sometimes created as NOTE instead of WORKOUT (Ride) in Intervals.icu
When I push planned training sessions into Intervals.icu via the available API action (writeCalendarV1), I get inconsistent behavior:
- Some planned cycling sessions are created correctly as category = “WORKOUT” and type = “Ride”, with a populated
workout_doc (structured workout).
These display properly in Intervals as a cycling workout.
- Other sessions (even with valid structured workout text) are created as category = “NOTE”, with
type = null and workout_doc = null.
These do not appear as workouts in Intervals, only as notes.
This happens even when:
- the workout text uses the correct Intervals formatting (Warmup/Main set/Cooldown structure, correct indentation),
- power targets are provided as %FTP,
- duration and load are included,
- and the session is clearly a bike workout.
Example from the last push:
- 2026-03-04 and 2026-03-10 were created correctly as
WORKOUT with type:"Ride" and workout_doc.
- 2026-03-09, 2026-03-11, 2026-03-12 repeatedly fell back to
NOTE, despite multiple formatting attempts (different bullet styles, different nesting, simplified versions).
I also tried adding fields like sub_type:"Ride" or indoor:true, but those either:
- cause 400 JSON parse errors (connector schema rejects unknown keys), or
- still result in
NOTE.
So currently, the connector can sometimes parse a planned workout into a structured Ride workout, but fails unpredictably for other sessions.
Why this matters
The user specifically needs planned sessions to show up as Workouts (Ride) in Intervals, not as Notes—because:
- workouts behave differently in the UI,
- they can be executed/parsed as structured sessions,
- and they integrate properly into the training plan view.
Desired fix / feature
The connector should allow explicitly setting:
category = WORKOUT
type = Ride
- and/or directly passing a structured payload (
workout_doc) instead of relying on text parsing.
A reliable solution would be either:
- Add support in
writeCalendarV1 for explicit fields like sport/type/category, OR
- Support passing a structured
workout_doc object directly.
Right now, we’re forced into a workaround: the user must manually open the NOTE in Intervals UI and convert it to a WORKOUT Ride via “Edit → Category WORKOUT → Sport Ride → Parse structured workout”.
here’s a paste-ready snippet showing the mismatch (WORKOUT vs NOTE) from the API response to the same writeCalendarV1 push.
[
{
“id”: 96388921,
“start_date_local”: “2026-03-04T00:00:00”,
“category”: “WORKOUT”,
“type”: “Ride”,
“name”: “Club ride + 3x3’ (110–115%)”,
“moving_time”: 3120,
“description”: “Warmup\n- 20m ramp 50-70% (121-170w)\n\nMain set\n3x\n- 3m 110-115% (267-279w)\n- 6m 55-60% (134-146w)\n\nThen\n- 35-55m 60-70% (146-170w) endurance (stay disciplined)\n\nCooldown\n- 5m 50% (121w)\n\nPlan B: si le groupe te fait sprinter, 1 seul effort 1-3min, puis tu te replaces.”,
“workout_doc”: {
“steps”: [
{ “ramp”: true, “power”: { “start”: 50, “end”: 70, “units”: “%ftp” }, “duration”: 1200 },
{
“reps”: 3,
“steps”: [
{ “power”: { “start”: 110, “end”: 115, “units”: “%ftp” }, “duration”: 180 },
{ “power”: { “start”: 55, “end”: 60, “units”: “%ftp” }, “duration”: 360 }
],
“duration”: 1620
},
{ “power”: { “value”: 50, “units”: “%ftp” }, “duration”: 300 }
],
“duration”: 3120
}
},
{
“id”: 96388922,
“start_date_local”: “2026-03-09T00:00:00”,
“category”: “NOTE”,
“type”: null,
“name”: “Openers répétabilité 30/30 (2x5)”,
“moving_time”: null,
“description”: “Warmup\n- 15m ramp 50-70% (121-170w)\n\nMain set\n2x\n- 5x\n - 30s 125% (304w)\n - 30s 55% (134w)\n- 6m 55-60% (134-146w)\n\nCooldown\n- 10m 50-55% (121-134w)\n\nFort = contrôlé (tu finis propre).”,
“workout_doc”: null
},
{
“id”: 96388923,
“start_date_local”: “2026-03-10T00:00:00”,
“category”: “WORKOUT”,
“type”: “Ride”,
“name”: “Endurance facile Z2 bas”,
“moving_time”: 3300,
“description”: “Warmup\n- 10m ramp 45-65% (109-158w)\n\nMain set\n- 40m 60-65% (146-158w)\n\nCooldown\n- 5m 50% (121w)”,
“workout_doc”: {
“steps”: [
{ “ramp”: true, “power”: { “start”: 45, “end”: 65, “units”: “%ftp” }, “duration”: 600 },
{ “power”: { “start”: 60, “end”: 65, “units”: “%ftp” }, “duration”: 2400 },
{ “power”: { “value”: 50, “units”: “%ftp” }, “duration”: 300 }
],
“duration”: 3300
}
},
{
“id”: 96388924,
“start_date_local”: “2026-03-11T00:00:00”,
“category”: “NOTE”,
“type”: null,
“name”: “Spécifique segments 1–5’ (4x2’ + 1x4’)”,
“moving_time”: null,
“description”: “Warmup\n- 15m ramp 50-75% (121-182w)\n\nMain set\n4x\n- 2m 120% (292w)\n- 4m 55-60% (134-146w)\n\n- 8m 55-60% (134-146w)\n\n- 4m 110% (267w)\n\nCooldown\n- 10m 50-55% (121-134w)\n\nSi sensations moyennes: faire 3x2’ au lieu de 4x2’.”,
“workout_doc”: null
},
{
“id”: 96388925,
“start_date_local”: “2026-03-12T00:00:00”,
“category”: “NOTE”,
“type”: null,
“name”: “Pré-course openers”,
“moving_time”: null,
“description”: “Warmup\n- 15m ramp 45-65% (109-158w)\n\nMain set\n3x\n- 30s 120% (292w) haute cadence\n- 3m 50-55% (121-134w)\n\nCooldown\n- 10m 50% (121w)\n\nBut: jambes vives, pas de fatigue.”,
“workout_doc”: null
}
]