Summary:
When creating a workout in a library plan via the API (POST /api/v1/athlete/{id}/workouts), the workout structure displayed in the UI is incorrect. It appears to be cached/reused from a previously deleted workout, despite the API returning the correct description and calculated values.
Steps to Reproduce:
- Create a new plan folder via API: POST /api/v1/athlete/{id}/folders
- Create a workout in that folder via API: POST /api/v1/athlete/{id}/workouts with a simple description:
{
"folder_id": 649610,
"name": "Just a test workout - API Test",
"day": 4,
"description": "Just a test workout.\n\nWarmup\n- 2m 42%\n\nMain Set\n\n4x\n- 30s 120% 100rpm\n- 4m30s 68%\n\nCooldown\n- 1m 50%",
"type": "VirtualRide"
}
- API returns success with the correct description echoed back:
{
"id": 71817686,
"folder_id": 649610,
"name": "Just a test workout - API Test",
"description": "Just a test workout.\n\nWarmup\n- 2m 42%\n\nMain Set\n\n4x\n- 30s 120% 100rpm\n- 4m30s 68%\n\nCooldown\n- 1m 50%",
"moving_time": 6060,
"icu_training_load": 95,
...
}
- Open the workout in the Intervals.icu UI
Expected Result:
Workout structure should match the description:
- Warmup: 2m 42%
- Main Set: 4x (30s 120% 100rpm, 4m30s 68%)
- Cooldown: 1m 50%
Actual Result:
Workout structure shows a completely different workout (see screenshot):
- Warmup: 2m 30%, 2m 33%, 1m30s 36%, 1m30s 39%, 1m 42%, 1m 46%, 1m 50%, 6m 70%, 3m 100%
- Main Set: 2x (5m 50%, 1m 114%), etc.
This structure is from an old workout I created weeks/months ago and have since deleted.
(see screenshot)
API Response:
Here is the complete response object.
{'athlete_id': 'i166567',
'attachments': None,
'carbs_per_hour': None,
'color': None,
'day': 4,
'days': None,
'description': 'Just a test workout.\n'
'\n'
'Warmup\n'
'- 2m 42%\n'
'\n'
'Main Set\n'
'\n'
'4x\n'
'- 30s 120% 100rpm\n'
'- 4m30s 68%\n'
'\n'
'Cooldown\n'
'- 1m 50%',
'distance': 0.0,
'folder_id': 649625,
'for_week': False,
'hide_from_athlete': False,
'icu_intensity': 74.015274,
'icu_training_load': 21,
'id': 139,
'indoor': None,
'joules': 250980,
'joules_above_ftp': 6240,
'moving_time': 1380,
'name': 'Just a test workout - API Test',
'plan_applied': None,
'sub_type': None,
'tags': None,
'target': None,
'targets': ['POWER'],
'time': None,
'type': 'VirtualRide',
'updated': '2025-12-26T13:32:35.904+00:00',
'workout_doc': {'average_watts': 182,
'description': 'Just a test workout.',
'distance': 0,
'duration': 1380,
'locales': [],
'normalized_power': 195,
'options': {},
'polarization_index': 2.85,
'steps': [{'duration': 120,
'power': {'units': '%ftp', 'value': 42},
'warmup': True},
{'distance': 0,
'duration': 1200,
'reps': 4,
'steps': [{'cadence': {'units': 'rpm',
'value': 100},
'duration': 30,
'power': {'units': '%ftp',
'value': 120}},
{'duration': 270,
'power': {'units': '%ftp',
'value': 68}}],
'text': '4x'},
{'cooldown': True,
'duration': 60,
'power': {'units': '%ftp', 'value': 50}}],
'strain_score': {'ss_cp': 24.344982255168986,
'ss_p_max': 0.05623423672097546,
'ss_w_prime': 0.6521008604374656,
'strain_score': 25.053317352327426},
'variability_index': 1.0714285714285714,
'zoneTimes': [{'color': '#009e80',
'id': 'Z1',
'max': 55,
'maxWatts': 144,
'minWatts': 1,
'name': 'Z1',
'percentRange': '0% - 55%',
'secs': 180,
'zone': 1},
{'color': '#009e00',
'id': 'Z2',
'max': 75,
'maxWatts': 196,
'minWatts': 145,
'name': 'Z2',
'percentRange': '56% - 75%',
'secs': 1080,
'zone': 2},
{'color': '#ffcb0e',
'id': 'Z3',
'max': 90,
'maxWatts': 235,
'minWatts': 197,
'name': 'Z3',
'percentRange': '76% - 90%',
'secs': 0,
'zone': 3},
{'color': '#ff7f0e',
'id': 'Z4',
'max': 105,
'maxWatts': 275,
'minWatts': 236,
'name': 'Z4',
'percentRange': '91% - 105%',
'secs': 0,
'zone': 4},
{'color': '#dd0447',
'id': 'Z5',
'max': 120,
'maxWatts': 314,
'minWatts': 276,
'name': 'Z5',
'percentRange': '106% - 120%',
'secs': 120,
'zone': 5},
{'color': '#6633cc',
'id': 'Z6',
'max': 150,
'maxWatts': 393,
'minWatts': 315,
'name': 'Z6',
'percentRange': '121% - 150%',
'secs': 0,
'zone': 6},
{'color': '#504861',
'id': 'Z7',
'max': 999,
'maxWatts': 9999,
'minWatts': 394,
'name': 'Z7',
'percentRange': '151% +',
'secs': 0,
'zone': 7},
{'color': '#ffa00e',
'gap': True,
'id': 'SS',
'max': 94,
'maxWatts': 246,
'min': 88,
'minWatts': 230,
'name': 'Sweet Spot',
'percentRange': '88% - 94%',
'secs': 0,
'zone': 8}]}}
Key Observation:
The API response contains the correct description that was sent, yet the UI displays a completely different workout structure. This suggests the workout steps are being retrieved from a cache or lookup that references old/deleted workout data, rather than being parsed from the description field.
Additional Details:
- The plan name is correct: “Just a test plan - API Test”
- The workout name is correct: “Just a test workout - API Test”
- The duration (1h40m) and load (95) match the API response
- The API response description field contains the correct text
- But the rendered workout steps/structure are from an old deleted workout
Environment:
- API endpoint: POST /api/v1/athlete/{id}/workouts
- Workout created inside a plan folder (not as a calendar event)
Screenshot attached: Shows the workout editor with correct name but wrong workout structure.
