upload_workout
Upload structured workout JSON to Garmin Connect to create a new workout with configured steps, targets, and sport type.
Instructions
Upload a workout from JSON data
Creates a new workout in Garmin Connect from structured workout data.
IMPORTANT: Step types must use Garmin's DTO format:
Use "ExecutableStepDTO" for regular steps (warmup, interval, cooldown, recovery)
Use "RepeatGroupDTO" for repeat/interval groups with numberOfIterations. Always include endCondition with conditionTypeId 7 and conditionTypeKey "iterations"; omitting conditionTypeId causes the API to silently corrupt the repeat count.
IMPORTANT: Heart rate targets come in two forms:
Named zone (e.g. Zone 2): set targetType to "heart.rate.zone" and use "zoneNumber" (1-5). Do NOT put the zone number in targetValueOne.
Custom HR range (e.g. 105-143 bpm): set targetType to "heart.rate.zone" and use "targetValueOne" (low bpm) / "targetValueTwo" (high bpm). Do NOT set "zoneNumber". This matches Garmin Connect's "Custom" heart rate target. For non-HR targets (pace, power, cadence), use targetValueOne/targetValueTwo directly. Target values are fields on the workout step, alongside targetType; do not put targetValueOne, targetValueTwo, or zoneNumber inside the targetType object. Use either zoneNumber or targetValueOne/targetValueTwo, not both. Garmin silently discards a custom range when a named zone is also present.
Note: a safety check converts targetValueOne 1-5 to zoneNumber when zoneNumber is missing, to catch the common mistake of putting a zone index in targetValueOne. Typical bpm values (e.g. 105, 143) are not affected.
IMPORTANT: Target type IDs and keys must match Garmin's canonical mapping. Garmin treats workoutTargetTypeId as authoritative, so mismatches are rejected before upload. Known mappings:
workoutTargetTypeId 1 -> "no.target"
workoutTargetTypeId 2 -> "power.zone" (cycling power zone 1-7, use zoneNumber)
workoutTargetTypeId 4 -> "heart.rate.zone"
workoutTargetTypeId 6 -> "pace.zone" (running/swim) OR "power.between" (cycling)
IMPORTANT: For cycling power targets use the correct target type:
Power zone (zone 1-7 based on FTP %): use workoutTargetTypeId 2, key "power.zone", and "zoneNumber" (1-7).
Absolute watt range (e.g. 200-250 W): use workoutTargetTypeId 6, key "power.between", and "targetValueOne" (low watts) / "targetValueTwo" (high watts). Using workoutTargetTypeId 2 with key "power.between" is a silent Garmin bug: the workout uploads but Garmin stores it as "power.zone" and the intent is lost.
Use {"workoutTargetTypeId": 4, "workoutTargetTypeKey": "heart.rate.zone"} with targetValueOne/targetValueTwo for custom heart-rate ranges.
IMPORTANT: Sport type IDs for workouts (different from activity API!):
1 = running, 2 = cycling, 5 = strength_training, 6 = cardio, 11 = walking
IMPORTANT: End condition IDs and keys must match Garmin's canonical mapping. Garmin treats conditionTypeId as authoritative, so mismatches such as {"conditionTypeId": 4, "conditionTypeKey": "heart.rate"} are rejected before upload because Garmin would interpret them as "calories". Use {"conditionTypeId": 6, "conditionTypeKey": "heart.rate"} for heart-rate end conditions.
Available Templates: Instead of building workout JSON from scratch, you can use these MCP resources as starting points:
workout://templates/simple-run - Basic warmup/run/cooldown structure
workout://templates/interval-running - Interval training with repeat groups
workout://templates/tempo-run - Tempo run with heart rate zone targets
workout://templates/strength-circuit - Strength training with exercises, reps, rest
workout://reference/structure - Complete JSON structure reference with all fields
Access these resources using your MCP client's resource reading capability, modify the template as needed, and pass the resulting JSON as the workout_data parameter.
Strength training workouts require these additional fields on each exercise step:
"category": exercise category (e.g. "BENCH_PRESS", "PULL_UP", "CURL", "SHOULDER_PRESS", "ROW", "SQUAT", "DEADLIFT", "TRICEPS_EXTENSION", "PLANK", "LUNGE", "CARDIO")
"exerciseName": specific exercise (e.g. "BARBELL_BENCH_PRESS", "PULL_UP", "DUMBBELL_BICEPS_CURL", "DUMBBELL_SHOULDER_PRESS", "BENT_OVER_ROW_WITH_DUMBELL", "BODY_WEIGHT_DIP", "BARBELL_SQUAT", "BARBELL_DEADLIFT")
"weightValue" (optional): weight as number (e.g. 24.0)
"weightUnit" (optional): {"unitId": 8, "unitKey": "kilogram", "factor": 1000.0} Use endCondition reps (conditionTypeId: 10) for exercises, rest (stepTypeId: 5) between sets.
Example strength exercise step: { "type": "ExecutableStepDTO", "stepOrder": 1, "stepType": {"stepTypeId": 3, "stepTypeKey": "interval"}, "endCondition": {"conditionTypeId": 10, "conditionTypeKey": "reps"}, "endConditionValue": 10.0, "targetType": {"workoutTargetTypeId": 1, "workoutTargetTypeKey": "no.target"}, "category": "BENCH_PRESS", "exerciseName": "BARBELL_BENCH_PRESS", "weightValue": 60.0, "weightUnit": {"unitId": 8, "unitKey": "kilogram", "factor": 1000.0} }
Example running workout with HR zone target: { "workoutName": "My Workout", "sportType": {"sportTypeId": 1, "sportTypeKey": "running"}, "workoutSegments": [{ "segmentOrder": 1, "sportType": {"sportTypeId": 1, "sportTypeKey": "running"}, "workoutSteps": [{ "type": "ExecutableStepDTO", "stepOrder": 1, "stepType": {"stepTypeId": 3, "stepTypeKey": "interval"}, "endCondition": {"conditionTypeId": 2, "conditionTypeKey": "time"}, "endConditionValue": 1200.0, "targetType": {"workoutTargetTypeId": 4, "workoutTargetTypeKey": "heart.rate.zone"}, "zoneNumber": 3 }] }] }
Args: workout_data: Dictionary containing workout structure (name, sport type, segments, etc.)
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| workout_data | Yes |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |