# Tredict API: Structured Workouts and Training Plans This document describes how to build structured workouts and training plans using the Tredict API. It assumes that authentication is already set up (see `setup.md`), regardless of whether you use a Personal Access Token or an OAuth2 application. Structured workouts are training sessions with defined steps, target zones, and repetitions. They can be added individually to an existing plan or created as part of a new plan. ## Required Scope `activityWrite` for all endpoints in this document. ## Two Ways to Place a Workout **Add a single workout to an existing plan:** ``` POST https://www.tredict.com/api/oauth/v2/plan/training ``` Body: ```json { "planId": "7K2mNxYzQpRsTvWuBcDeFg", "planTraining": { "day": 3, "time": 480, "structuredWorkout": { ... } } } ``` `day` is the day index within the plan (1 to 1024). `time` is minutes from midnight (0 to 1449), so `480` is 8:00 in the morning. **Create a complete new plan:** ``` POST https://www.tredict.com/api/oauth/v2/plan ``` Body: ```json { "plan": { "title": "Half Marathon Build", "description": "12-week plan with focus on threshold pace", "categories": ["building"], "targetgroups": ["intermediate"], "zonetypes": ["heartrate", "pace"], "language": "en" }, "planTrainings": [ { "day": 1, "time": 480, "structuredWorkout": { ... } }, { "day": 3, "time": 480, "structuredWorkout": { ... } } ] } ``` ## Anatomy of a structuredWorkout There are two types: note workouts (plain text entries without structure) and planned workouts (with steps and targets). ### Note Workout (e.g. Rest Day) ```json { "title": "Rest Day", "notes": "Optional mobility, otherwise free", "trainingType": "note", "sportType": "misc", "subSportType": "rest_day" } ``` `subSportType` can be `note` or `rest_day`. ### Planned Workout with Structure ```json { "title": "5x1000m Tempo", "notes": "Active recovery between reps", "trainingType": "planned", "sportType": "running", "subSportType": "street", "steps": [ ... ] } ``` For swimming workouts, also provide `poolLength` in meters. ## Steps The `steps` list contains two kinds of entries: base steps and repetition steps. ### Base Step ```json { "intensityType": "active", "durationType": "distance", "distance": 1000, "targetMode": "padding", "targetZoneType": "pace", "targets": { "pace": { "value": 240, "padding": 5 } }, "note": "Optional inline note" } ``` `intensityType` describes the role of the step within the workout. Possible values: - `warmup`: warm-up - `active`: main effort - `recover`: active recovery between efforts - `rest`: passive rest - `cooldown`: cool-down - `misc`: miscellaneous `durationType` defines what ends the step: - `time`: after seconds, value in `duration` - `distance`: after meters, value in `distance` - `open`: no fixed end, the athlete decides `progressionType` is optional, default is `steady`. `ramp` means the intensity rises across the step. ### Repetition Step ```json { "repetitions": 5, "steps": [ { "intensityType": "active", "durationType": "distance", "distance": 1000, "targetMode": "padding", "targetZoneType": "pace", "targets": { "pace": { "value": 240, "padding": 5 } } }, { "intensityType": "recover", "durationType": "time", "duration": 120, "targetMode": "padding", "targetZoneType": "pace", "targets": { "pace": { "value": 360, "padding": 30 } } } ] } ``` The inner `steps` block is repeated `repetitions` times. Nested repetitions are not supported, one level of repetition covers practically all real-world trainings. ## Helping Tredict Estimate Effort Accurately A step has two independent length dimensions: `duration` (time in seconds) and `distance` (meters). A pace target inside `targets` is not a third dimension, it is the ratio of the two. So if you provide `distance` plus a pace target, `duration` is mathematically determined and does not need to be sent. `durationType` decides which dimension ends the step on the watch. But the data you put in the step can be richer than what the watch needs. Tredict uses every length value you provide to compute a more accurate workload estimate before the workout is even executed. There are three ways to give Tredict enough information to derive both duration and distance: 1. Provide `duration` and `distance` directly. 2. Provide one of them plus a concrete pace target via `targetMode: "padding"` and `targets.pace`. 3. Provide one of them plus a relative effort target via `targetMode: "range"` and `targets.ftpa` (running) or `targets.ftp` (cycling). Tredict translates the relative effort into an expected pace using the user's stored capacities and derives the missing dimension from there. Option 3 is often the most elegant choice for plans intended for multiple athletes or for your own future self, because the workout scales automatically with the athlete's current threshold. A run defined as "30 minutes at 70 to 78 percent of FTPa" stays meaningful when fitness changes; a fixed pace does not. Example with relative effort, where Tredict can compute distance from duration and the FTPa range: ```json { "intensityType": "active", "durationType": "time", "duration": 2400, "targetMode": "range", "targetZoneType": "heartrate", "targets": { "hrMax": { "from": 0.70, "to": 0.78 }, "ftpa": { "from": 0.70, "to": 0.78 } } } ``` The watch executes the step by heart rate (because of `targetZoneType`), but Tredict uses the FTPa hint to estimate the resulting distance and load. Example with a concrete pace target, where Tredict can compute duration from distance and pace: ```json { "intensityType": "active", "durationType": "distance", "distance": 1000, "targetMode": "padding", "targetZoneType": "pace", "targets": { "pace": { "value": 240, "padding": 5 } } } ``` ## Target Zones: range vs. padding The most important concept when building workouts. `targetMode` can be `range` or `padding`, and it defines how the target zone is interpreted. **Prefer `range` with relative values whenever possible.** Workouts built on `hrMax`, `ftp`, or `ftpa` fractions scale automatically with the athlete's current capacities. The same plan stays meaningful as fitness changes, and works for any user without rewriting absolute numbers. Use `padding` with concrete values only when a specific pace, heart rate, or power matters, for example race-specific sessions or test protocols. ### range A span expressed as a fraction of the relevant capacity. Useful when the athlete trains based on threshold values and Tredict should compute absolute numbers from the stored capacities. ```json { "targetMode": "range", "targetZoneType": "heartrate", "targets": { "hrMax": { "from": 0.75, "to": 0.85 } } } ``` Available keys in the `targets` object: - `hrMax`: fraction of maximum heart rate (0.0 to 1.0) - `ftp`: fraction of FTP (Functional Threshold Power for cycling) - `ftpa`: fraction of FTPa (aerobic threshold power for running, mapped to pace) ### padding A concrete target value with tolerance up and down. Useful when the athlete should hit a specific pace, heart rate, or power. ```json { "targetMode": "padding", "targetZoneType": "pace", "targets": { "pace": { "value": 240, "padding": 5 } } } ``` Available keys in the `targets` object: - `pace`: seconds per kilometer. **Important: 240 means 4:00/km, not 2:40/km.** - `heartrate`: beats per minute - `power`: watts - `cadence`: steps per minute for running, revolutions per minute for cycling `padding` is the tolerance in the same unit as `value`. So seconds for pace, BPM for heart rate, watts for power. You can mix multiple targets in the same `targets` object, for example both a `pace` and a `heartrate` definition. The watch executes whichever one matches `targetZoneType`, and the additional targets are available as alternative views inside Tredict. `targetZoneType` defines what the watch actually executes and displays during the workout. The `targets` object can hold multiple target definitions at once (e.g. both `pace` and `heartrate`), and Tredict lets you switch between them in the web and app views without changing the step itself. Only the one matching `targetZoneType` is sent to the device. ## Sport and Sub-Sport Types The main categories are `running`, `cycling`, `swimming`, `misc`. Each has its own sub-types. Frequently used sub-types: - Running: `street`, `track`, `trail`, `treadmill` - Cycling: `road`, `mountain`, `gravel_cycling`, `indoor_cycling`, `spin` - Swimming: `lap_swimming`, `open_water` - Misc: `hiking`, `strength_training`, `yoga`, `walking` A complete list is in `https://www.tredict.com/llms-full.txt`. ## Complete Examples ### Easy Run by Heart Rate ```json { "title": "Easy Aerobic", "trainingType": "planned", "sportType": "running", "subSportType": "street", "steps": [ { "intensityType": "warmup", "durationType": "time", "duration": 600, "targetMode": "range", "targetZoneType": "heartrate", "targets": { "hrMax": { "from": 0.60, "to": 0.70 }, "ftpa": { "from": 0.60, "to": 0.70 } } }, { "intensityType": "active", "durationType": "time", "duration": 2400, "targetMode": "range", "targetZoneType": "heartrate", "targets": { "hrMax": { "from": 0.70, "to": 0.78 }, "ftpa": { "from": 0.70, "to": 0.78 } } }, { "intensityType": "cooldown", "durationType": "time", "duration": 300, "targetMode": "range", "targetZoneType": "heartrate", "targets": { "hrMax": { "from": 0.55, "to": 0.65 }, "ftpa": { "from": 0.55, "to": 0.65 } } } ] } ``` The watch executes the workout by heart rate. The FTPa ranges let Tredict estimate the expected distance and workload based on the athlete's current threshold, and they also serve as an alternative view inside the Tredict UI. ### 5x1000m Tempo with Active Recovery ```json { "title": "5x1000m Tempo", "trainingType": "planned", "sportType": "running", "subSportType": "track", "steps": [ { "intensityType": "warmup", "durationType": "time", "duration": 900, "targetMode": "range", "targetZoneType": "pace", "targets": { "ftpa": { "from": 0.60, "to": 0.70 } } }, { "repetitions": 5, "steps": [ { "intensityType": "active", "durationType": "distance", "distance": 1000, "targetMode": "range", "targetZoneType": "pace", "targets": { "ftpa": { "from": 0.95, "to": 1.00 } } }, { "intensityType": "recover", "durationType": "time", "duration": 120, "targetMode": "range", "targetZoneType": "pace", "targets": { "ftpa": { "from": 0.50, "to": 0.60 } } } ] }, { "intensityType": "cooldown", "durationType": "time", "duration": 600, "targetMode": "range", "targetZoneType": "pace", "targets": { "ftpa": { "from": 0.55, "to": 0.65 } } } ] } ``` The watch executes by pace, but the actual numbers are computed from the athlete's FTPa. The same plan stays correct as fitness changes, and works for any user, regardless of their current pace level. ### Ramp Test on the Bike ```json { "title": "Ramp Test", "trainingType": "planned", "sportType": "cycling", "subSportType": "indoor_cycling", "steps": [ { "intensityType": "warmup", "durationType": "time", "duration": 600, "targetMode": "range", "targetZoneType": "power", "targets": { "ftp": { "from": 0.50, "to": 0.60 } } }, { "intensityType": "active", "durationType": "time", "duration": 1200, "progressionType": "ramp", "targetMode": "range", "targetZoneType": "power", "targets": { "ftp": { "from": 0.70, "to": 1.20 } } }, { "intensityType": "cooldown", "durationType": "time", "duration": 600, "targetMode": "range", "targetZoneType": "power", "targets": { "ftp": { "from": 0.40, "to": 0.55 } } } ] } ``` ## Rescheduling an Existing Workout Move a planned workout to a different date without changing its content: ``` POST https://www.tredict.com/api/oauth/v2/plannedTraining/changeDate ``` Body: ```json { "trainingId": "3F321GQqJjViDBnJNnSCrh", "date": "2026-05-12T08:00:00Z" } ``` The date must be in the future. Title, notes, and steps stay unchanged. ## Common Pitfalls - Pace values are seconds per kilometer. 4:00/km is 240, not 4.0 or 400. - In `range` mode the values are fractions between 0 and 1, not percentages. 75% is 0.75, not 75. - `time` in a plan training entry refers to the time of day, while `duration` inside a step refers to a duration in seconds. Mixing them up produces absurd values. - Swimming workouts need `poolLength` at the workout level, not in the step.