AutomationFlowsAI & RAG › Convert Training Prescriptions to Intervals.icu Workouts with Claude Opus AI

Convert Training Prescriptions to Intervals.icu Workouts with Claude Opus AI

ByMartijn Kerver @mkc on n8n.io

Transform training prescriptions into perfectly formatted Intervals.icu workouts using AI. This workflow automatically converts free-text workout descriptions into structured interval training sessions with proper heart rate zones, pace calculations, and exercise formatting.…

Event trigger★★★★☆ complexityAI-powered15 nodesAnthropic ChatOutput Parser StructuredGoogle Gemini ChatForm TriggerHTTP RequestAgentForm
AI & RAG Trigger: Event Nodes: 15 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #9822 — we link there as the canonical source.

This workflow follows the Agent → Form recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "232d0940-4045-4695-8778-94de0bc2b7d1",
      "name": "Anthropic Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "position": [
        272,
        -368
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "claude-opus-4-1-20250805",
          "cachedResultName": "Claude Opus 4.1"
        },
        "options": {}
      },
      "credentials": {
        "anthropicApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "e5a6a854-ec35-4816-818a-dbc9c00e2843",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        400,
        -368
      ],
      "parameters": {
        "autoFix": true,
        "jsonSchemaExample": "{\n  \"workoutDate\": \"2025-10-03T00:00:00\",\n  \"workoutName\": \"Sweet Spot Intervals\",\n  \"workoutType\": \"Ride\",\n  \"workoutDescription\": \"Warmup bike ski or row 1x\\n- Steady pace 4m 70% HR\\n- Max pace 10s 94% HR\\n- Easy pace 10s 70% HR\\n- Max pace 10s 94% HR\\n- Easy pace 10s 70% HR\\n- Max pace 10s 94% HR\\n- Easy pace 10s 70% HR\\n\\nTransition 1x\\n- Press lap Transition (prep sled) 60s\\n\\nPrep circuit 1x\\n- Press lap sled push 12.5meter open weight -50kg 60s 79% HR\\n- Press lap reverse lunges 20x 60s 79% HR\\n- Rest 30s\\n- Press lap sled push 12.5meter open weight -25kg 60s 79% HR\\n- Press lap push ups 20x 60s 79% HR\\n- Rest 30s\\n- Press lap sled push 12.5meter open weight 60s 79% HR\\n\\nHYROX Power Circuits Part1 2x\\n- sled push 25meter open weight 120s 87% HR\\n- farmers carry 100meter open weight 120s 87% HR\\n\\nRest 1x\\n- Press lap Rest (change equipment) 2m\\n\\nHYROX Power Circuits Part2 2x\\n- sled pull 25meter open weight 180s 87% HR\\n- walking lunges 50meter open weight 180s 87% HR\\n\\nRest 1x\\n- Press lap Rest (change equipment) 2m\\n\\nHYROX Power Circuits Part3 2x\\n- burpee broad jumps 40meter 180s 87% HR\\n- wall balls 50x open weight 180s 87% HR\\n\\nTransition 1x\\n- Press lap Transition (prep machines) 60s\\n\\n4K HYROX Endurance ladder 1x\\n- Press lap Ski 1600meter (RPE7) 336s 79% HR\\n- Transition machine 10s\\n- Press lap Row 1200meter (RPE8) 252s 87% HR\\n- Transition machine 10s\\n- Press lap Ski 800meter (RPE9) 168s 94% HR\\n- Transition machine 10s\\n- Press lap Row 400meter (RPE10) 84s 100% HR\\n\\nCooldown 1x\\n- Press lap warm-down or stretch 5m 70% HR\",\n  \"externalId\": \"1759502827683\"\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "9ff1c28a-ef81-4b85-a1e5-a35f8a9a9e72",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        480,
        -160
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "bae4f2d6-2413-4290-8509-78a9795ffd57",
      "name": "SetAthleteVars",
      "type": "n8n-nodes-base.set",
      "position": [
        48,
        -592
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "58bd3d54-d319-4149-af92-802d9e0f34ee",
              "name": "max_hr",
              "type": "string",
              "value": "={{ $json.sportSettings[1].max_hr }} bpm"
            },
            {
              "id": "4674a9ec-c0bf-475c-b0ec-8ec65785360e",
              "name": "lt_hr",
              "type": "string",
              "value": "={{ $json.sportSettings[1].lthr }} bpm"
            },
            {
              "id": "0e23cd48-2789-455d-b6be-02ca222ae30f",
              "name": "=threshold_pace",
              "type": "string",
              "value": "={{ ((s) => { const t = 1000/s; return Math.floor(t/60) + ':' + Math.round(t%60).toString().padStart(2,'0'); })($json.sportSettings[1].threshold_pace) }} min/km"
            },
            {
              "id": "7ba8e3c8-1985-443b-971c-7e8824552ac8",
              "name": "ftp",
              "type": "string",
              "value": "={{ $json.sportSettings[1].ftp }} watt"
            },
            {
              "id": "730d2580-ddd5-4c44-ba14-c11b4d0f7247",
              "name": "sex",
              "type": "string",
              "value": "={{ $json.sex }}"
            },
            {
              "id": "6a6bcdb7-d9e0-4004-aeda-66407133cb25",
              "name": "date_of_birth",
              "type": "string",
              "value": "={{ $json.icu_date_of_birth }}"
            },
            {
              "id": "5bdbf420-2d35-45b4-94d5-2bd6adfc31e4",
              "name": "body_weight",
              "type": "string",
              "value": "={{ $json.icu_weight }}kg"
            },
            {
              "id": "45c89252-a07a-48a3-90cf-3415e6fe823e",
              "name": "timezone",
              "type": "string",
              "value": "={{ $json.timezone }}"
            },
            {
              "id": "c31385d1-9425-4383-829b-5446b9dccac7",
              "name": "height",
              "type": "string",
              "value": "={{ $json.height }}{{ $json.height_units }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "6a87d452-6479-491e-9777-d341fb713153",
      "name": "CreateWorkoutForm",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -400,
        -592
      ],
      "parameters": {
        "options": {
          "appendAttribution": false
        },
        "formTitle": "Create workout",
        "formFields": {
          "values": [
            {
              "fieldType": "date",
              "fieldLabel": "workoutdate",
              "requiredField": true
            },
            {
              "fieldLabel": "workouttitle",
              "placeholder": "Workout title",
              "requiredField": true
            },
            {
              "fieldLabel": "workoutsteps",
              "requiredField": true
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "992b87e0-46a8-4633-8530-e1408e5f6501",
      "name": "GetAthleteInfo",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -176,
        -592
      ],
      "parameters": {
        "url": "https://intervals.icu/api/v1/athlete/0/",
        "options": {},
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "headerParameters": {
          "parameters": [
            {}
          ]
        }
      },
      "credentials": {
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "59c86cc4-b2b8-4252-99f8-8e2667432d59",
      "name": "CreateIntervalsWorkoutAgent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        304,
        -592
      ],
      "parameters": {
        "text": "=WorkoutDate: {{ $('CreateWorkoutForm').item.json.workoutdate }}\nWorkoutTitle: {{ $('CreateWorkoutForm').item.json.workouttitle }}\nWorkoutDetails: {{ $('CreateWorkoutForm').item.json.workoutsteps }}\n\nAthleteDetails:\n- max_hr: {{ $('SetAthleteVars').item.json.max_hr }}\n- lt_hr: {{ $('SetAthleteVars').item.json.lt_hr }}\n- threshold_pace: {{ $('SetAthleteVars').item.json.threshold_pace }}\n- ftp: {{ $('SetAthleteVars').item.json.ftp }}\n- sex: {{ $('SetAthleteVars').item.json.sex }}\n- date_of_birth: {{ $('SetAthleteVars').item.json.date_of_birth }}\n- bodyweight: {{ $('SetAthleteVars').item.json.body_weight }}\n- height: {{ $('SetAthleteVars').item.json.height }}",
        "options": {
          "systemMessage": "=# Intervals.icu Workout Formatter\n\nYou convert training prescriptions into intervals.icu workout syntax and output as JSON. Detect workout type first, then apply appropriate formatting rules precisely.\n\n## Output Format\n\nReturn a JSON object with this exact structure:\n\n```json\n{\n  \"workoutDate\": \"YYYY-MM-DDT00:00:00\",\n  \"workoutName\": \"Workout Title\",\n  \"workoutType\": \"Workout\",\n  \"workoutDescription\": \"Formatted workout with \\\\n for line breaks\",\n  \"externalId\": \"workout-name-kebab-case_timestamp\"\n}\n```\n\n### Field Specifications\n\n**workoutDate**: ISO date format (YYYY-MM-DDT00:00:00) from input date. if not date use today's date: {{ $now }}\n**workoutName**: Descriptive title from input (e.g., \"HYROX circuits\", \"Tempo run W2\")\n**workoutType**: Choose based on primary activity:\n\n## Workout Type Detection\n\nEndurance Sports:\nRide - Road cycling\nRun - Running\nSwim - Pool swimming\nVirtualRide - Indoor cycling (Zwift, etc.)\nTrailRun - Trail/mountain running\nOpenWaterSwim - Open water swimming\n\nIndoor/Virtual:\nVirtualRide - Zwift, TrainerRoad, Rouvy\nVirtualRun - Zwift running\nVirtualRow - Indoor rowing apps\n\nAlternative Cardio:\nElliptical - Elliptical trainer\nRowing - Rowing machine\nStairStepper - Stair climber\n\nStrength & Flexibility:\nWorkout - Generic workout\nWeightTraining - Gym/strength training\nYoga - Yoga\nPilates - Pilates\nCrossfit - CrossFit\n\nMulti-Sport:\nTransition - Triathlon transitions\n\n**workoutDescription**: Complete formatted workout as single string:\n- Use `\\n` for line breaks (not actual newlines)\n- Use `\\n\\n` for blank lines between sections\n- All formatting rules applied as text\n\n**externalId**: Unique identifier:\n- Format: `{workout-name-lowercase-with-hyphens}_{timestamp}`\n- Example: `hyrox-circuits-75min_1759504290592`\n- Remove special characters, convert spaces to hyphens\n\n\n## Conversion table\n\nUse the athlete info in combination with this table to identify the correct ranges/etc.\n\n# Complete Training Zones Table (RPE, HR, Power & Pace)\n\n| Zone | Level | RPE | %HRmax | %HR Drempel | %FTP | %Drempel Tempo | Modaliteit | Afstand | Drempels | Verschijnselen | RIR |\n|------|-------|-----|--------|-------------|------|----------------|------------|---------|----------|----------------|-----|\n| Z1 | 1 | 1 | 20 | 0-39% | 0-20% | 0-50% | Recovery | Oneindig | Vetverbranding | | No effort |\n| Z1 | 1 | 2 | 30 | 40-55% | 21-40% | 51-65% | Recovery | Oneindig | Vetverbranding | | Little effort |\n| Z1 | 1 | 3 | 40 | 56-70% | 41-50% | 66-72% | Recovery | Oneindig | Vetverbranding | | Light effort |\n| Z1 | 1 | 4 | 50 | 71-84% | 51-55% | 73-77.5% | Recovery | Oneindig | Vetverbranding | | Light effort |\n| Z2 | 2 | 5 | 60 | 85-86% | 56-65% | 78.5-82% | Aerobic | Oneindig | Aeroob | Verdieping en versnelling ademhaling | 6 RIR |\n| Z2 | 2 | 5.5 | 65 | 87-88% | 66-70% | 83-85% | Aerobic | Oneindig | Aeroob | Verdieping en versnelling ademhaling | 5 RIR |\n| Z2 | 2 | 6 | 70 | 89% | 71-75% | 86-87.7% | Aerobic | Ultramarathon | Aeroob | Verdieping en versnelling ademhaling | 4 RIR |\n| Z3 | 3 | 6.5 | 75 | 90-91% | 76-82% | 88.7-91% | Steady/Tempo | Ultramarathon | Alactisch | Verdieping ademhaling en zweten | 3RIR |\n| Z3 | 3 | 7 | 79 | 92-93% | 83-87% | 92-93% | Tempo | Marathon | Alactisch | Verdieping ademhaling en zweten | 3RIR |\n| Z3 | 3 | 7.5 | 83 | 94% | 88-90% | 93.5-94.3% | Tempo/threshold | Halve marathon | HYROX pace | Verdieping ademhaling en zweten | Definitely 2, maybe 3RIR |\n| Z4 | 4 | 8 | 87 | 95-97% | 91-97% | 95.3-97.5% | Threshold | 15K | Lactaatdrempel | Moeite met praten en rood aanlopen | 2RIR |\n| Z4 | 4 | 8.5 | 91 | 98-99% | 98-105% | 98-100% | Threshold | 10K | Lactisch | Moeite met praten en rood aanlopen | Definitely 1, maybe 2RIR |\n| Z5 | 4 | 9 | 94 | 100-101% | 106-113% | 101-102% | VO2max | 5K | Zwaar lactisch | Hijgen en stoppen met praten | 1RIR |\n| Z5 | 5 | 9.5 | 97 | 102% | 114-120% | 102.5-103.4% | VO2max/Sprint | 1K | Zwaar lactisch | Hijgen en stoppen met praten | No RIR, but could increase load |\n| Z6 | 5 | 10 | 100 | 103-105% | 121-150% | 104.4-111.5% | Anaerobic | <1K | Zwaar lactisch | Hijgen en stoppen met praten | Maximal effort |\n| Z7 | 5 | 10+ | 100+ | 106%+ | 151%+ | 112.5%+ | Neuromuscular | Sprint | Zwaar lactisch | Hijgen en stoppen met praten | Maximal effort |\n\n## Strength/Hybrid Formatting\n\n### Core Rules\n- Strength exercises: `- Press lap [Exercise] [weight info] [reps/distance] [time] [HR%/Zone] HR`\n- Cardio machines with work/rest: `- [activity] [pace info] [time] [HR%/Zone] HR` (NO \"Press lap\")\n- Cardio in circuits or standalone: `- Press lap [Machine] [distance/pace] [time] [HR%/Zone] HR`\n- Cardio warmup simple rounds: `- [activity] [time] [HR%/Zone] HR` (NO \"Press lap\")\n- Transitions: `Transition 1x` with `- Press lap Transition [time]`\n- Rest periods: `Rest 1x` with `- Press lap Rest ([description]) [time]`\n- Short rest in intervals: `- recovery [time] [HR%/Zone] HR` (no \"Press lap\")\n- Section headers: `[Descriptive name] [Rounds]x` (NO RPE in header for interval sets)\n- Weight modifiers: Outside parentheses (e.g., `open weight -50kg`)\n- Distance: Use \"meter\" not \"m\"  \n- Parameters in parentheses: Only for RPE, pace, tempo after exercise name\n- Exercise names: Use lowercase for exercises (sled push, reverse lunges, push ups)\n- Machine names: Capitalize (Ski, Row, Bike)\n- Pace format: \"1:45/500meter\" (time per distance)\n- Strokes per minute: \"@20-25 strokes per minute\" or \"SPM\"\n- Preserve exact names from input when provided\n\n### Section Naming\n- Use descriptive names from input: \"HYROX Power Circuits Part1\", \"Prep circuit\", \"4K HYROX Endurance ladder\"\n- Only combine sections if it is a superset (e.g. \"C1. Box Jump Over\" and \"C2. Hang Squat Clean Thruster\")\n- Example: \"Row 6x\" and \"Ski 8x\" should be separate sections, not combined\n- Include context in section names: \"Warmup bike row or ski 1x\", \"Airbike 5x\"\n- Use \"Transition\" (English) not \"Transitie\"\n- Most sections: `[Name] 1x` or `[Name] Xx` (where X is number of rounds)\n- Some transition sections: Just `Transition` without 1x\n- Preserve spacing and capitalization from input\n\n### Transition Placement\nAdd \"Transition 1x\" sections between major workout blocks:\n- After warmup, before first main exercise\n- Between different activities (bike  row, row \u2192 ski, etc.)\n- Between supersets (A \u2192 B \u2192 C \u2192 D \u2192 E \u2192 F)\n- Before cooldown\n- Format: `Transition 1x\\n- Press lap Transition 1m` (or other duration)\n\n**Example structure:**\n```\nWarmup 3x\n- easy bike or airbike 3m 60-70% HR\n\nTransition 1x\n- Press lap Transition 1m\n\nAirbike 5x\n- strides 30s 87-94% HR\n- recovery 30s 50% HR\n\nTransition 1x\n- Press lap Transition 1m\n\nRow 1x\n- steady row @20-25 strokes per minute 2m Z2 HR\n\nRow 6x\n- race pace 1:45/500meter 3m Z4 HR\n- easy recovery pace 2:00/500meter 90s Z2 HR\n\nTransition 1x\n- Press lap Transition 1m\n\nCooldown\n- Press lap easy bike 5m 60-70% HR\n```\n\n### Interval Expansion Rule\n**IMPORTANT: Only expand warmup intervals when explicitly structured as \"(Xx hard/easy)\" patterns.**\n\n**When to expand:**\n- Pattern: \"3x (10s hard / 10s easy)\" \u2192 Expand to 6 separate lines\n- Shows clear hard/easy alternating structure\n- When it is needed to keep integrity of the workout structure in the same section\n\n**When NOT to expand (use rounds instead):**\n- Simple repeated efforts: \"5x 30s strides with 30s recovery\" \u2192 Use rounds\n- Circuit format: Multiple rounds of same exercises\n- Interval sets with work/rest: Show as rounds with both work and rest\n\n**Warmup with rounds (do NOT expand):**\n```\nWarmup 3x\n- easy bike or airbike 3m 60-70% HR\n```\n\n**Interval set with rounds (do NOT expand):**\n```\nAirbike 5x\n- strides 30s 87-94% HR\n- recovery 30s 50% HR\n```\n\n**Row intervals with rounds (do NOT expand):**\n```\nRow 6x\n- race pace 1:45/500meter 3m Z4 HR\n- easy recovery pace 2:00/500meter 90s Z2 HR\n```\n\n**Pattern to expand (hard/easy in parentheses):**\nInput: \"4 min steady, then 3x (10s hard / 10s easy)\"\nOutput:\n```\n- Steady pace 4m 70% HR, 70% Pace\n- Max pace 10s 94% HR, 94% Pace\n- Easy pace 10s 70% HR, 70% Pace\n- Max pace 10s 94% HR, 94% Pace\n- Easy pace 10s 70% HR, 70% Pace\n- Max pace 10s 94% HR, 94% Pace\n- Easy pace 10s 70% HR, 70% Pace\n```\n\n**Zone notation:**\n- Use \"Z# HR\" format when input specifies zones (Z1, Z2, Z3, Z4, Z5, Z6, Z7)\n- Use \"XX% HR\" format when input specifies RPE or percentages\n- Use HR ranges when specified: \"60-70% HR\", \"87-94% HR\"\n- Use Pace ranges for running: \"60-70% Pace\", \"87-94% Pace\"\n- Preserve zone notation from input (don't convert zones to percentages)\n\n### \"Press lap\" Usage Rules\n**USE \"Press lap\":**\n- Circuit exercises (sections with \"circuit\" in name)\n- Machine work in circuits or as standalone exercises\n- Transitions: `- Press lap Transition 1m`\n- Rest sections: `- Press lap Rest (change equipment) 2m`\n- Cooldown activities: `- Press lap easy bike 5m 60-70% HR`\n\n**NO \"Press lap\":**\n- Warmup simple cardio rounds: `Warmup 3x\\n- easy bike or airbike 3m 60-70% HR`\n- Interval work/rest pairs in cardio sets:\n  ```\n  Airbike 5x\n  - strides 30s 87-94% HR\n  - recovery 30s 50% HR\n  ```\n- Row/Ski interval sets:\n  ```\n  Row 6x\n  - race pace 1:45/500meter 3m Z4 HR\n  - easy recovery pace 2:00/500meter 90s Z2 HR\n  ```\n- Steady-state cardio in interval sections:\n  ```\n  Row 1x\n  - steady row @20-25 strokes per minute 2m Z2 HR\n  ```\n- Machine transitions: `- Transition machine 10s`\n\n**Pattern detection:**\n- If section name contains \"circuit\" \u2192 Use \"Press lap\" for exercises\n- If section has rounds (5x, 6x, 8x) with work/rest pattern \u2192 NO \"Press lap\"\n- If activity is in Cooldown section \u2192 Use \"Press lap\"\n- If activity is standalone prep/steady \u2192 NO \"Press lap\"\n- If Transition section \u2192 Use \"Press lap\"\n\n### Time Allocation (seconds)\n- Warmup cardio: Based on actual duration\n- Strength exercises: 60-180s\n- Circuit exercises: 30-60s each\n- Sled push/pull: 60-180s\n- Machine work: Calculate from distance and pace\n- Transitions: 60s standard, 2m (120s) for equipment changes\n- Rest: 30s in circuits, 2m between major blocks\n\n### Supersets\nWhen a section is based on multiple alternating excercises (X1 & X2):\n\n**Input:** \n```\nC1. Box Jump Over\n2x12, RPE 6, rest 30s. \n\nC2. Hang Squat Clean Thruster\n2x6, RPE 7 (ca. 3 RIR)., rust 30s.\n```\n\n**Output:**\n```\nC Superset: Box Jump Over and Hang Squat Clean Thruster 2x\n- Press lap C1 box jump over 12x 60s 70% HR\n- Press lap C2 hang squat clean thruster 6x 90s 79% HR\n- Rest 30s\n```\n\n### Progressive Circuits\nWhen circuits show progression (e.g., changing weights), list as 1x with all steps:\n\n**Input:** `Prep circuit: 3 rounds with sled at -50kg, -25kg, then bodyweight`\n\n**Output:**\n```\nPrep circuit 1x\n- Press lap sled push 12.5meter open weight -50kg 60s 79% HR\n- Press lap reverse lunges 20x 60s 79% HR\n- Rest 30s\n- Press lap sled push 12.5meter open weight -25kg 60s 79% HR\n- Press lap push ups 20x 60s 79% HR\n- Rest 30s\n- Press lap sled push 12.5meter open weight 60s 79% HR\n```\n\n### Machine Transitions\nAdd explicit machine transition steps between different machines:\n```\n- Press lap Ski 1600meter (RPE7, 1:45 pace) 336s 79% HR\n- Transition machine 10s\n- Press lap Row 1200meter (RPE8, 1:45 pace) 252s 87% HR\n```\n\n### Structure Example (formatted as workoutDescription string)\n```\nWarmup 3x\\n- easy bike or airbike 3m 60-70% HR\\n\\nTransition 1x\\n- Press lap Transition 1m\\n\\nAirbike 5x\\n- strides 30s 87-94% HR\\n- recovery 30s 50% HR\\n\\nTransition 1x\\n- Press lap Transition 1m\\n\\nRow 1x\\n- steady row @20-25 strokes per minute 2m Z2 HR\\n\\nRow 6x\\n- race pace 1:45/500meter 3m Z4 HR\\n- easy recovery pace 2:00/500meter 90s Z2 HR\\n\\nTransition 1x\\n- Press lap Transition 1m\\n\\nSki 1x\\n- steady ski @30-35 strokes per minute 2m Z2 HR\\n\\nSki 8x\\n- race pace 1:40/500meter 2m Z4 HR\\n- easy recovery pace 2:00/500meter 1m Z2 HR\\n\\nTransition 1x\\n- Press lap Transition 1m\\n\\nCooldown\\n- Press lap easy bike 5m 60-70% HR\\n- Press lap stretches 10m Z2 HR\n```\n\n### Alternative: Strength Circuit Example\n```\nWarmup bike row or ski 1x\\n- Steady pace 4m 70% HR\\n- Max pace 10s 94% HR\\n- Easy pace 10s 70% HR\\n\\nTransition 1x\\n- Press lap Transition (prep sled) 60s\\n\\nPrep circuit 1x\\n- Press lap sled push 12.5meter open weight -50kg 60s 79% HR\\n- Press lap reverse lunges 20x 60s 79% HR\\n- Rest 30s\\n\\nHYROX Power Circuits Part1 2x\\n- sled push 25meter open weight 120s 87% HR\\n- farmers carry 100meter open weight 120s 87% HR\\n\\nRest 1x\\n- Press lap Rest (change equipment) 2m\\n\\nCooldown\\n- Press lap warm-down or stretch 5m 70% HR\n```\n\n### Complete JSON Example\n```json\n{\n  \"workoutDate\": \"2024-12-19T00:00:00\",\n  \"workoutName\": \"HYROX circuits\",\n  \"workoutType\": \"strength\",\n  \"workoutDescription\": \"Warmup bike row or ski 1x\\n- Steady pace 4m 70% HR\\n- Max pace 10s 94% HR\\n- Easy pace 10s 70% HR\\n\\nTransition 1x\\n- Press lap Transition (prep sled) 60s\\n\\nPrep circuit 1x\\n- Press lap sled push 12.5meter open weight -50kg 60s 79% HR\\n- Press lap reverse lunges 20x 60s 79% HR\\n- Rest 30s\\n\\nHYROX Power Circuits Part1 2x\\n- sled push 25meter open weight 120s 87% HR\\n- farmers carry 100meter open weight 120s 87% HR\\n\\nRest 1x\\n- Press lap Rest (change equipment) 2m\\n\\nCooldown\\n- Press lap warm-down or stretch 5m 70% HR\",\n  \"externalId\": \"hyrox-circuits-75min_1759504290592\"\n}\n```\n\n## Running Formatting\n\n### Rules\n- NO \"Press lap\" prefix\n- NO transitions\n- Format: `- [Pace type] (RPE #) [pace min/km and km/h], [duration] [%Pace] Pace [%HR] HR`\n- Warmup section: `Warm up 1x` (with space)\n- Main section: `Main set 1x`\n- HR ranges allowed: `50-75% HR`\n\n### Structure Example (formatted as workoutDescription string)\n```\nWarm up 1x\\n- Personal mobilisation and activation exercises 5m 50% HR\\n\\nMain set 1x\\n- Easy pace (RPE 6) 5:30min/k, 10m 80% Pace 50-75% HR\\n- 15K pace (RPE 8-8,5) 4:55 min/k, 8m 90% Pace 87-91% HR\\n- Easy pace (RPE 6) 5:30min/k, 2m 80% Pace 75% HR\\n- 10K pace (RPE 9) 4:30 min/k, 4m 98% Pace 94% HR\\n\\nCooldown\\n- Easy pace jog or walk (RPE 6) 5:30min/k, 5m 80% Pace 50-70% HR\n```\n\n### Complete JSON Example (Running)\n```json\n{\n  \"workoutDate\": \"2025-10-02T00:00:00\",\n  \"workoutName\": \"Tempo run W2\",\n  \"workoutType\": \"Run\",\n  \"workoutDescription\": \"Warm up 1x\\n- Personal mobilisation and activation exercises 5m 50% HR\\n\\nMain set 1x\\n- Easy pace (RPE 6) 5:30min/k, 10m 80% Pace 50-75% HR\\n- 15K pace (RPE 8-8,5) 4:55 min/k, 8m 90% Pace 87-91% HR\\n- Easy pace (RPE 6) 5:30min/k, 2m 80% Pace 75% HR\\n- 10K pace (RPE 9) 4:30 min/k, 4m 98% Pace 94% HR\\n- Easy pace (RPE 6) 5:30min/k, 1m 80% Pace 75% HR\\n\\nCooldown\\n- Easy pace jog or walk (RPE 6) 5:30min/k, 5m 80% Pace 50-70% HR\",\n  \"externalId\": \"tempo-run-w2-35min_1728123456789\"\n}\n```\n\n## Processing Steps\n\n1. **Extract metadata from input**:\n   - **Date**: Look for date patterns (e.g., \"27th September, 2025\", \"October 2, 2025\")\n     - Convert to ISO format: YYYY-MM-DDT00:00:00\n     - If no date found, use \"2025-10-03T00:00:00\" (current date)\n   - **Workout name**: Extract from title line (e.g., \"**Tempo run W2 (35 min.)**\" \u2192 \"Tempo run W2\")\n     - Remove formatting (**, parentheses with duration)\n     - Keep week numbers (#2, W2, Week 2)\n   - **Workout type**: Analyze content to determine correct type\n     - Strength/hybrid/HYROX \u2192 `Workout`\n     - Running with paces \u2192 `Run`\n     - Cycling \u2192 `Ride` or `VirtualRide`\n     - Default \u2192 `Workout`\n\n2. **Parse workout sections**: Identify warmup, main blocks (A, B, C, etc.), cooldown\n   - Preserve section names from input\n   - Note if circuits, supersets, or progressive\n\n3. **Check for special patterns**: \n   - Warmup intervals to expand (steady/hard/easy)\n   - Progressive circuits (changing weights/loads)\n   - Supersets (X1/X2)\n   - Machine sequences (need \"Transition machine\" steps)\n   - Long rest periods (need descriptions in Rest sections)\n\n4. **Extract exercise details**: For each exercise get:\n   - Name, sets\u00d7reps, RPE, weight, distance, duration, tempo, RIR\n\n5. **Expand intervals**: Convert warmup patterns like \"3x (10s hard / 10s easy)\" into separate pace lines\n\n6. **Apply formatting rules**: \n   - Use correct section headers with rounds\n   - Apply \"Press lap\" rules based on section type\n   - Format weight info outside parentheses\n   - Use \"meter\" not \"m\"\n   - Add Transition/Rest sections with descriptions\n\n7. **Convert all values**: \n   - Sets \u2192 rounds in header (1x for progressions)\n   - Reps \u2192 \"Xx\" format  \n   - Time \u2192 seconds (s) or minutes (m)\n   - RPE \u2192 HR percentage\n   - Calculate machine work time from distance + pace\n\n8. **Add transition steps**: Between machines and between major workout blocks and between supersets\n\n9. **Build workoutDescription**: \n   - Combine all formatted lines\n   - Use `\\n` for single line breaks\n   - Use `\\n\\n` for blank lines between sections\n   - Ensure proper JSON escaping\n\n10. **Generate externalId**: \n    - Convert workout name to lowercase\n    - Replace spaces with hyphens\n    - Remove special characters\n    - Add underscore + Unix timestamp (milliseconds, 13 digits)\n    - Example: \"Tempo run W2\" \u2192 \"tempo-run-w2_1728123456789\"\n\n11. **Output JSON**: Return complete, valid JSON object with all fields\n\n## Critical Requirements\n\n**JSON Output:**\n- \u2713 Always return valid JSON object\n- \u2713 Use `\\n` for line breaks in workoutDescription\n- \u2713 Use `\\n\\n` for blank lines between sections\n- \u2713 Include all required fields: workoutDate, workoutName, workoutType, workoutDescription, externalId\n- \u2713 workoutType must be on of the allowed values\n- \u2713 externalId format: workout-name-kebab-case_timestamp\n\n**Strength/Hybrid:**\n- \u2713 Use descriptive section names from input\n- \u2713 \"Transition\" (English) not \"Transitie\"\n- \u2713 \"Press lap\" for strength exercises and machines with distance/pace\n- \u2713 NO \"Press lap\" for warmup pace intervals or short rest\n- \u2713 Weight info outside parentheses: `open weight -50kg`\n- \u2713 Distance as \"meter\" not \"m\"\n- \u2713 Expand warmup intervals with pace descriptors (Steady/Max/Easy pace)\n- \u2713 Progressive circuits as 1x with all steps listed\n- \u2713 Add \"Transition machine 10s\" between machine changes\n- \u2713 Long rest as \"Rest 1x\" sections with descriptions\n- \u2713 NO RPE in superset headers\n- \u2713 Capitalize section headers: Transition, Rest, Warmup, Cooldown\n- \u2713 Include contextual descriptions in parentheses for transitions/rest\n\n**Running:**\n- \u2713 Start \"Warm up 1x\" (with space), end \"Cooldown\"  \n- \u2713 Main section always \"Main set 1x\"\n- \u2713 NO \"Press lap\", NO transitions\n- \u2713 Include: pace (min/k), %Pace, %HR\n- \u2713 HR ranges allowed (50-75%)\n\nOutput only the JSON object. Do not include explanatory text before or after the JSON."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "19bd22df-5298-4d3f-9a4c-5d5d300b4519",
      "name": "PrepareAgentOutputToWorkoutArray",
      "type": "n8n-nodes-base.code",
      "position": [
        768,
        -592
      ],
      "parameters": {
        "jsCode": "const items = [];\n\nfor (const output of $input.all()) {\n  const workoutArray = [{\n    \"category\": \"WORKOUT\",\n    \"type\": output.json.output.workoutType,\n    \"start_date_local\": output.json.output.workoutDate,\n    \"name\": output.json.output.workoutName,\n    \"description\": output.json.output.workoutDescription,\n    \"external_id\": output.json.output.externalId\n  }];\n  \n  items.push({\n    json: { workoutArray: workoutArray }\n  });\n}\n\nreturn items;"
      },
      "typeVersion": 2
    },
    {
      "id": "00a1a6c1-7b1c-4c35-97d9-844804747141",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -624,
        -720
      ],
      "parameters": {
        "color": 4,
        "width": 352,
        "height": 288,
        "content": "## Start form\n- workout date\n- workout title\n- workout description"
      },
      "typeVersion": 1
    },
    {
      "id": "7eec26e9-c791-437f-a208-59814af6497c",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -240,
        -720
      ],
      "parameters": {
        "width": 448,
        "height": 288,
        "content": "## Get athlete info from intervals.icu\n- get through API\n- convert to usable format"
      },
      "typeVersion": 1
    },
    {
      "id": "db6641a3-b428-4175-bf0b-c6b0b06b4f97",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        240,
        -864
      ],
      "parameters": {
        "width": 416,
        "height": 848,
        "content": "## \ud83c\udfcb\ufe0f Workout Formatter for Intervals.icu\nConverts training prescriptions \u2192 structured intervals.icu JSON format\n\u2705 Handles: Running, Strength, HYROX, Cycling workouts\n\u2705 Auto-detects workout type\n\u2705 Formats with proper HR zones, pace, RPE conversion\n\u2705 Adds transitions, rest periods, circuit formatting\n\u2705 Outputs: JSON with workout steps ready for import\nUses Claude Opus 4.1 + structured output parser"
      },
      "typeVersion": 1
    },
    {
      "id": "255b3d8a-41e4-4162-b971-e14ead4c6c8d",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        688,
        -720
      ],
      "parameters": {
        "width": 448,
        "height": 288,
        "content": "## Get athlete info from intervals.icu\n- convert output array to workout array for API\n- call API to create workout"
      },
      "typeVersion": 1
    },
    {
      "id": "2766727f-d1cb-45ba-9285-86b7ee73c66e",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1168,
        -720
      ],
      "parameters": {
        "color": 4,
        "width": 352,
        "height": 288,
        "content": "## End form\n- simple form to mention workout is created"
      },
      "typeVersion": 1
    },
    {
      "id": "87972403-864d-4c3f-8b6d-5a8eac81fc3a",
      "name": "CreateWorkoutAPI",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        992,
        -592
      ],
      "parameters": {
        "url": "https://intervals.icu/api/v1/athlete/0/events/bulk?upsert=true",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "jsonBody": "={{ $json.workoutArray }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth"
      },
      "credentials": {
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "acdfa69d-bf35-4150-8eca-536b68900bb9",
      "name": "TheEndForm",
      "type": "n8n-nodes-base.form",
      "position": [
        1200,
        -592
      ],
      "parameters": {
        "options": {},
        "operation": "completion",
        "completionTitle": "The End - Bye"
      },
      "typeVersion": 2.3
    }
  ],
  "connections": {
    "GetAthleteInfo": {
      "main": [
        [
          {
            "node": "SetAthleteVars",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SetAthleteVars": {
      "main": [
        [
          {
            "node": "CreateIntervalsWorkoutAgent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CreateWorkoutAPI": {
      "main": [
        [
          {
            "node": "TheEndForm",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CreateWorkoutForm": {
      "main": [
        [
          {
            "node": "GetAthleteInfo",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anthropic Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "CreateIntervalsWorkoutAgent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Structured Output Parser",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "CreateIntervalsWorkoutAgent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "CreateIntervalsWorkoutAgent": {
      "main": [
        [
          {
            "node": "PrepareAgentOutputToWorkoutArray",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PrepareAgentOutputToWorkoutArray": {
      "main": [
        [
          {
            "node": "CreateWorkoutAPI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Transform training prescriptions into perfectly formatted Intervals.icu workouts using AI. This workflow automatically converts free-text workout descriptions into structured interval training sessions with proper heart rate zones, pace calculations, and exercise formatting.…

Source: https://n8n.io/workflows/9822/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

My workflow 53. Uses formTrigger, httpRequest, lmChatOpenAi, form. Event-driven trigger; 74 nodes.

Form Trigger, HTTP Request, OpenAI Chat +15
AI & RAG

Automatically generate SEO-optimized YouTube Titles, Descriptions, Tags & Hashtags – enriched with blog articles, affiliate links, and product recommendations!

Form Trigger, Form, YouTube +7
AI & RAG

Summary

Google Sheets, WordPress, HTTP Request +5
AI & RAG

This workflow contains community nodes that are only compatible with the self-hosted version of n8n.

Output Parser Structured, HTTP Request, Form Trigger +6
AI & RAG

This automated workflow transforms simple text descriptions into professional 8-second videos using Google's cutting-edge VEO3 AI model. Users submit video ideas through a web form, and the system aut

Anthropic Chat, Output Parser Structured, Agent +4