DSL Syntax Guide

Describe an interval session as plain text; VelocityStack finds matching efforts in your rides.

Structure

4x
work 3min ±30% @ 90-110%FTP
recovery 2min ±50% @ <55%FTP

The first line is the repeat count (optional: without it the steps run once, in order); each other line is a step in the form role value unit [±tolerance%] @ band. Put 6x on its own line further down to repeat the steps below it until a blank line or end; groups can be nested up to 3 levels.

work | recovery
— Step role. "work" is the effort you want analysed; "recovery" is the rest between efforts.
<value><unit>
— Length of the step. Duration units: h, min, sec (also hr/hrs/hour/hours, mins/minutes and s/secs/seconds; 2h = 120min). Distance units: km, m ("m" is always metres, never minutes). Decimals are allowed (1.5min).
±<n>%
— Optional tolerance (0-100%) on the length when matching the step against an activity. Left out it is ±0%, so "work 15min @ Z3" and "work 15min ±0% @ Z3" are the same. It is separate from the band: a bare target like 50W is still 50W ±5%.
@ <band>
— Intensity target (power, speed, heart rate or cadence): a range, an upper/lower bound, or "best".

Bands (the part after @)

  • 90-110%FTP — Range as a percentage of your FTP.
  • <55%FTP — Upper bound only (at most 55% FTP) — typical for recoveries.
  • >80%FTP — Lower bound only (at least 80% FTP).
  • 250-300W — Absolute watts range; works without an FTP set. Bounds (<150W, >200W) work too.
  • 30-40km/h — Speed range in km/h. Bounds (<20km/h, >45km/h) work too.
  • 150-165bpm — Average heart rate range in beats per minute. Bounds (<120bpm, >170bpm) work too.
  • 80-90%HRmax — Heart rate as a percentage of the activity's max HR (the recorded max, or the highest value in the HR stream). Bounds work too.
  • 95-105%LT2 — Power as a percentage of your LT2 (lactate threshold 2) power, taken from the activity's own LT snapshot for its date. Bounds (<100%LT1, >100%LT2) work too. Set LT values in your profile settings; without them the run reports a message.
  • 85-100%LT1 — Same as above but relative to LT1 power. Use %LT1HR / %LT2HR (e.g. 97-103%LT2HR) for the average heart rate relative to LT1/LT2 heart rate.
  • Z3 — A power zone (Z1 to Z6) from your zone settings: Z3, Z2-Z3 (range), <Z2 (below) or >Z4 (above). Zones are built from LT1/LT2 (or estimated from FTP) as stored on the activity; without either the run reports a message.
  • 3-3.5W/kg — Power per kilogram of body weight, using the weight valid on the activity's date (set it under body weight in your profile). Bounds (<2W/kg, >4W/kg) work too; a bare target like 3W/kg means 3 W/kg ±5% (stored as 2.85-3.15W/kg). Without a weight the run reports a message.
  • 90-100rpm — Average cadence range in rpm. Bounds (<60rpm, >110rpm) work too.
  • best / max — No threshold: find the single highest-average window of that length. On duration steps this means power.
  • best power / best speed — Required for "best"/"max" on distance steps, which have no default metric.

Warm-up, cool-down, sets, multi-step blocks and contiguous steps

  • 6x — A repeat count on its own line (not the first line) starts a repeated group: the steps below it repeat 6 times. The group ends at the next blank line or an "end" line.
  • end — Closes the innermost repeated group, so steps can follow it directly (and a group can contain another group). Groups nest up to 3 levels; a blank line closes all open groups.
  • 3 sets of 5x — First line: 3 sets, each with 5 repeats of the block. Reps are numbered within each set.
  • set recovery 5min ±30% @ <55%FTP — Optional, only with sets: the rest searched for between sets (never after the last one). Same syntax as a step, but without then.
  • then work 1min ... — Prefix a step with "then" to make it start exactly where the previous step ended (over-unders, pyramids, ramps). Not allowed on the first step.
  • warmup 15min — Ignore the first 15 minutes of the activity (h, min or sec). At most once.
  • metrics np, vi, hr_recovery(30sec), decoupling — Template runs always report avg W/kg, avg power, NP, avg speed, avg cadence and avg HR where data exists. Choose extra summary metrics per work effort and for the run: avg_power, max_power, np, vi, if, avg_wkg, max_wkg, np_wkg, avg_hr, max_hr, avg_cadence, max_cadence, avg_speed, max_speed, work_kj, pacing, dropoff, decoupling, time_below_lt1, time_lt1_lt2, time_above_lt2, time_below_lt1_hr, time_lt1_lt2_hr, time_above_lt2_hr, time_z1, time_z2, time_z3, time_z4, time_z5, time_z6, hr_recovery, estimated_ftp, estimated_lt2_power, estimated_lt2_hr, estimated_lt1_power, estimated_lt1_hr. hr_recovery takes an optional window (default 60sec); np_wkg, avg_wkg and max_wkg are power per kg of the activity weight; the time_below/lt1/above_* metrics are seconds in lactate zones (power, or heart rate with the _hr suffix); time_z1 to time_z6 are seconds in each power zone; the estimated_* metrics propose FTP / LT1 / LT2 from a threshold test, with the test protocol in brackets, e.g. estimated_ftp(20min), estimated_ftp(8min), estimated_ftp(ramp), estimated_lt2_hr(30min), estimated_lt1_power(steady). At most once.
  • pattern repeated_bursts — Use instead of the repeat line to run a built-in detector: repeated_bursts, flying_sprint, sustained_tt, standing_start. Then add its option lines (repeated_bursts: count, duration, recovery window; flying_sprint: distance; sustained_tt: min duration, distances, lap; standing_start: distance). Only metrics is also allowed.
  • lap 250m — Track context: report per-lap split times (full laps only, from the speed stream) for every work effort, plus lap_count, best_lap_sec, slowest_lap_sec and avg_lap_sec for the run. At most once; also valid under pattern sustained_tt.
  • time & distance results — Automatic, no line needed. Duration steps (20min) report the distance covered plus average speed; distance steps (3km) and the legacy pursuit/flying/standing patterns report the exact elapsed time (interpolated, shown as a race time) plus average speed. Needs a speed stream; without one the run says so. Run summaries add best/avg/total time or distance, and a faster time counts as an improvement in comparisons.
  • cooldown 10min — Ignore the last 10 minutes of the activity (h, min or sec). At most once.

A repeat can contain several work/recovery lines. They are matched one after another in the order written; prefix a step with then to require it to start right where the previous one ended.

Write 3 sets of 5x on the first line for sets: the block repeats 5 times per set, three times. An optional set recovery line is the rest searched for between sets.

Duration vs. distance steps

Use h/min/sec to match by time (work 2h @ Z2), or km/m to match by distance (e.g. a 4km individual pursuit or a 200m flying sprint). Because a distance window has no inherent power-vs-speed preference, best/max must be written as best power or best speed on those steps.

Examples

Plain list of steps (no repeat line)

Without a repeat line every step is simply done once, in order. Names are case-insensitive and a bare target like 50W means 50W ±5%.

work 15min ±0% @ Z3
recovery 8min ±0% @ <Z3
Work 15min ±0% @ Z3
Recovery 8min ±0% @ 50W
work 15min ±0% @ Z3
Hours, no tolerance

h is hours (1.5h = 90 minutes). The ±tolerance is optional and defaults to ±0%, so these lines match exactly like "work 2h ±0% @ Z2".

work 2h @ Z2
recovery 10min @ <Z2
work 1.5h @ Z3
Repeated group inside a list

A "6x" line repeats the steps below it until the next blank line.

recovery 20min ±0% @ Z1

6x
work 40sec ±0% @ Z5
recovery 20sec ±0% @ 50W

recovery 10min ±0% @ Z2
Nested intervals

A group inside a group; "end" closes the inner one so the recovery belongs to the outer repeat. (When the first line is a repeat count it applies to the whole description.)

3x
work 10min ±10% @ Z3
2x
work 1min ±5% @ Z5
recovery 1min ±20% @ <Z2
end
recovery 5min ±30% @ <Z2
Repeated VO2max intervals (%FTP-banded)

Four 3-minute efforts at 90-110% FTP, each followed by 2 minutes of easy spinning.

4x
work 3min ±30% @ 90-110%FTP
recovery 2min ±50% @ <55%FTP
Threshold block

One continuous 20-minute effort at 95-105% FTP.

1x
work 20min ±15% @ 95-105%FTP
3 x 10min sweet spot

Longer repeats with a lower-bound-only target, so anything above 88% FTP counts.

3x
work 10min ±10% @ >88%FTP
recovery 5min ±30% @ <60%FTP
Short sprints

Ten 15-second sprints at or above 150% FTP with long recoveries.

10x
work 15sec ±20% @ >150%FTP
recovery 3min ±50% @ <55%FTP
Decisive climb / final sprint (one-shot, no FTP needed)

Find the best 5-minute effort in the ride; no recovery line and no threshold.

1x
work 5min ±40% @ best
Absolute watts (no FTP set)

Use watts directly when the rider has no FTP configured.

5x
work 30sec ±20% @ 250-300W
recovery 90sec ±50% @ <150W
Speed-banded effort

Target speed instead of power, e.g. for motor-paced or track efforts.

2x
work 2km ±10% @ 45-50km/h
recovery 3min ±50% @ <30km/h
Heart-rate-controlled blocks

Work by heart rate (bpm or %HRmax) instead of power; needs an HR stream on the activity.

3x
work 10min ±10% @ 150-165bpm
recovery 5min ±30% @ <70%HRmax
Lactate threshold intervals

Work relative to your LT2 power with recoveries below LT1, reporting time spent between and above the thresholds. Needs LT1/LT2 in your profile.

4x
work 8min ±15% @ 95-105%LT2
recovery 4min ±40% @ <100%LT1
metrics avg_power, time_lt1_lt2, time_above_lt2
Lactate threshold heart-rate blocks

Heart rate relative to LT2 / LT1 heart rate.

3x
work 10min ±15% @ 97-103%LT2HR
recovery 5min ±40% @ <100%LT1HR
metrics time_lt1_lt2_hr, time_above_lt2_hr
Power zone intervals

Work in a power zone with recoveries below it, reporting time in each zone. Needs LT1/LT2 or an FTP.

3x
work 15min ±10% @ Z3
recovery 5min ±40% @ <Z3
metrics avg_power, time_z2, time_z3, time_z4
Power-to-weight blocks

Work at a W/kg target. Needs a body weight in your profile.

2x
work 20min ±15% @ 3.5-4W/kg
recovery 5min ±40% @ <2W/kg
metrics avg_power, avg_wkg, np_wkg
Threshold test: estimate FTP and LT2

Find the best 20 minutes and propose FTP (x 0.95) and LT2 heart rate. Protocols: 20min, 8min, ramp, 30min, 60min; estimated_lt1_* use "steady" for a decoupling-based LT1 test. Proposals can be reviewed and applied to your profile from the run card.

1x
work 20min ±0% @ best
metrics avg_power, avg_hr, pacing, estimated_ftp(20min), estimated_lt2_hr(20min)
Cadence drills

High-cadence work bouts with low-cadence recoveries.

4x
work 5min ±15% @ 100-110rpm
recovery 3min ±30% @ <70rpm
Ignore warm-up and cool-down

Skip the first 15 and last 10 minutes so openers and a final sprint do not count as reps.

3x
warmup 15min
cooldown 10min
work 10min ±10% @ 90-100%FTP
recovery 5min ±30% @ <55%FTP
Multi-step block

Each repeat is a 2-minute tempo step, a 1-minute hard step, then a recovery.

3x
work 2min ±10% @ 95-100%FTP
work 1min ±10% @ 105-110%FTP
recovery 3min ±30% @ <55%FTP
Over-unders

Back-to-back under/over/under steps, then a recovery; "then" keeps them contiguous.

4x
work 2min ±5% @ 95-100%FTP
then work 1min ±5% @ 105-110%FTP
then work 2min ±5% @ 95-100%FTP
recovery 4min ±30% @ <55%FTP
Pyramid

Ascending then descending steps with no rest in between.

1x
work 1min ±5% @ 100-105%FTP
then work 2min ±5% @ 105-110%FTP
then work 3min ±5% @ 110-115%FTP
then work 2min ±5% @ 105-110%FTP
then work 1min ±5% @ 100-105%FTP
Ramp

Stepwise increasing targets.

1x
work 1min ±5% @ 50-60%FTP
then work 1min ±5% @ 60-70%FTP
then work 1min ±5% @ 70-80%FTP
then work 1min ±5% @ >80%FTP
Sets: 3 sets of 5 short efforts

Five 30-second efforts with 30 seconds easy, repeated in three sets separated by 5 minutes of rest.

3 sets of 5x
work 30sec ±20% @ 120-140%FTP
recovery 30sec ±50% @ <55%FTP
set recovery 5min ±30% @ <55%FTP
Threshold with chosen metrics

Report normalized power, variability, power:HR decoupling and HR recovery 30 seconds after each rep.

3x
work 10min ±10% @ 90-100%FTP
recovery 5min ±30% @ <55%FTP
metrics np, vi, decoupling, hr_recovery(30sec), dropoff, pacing
Pursuit pacing: best power over 4 x 1km

Distance-keyed steps need an explicit metric after "best".

4x
work 1km ±5% @ best power
recovery 2min ±50% @ <55%FTP
Flying 200: best speed over a single 200m

One-shot distance step looking for the fastest 200m.

1x
work 200m ±10% @ best speed
Built-in pattern: three 3-minute VO2max bursts

Legacy fixed patterns are written with "pattern" and options instead of steps.

pattern repeated_bursts
count 3
duration 3min ±45sec
recovery window 1min
Built-in pattern: track pursuit

Sustained time trial over 2, 3 and 4 km with 250m lap splits and decoupling.

pattern sustained_tt
distances 2km, 3km, 4km
lap 250m
metrics decoupling
Kilo with 250m lap splits

Fastest 1km on the track, with a split time for every 250m lap.

1x
work 1km ±10% @ best speed
lap 250m

Rules

  • The repeat line is optional. Without it the steps run once, in order. A first line "4x" (1 or greater) repeats the whole description; "3 sets of 5x" adds sets.
  • Every other line is one step, a warmup/cooldown line, or a repeated group ("6x" ... blank line/"end"); at least one "work" step is required.
  • Repeated groups nest up to 3 levels, repeat 1-100 times, and expand to at most 200 steps in total.
  • A repeat can have several work/recovery steps; they are matched in the order written.
  • Step values must be greater than 0; tolerance (optional, default ±0%) must be 0-100%.
  • In a range, the minimum must be lower than the maximum.
  • The tolerance sign is "±" (plus-minus).
  • "best"/"max" on km/m steps must say "power" or "speed".

Common mistakes

1x
work 1km ±10% @ best speed
lap 250m
lap 125m

Only one "lap" line is allowed.

pattern flying_sprint
count 3

That option does not belong to this pattern. flying_sprint only accepts "distance" (and metrics).

1x
work 5min ±10% @ best
metrics np, speedy

Unknown metric. Use the names listed under "metrics".

3x
2x
1x
1x
1x
work 1min ±5% @ Z3

Repeated groups can be nested at most 3 levels deep.

2x
end

A repeated group needs steps, and "end" only closes a group that is open.

1x
work 1km ±5% @ best

Distance steps need a metric: "best power" or "best speed".

1x
work 3min +-30% @ 90-110%FTP

Use the single "±" character, not "+-".

1x
work 3min ±30% @ 110-90%FTP

The minimum must be lower than the maximum.

1x
then work 3min ±5% @ 90-110%FTP

"then" needs a preceding step; remove it from the first step.

3x
work 1min ±5% @ best
set recovery 5min ±30% @ <55%FTP

"set recovery" needs a sets line first, e.g. "3 sets of 3x".