Overview
Terra Planned Workouts API
Push structured workouts directly to your users' fitness devices.
Documentation
Introduction & Quick Start - Get started in 5 minutes
Pricing & Access - Add-on cost, credits, and what works without it
Core Concepts - Workout structure, blocks, steps, targets
Sport-Specific Examples - Running, cycling, swimming, strength
Athlete Parameters - Personalizing workouts per user
Provider Compatibility - What works where
Coercion Warnings - Handling feature limitations
Exercise Reference - Exercise names and how they map across providers
Garmin & Hevy Exercise Reference - Provider exercise catalogs
Quick Links
OpenAPI Specification (to be added soon)
Garmin Exercise Names reference
API Endpoints
POST
/workouts
Create workout template
GET
/workouts
List all templates
GET
/workouts/{id}
Get template details
DELETE
/workouts/{id}
Delete template
POST
/workouts/{id}/plan?user_id=X
Schedule workout to user
GET
/plannedWorkouts/{id}
Get a planned workout created through Terra
GET
/plannedWorkouts?user_id=X
List Terra-created and provider-created workouts (dates optional)
PATCH
/plannedWorkouts/{id}?user_id=X
Update scheduled date (only planned_date, not athlete params)
DELETE
/plannedWorkouts/{id}?user_id=X
Remove scheduled workout
A template's estimated_tss, estimated_intensity_factor, estimated_distance_meters and estimated_calories are forwarded to TrainingPeaks when you plan or reschedule the workout. TrainingPeaks ignores TSS, intensity factor and duration whenever any step target is something other than RPE: it derives them from the athlete's own thresholds instead, and Terra returns warnings on workout.estimated_tss, workout.estimated_intensity_factor and workout.estimated_duration_seconds. POST /workouts rejects an estimated_tss outside 0 to 9999 and an estimated_intensity_factor outside 0 to 5.
Deleting a workout template cascades to all scheduled workouts
When you call DELETE /workouts/{id}, Terra automatically removes all planned workouts linked to that template from every connected provider (e.g. Garmin, Suunto, Wahoo) before deleting the template itself. This cannot be undone. If you only want to unschedule a workout for a specific user, use DELETE /plannedWorkouts/{id} instead.
Reading scheduled workouts
GET /plannedWorkouts?user_id=X returns everything on the user's calendar, from both origins, in one shape. Every item carries the workout body under workout (name, sport, step_blocks, estimates), plus planned_date, provider_workout_id and warnings:
Workouts you scheduled through Terra: identified by a
planned_workout_id. Use it with theGET/PATCH/DELETE /plannedWorkouts/{id}endpoints. The workout body is inline underworkoutandworkout_idnames the template it was planned from.Workouts created on the provider's side (e.g. by a coach on TrainingPeaks): marked
is_external: true. These are read live from the provider and are read-only.planned_workout_idandworkout_idare null and they cannot be addressed by id. The body arrives underworkoutlike every other item: title, sport, structured steps with targets, and planned estimates such asestimated_duration_seconds,estimated_tssandestimated_intensity_factor. Useprovider_workout_idas their stable identifier; a completed workout's activity carries the same id insummary_id.
Provider-created workouts are listed for Garmin, Wahoo, Suunto, TrainingPeaks and Intervals.icu. Intervals.icu is read-only for planned workouts: you can list what the athlete has planned there, but you cannot push to it. COROS, Zepp, Huawei and Apple have no way to read a calendar back, and Hevy has no calendar (a workout pushed to Hevy becomes a library routine and carries no date), so for those providers the list contains only the workouts you scheduled through Terra. A workout you pushed through Terra that the provider echoes back is listed once, as the Terra row.
Items also carry coercion_warnings and details, the older forms of warnings and workout. Both are deprecated: read warnings and workout, which use the same field names and values as GET /workouts/{workout_id}.
Two response headers report the live provider read:
X-Terra-Provider-Fetch
ok, skipped, rate_limited, unauthorized, unavailable, timeout, failed
X-Terra-Provider-Data-Truncated
true when provider-side workouts could not be included
skipped means the provider does not expose planned workouts, or the Planned Workouts add-on is not active. Anything other than ok also sets X-Terra-Provider-Data-Truncated: true. The call still returns 200 with the workouts Terra has stored, so check the headers before treating a short list as an empty calendar.
warnings is an array of {path, message} entries describing what had to change between your template and the provider's format: on workouts you pushed, what the push adjusted; on provider-created workouts, what reading them back adjusted.
Pass a date window to see upcoming workouts
Without start_date/end_date, provider-side workouts default to the trailing 30 days, so upcoming ones will not appear. Workouts you scheduled through Terra are not windowed when the dates are omitted. Pass an explicit window (e.g. today through next week) to list what is ahead: both bounds are planned dates and both are inclusive. Some providers also limit how far ahead they expose planned workouts.
A planned workout the user has completed leaves this list and is delivered as a completed activity instead.
Last updated
Was this helpful?