{
  "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
          }
        ]
      ]
    }
  }
}