How to send structured workouts to a Garmin watch from your app

Put intervals, pace targets and strength sessions on your athletes' Garmin watches with one API call, and know whether each one landed.

A training plan is more useful on the wrist than in an app. If you build a coaching product, a training planner or a running club tool, the feature your users ask for is "send it to my Garmin": intervals with pace targets that the watch walks them through, on the right day. This guide shows how to do that with one API call. ## What you would otherwise need Garmin's Training API is what puts workouts on a watch. It sits behind the Garmin Connect Developer Program, which [is not taking new applications](https://stridee.com/blog/garmin-developer-program-paused). With access, you would still write the encoder from your workout model to Garmin's step format, schedule each workout on the calendar as a second call, and handle the cases where Garmin rejects a step. With the [Stridee Platform](https://platform.stridee.com) the approval is already in place and the encoder is ours. Workout push is included on every plan. ## 1. Connect the athlete A workout goes to a user who has linked their Garmin account. That is one call to `POST /v1/connect` and one redirect, covered in [How to integrate Garmin data into your app](https://stridee.com/blog/how-to-integrate-garmin-api). One thing matters here: on Garmin's consent screen the athlete has to leave **Workout Import** switched on. Without it the connection works for reading data and every push fails. ## 2. Send the workout Describe the session as a list of steps: ```json { "user_id": "4c9a7e15-6d3b-42f8-91c0-8e5b2a7d04f6", "scheduled_date": "2026-10-14", "name": "5 × 1km @ threshold", "sport": "running", "steps": [ { "type": "warmup", "duration": { "type": "time", "seconds": 600 } }, { "type": "repeat", "repeats": 5, "steps": [ { "type": "active", "duration": { "type": "distance", "meters": 1000 }, "target": { "type": "pace", "low": 3.5, "high": 3.8 } }, { "type": "rest", "duration": { "type": "time", "seconds": 90 } } ] }, { "type": "cooldown", "duration": { "type": "open" } } ] } ``` Send it as a signed `POST /v1/workouts`. It appears in Garmin Connect on the scheduled date and syncs to the watch. The rules that trip people up: - **Durations are seconds and metres**, not minutes and kilometres. - **Pace is minutes per kilometre**, and `low` is the faster end, so the smaller number. - **`open` runs until the athlete presses lap.** - **A `repeat` cannot contain another `repeat`.** Watches render one level, and we refuse the workout instead of silently flattening it. ## 3. Read the result The call is synchronous. It returns once Garmin has answered, usually in under a second, so there is no job to poll: ```json { "id": "0f31a8c4-59d2-4e07-b6a1-8c74e2f95d30", "name": "5 × 1km @ threshold", "scheduled_date": "2026-10-14", "pushes": [ { "provider": "garmin", "status": "synced", "external_id": "1284410973" } ] } ``` | `status` | Meaning | | --- | --- | | `synced` | It is on the watch. `external_id` is Garmin's id for it | | `pending` | Accepted, not on the device yet. Nothing for you to do | | `unsupported` | This provider cannot represent this workout. Retrying changes nothing | | `failed` | It did not land. `reason` says why | A failed push carries a stable `reason` to branch on. The one you will see most with Garmin: ```json { "provider": "garmin", "status": "failed", "reason": "not_permitted", "error": "the athlete has not granted Garmin's Workout Import permission — ask them to reconnect Garmin and leave Workout Import switched on" } ``` Show that to the user as "reconnect Garmin and allow workouts" and send them through `POST /v1/connect` again. Retrying before they do fails the same way. ## Targets A step can hold a target, and a second one alongside it: ```json { "type": "pace", "low": 3.5, "high": 3.8 } { "type": "heart_rate", "low": 150, "high": 165 } { "type": "heart_rate_zone", "zone": 3 } { "type": "power", "low": 240, "high": 260 } { "type": "power_percent", "low": 95, "high": 105 } { "type": "cadence", "low": 85, "high": 95 } ``` Zones are the athlete's own, as set on their device. `power_percent` is percent of FTP, and `heart_rate_percent` is percent of maximum heart rate. Steps can also end on a condition instead of a clock, which is how you write "recover until 130": ```json { "type": "recovery", "duration": { "type": "heart_rate_below", "bpm": 130 } } ``` ## Strength sessions Garmin is the only provider that takes strength workouts. Write them as exercises: ```json { "user_id": "4c9a7e15-6d3b-42f8-91c0-8e5b2a7d04f6", "scheduled_date": "2026-10-15", "name": "Lower body A", "sport": "strength", "steps": [ { "type": "exercise", "name": "Barbell back squat", "sets": 4, "reps": 8, "weight_kg": 80, "rest_seconds": 120 }, { "type": "exercise", "name": "Romanian deadlift", "sets": 3, "reps": 10, "weight_kg": 60, "rest_seconds": 90 } ] } ``` We match `name` against Garmin's exercise library, so the watch shows Garmin's own exercise and logs the sets under it. Write names the way a gym would. A name we cannot match still arrives, as a reps step labelled with your text. ## Editing and deleting `PATCH /v1/workouts/{id}` takes the whole workout and pushes it again. `DELETE /v1/workouts/{id}` removes it from the watch and then from us. When the athlete changes their plan in your app, their watch follows. ## More than Garmin The same call reaches COROS, Wahoo and Apple Watch. Leave `provider` out and the workout goes to every device the user has connected, with one entry in `pushes` for each: ```json "pushes": [ { "provider": "garmin", "status": "synced", "external_id": "1284410973" }, { "provider": "wahoo", "status": "unsupported", "error": "Wahoo structured plans cover running and cycling, not 'swimming'" } ] ``` A partial success is still a `201`. You only get an error when nothing could have worked. ## Routes too A workout says what to do. A route says where. `POST /v1/routes` takes a GPX, TCX or FIT file and puts it on the watch as a course the athlete can follow. See the [routes docs](https://platform.stridee.com/docs/routes). ## Next - [Workouts reference](https://platform.stridee.com/docs/workouts): swimming, multi-sport, limits and every step type - [Quickstart](https://platform.stridee.com/docs/quickstart) - [Garmin integration](https://platform.stridee.com/integrations/garmin) - [Pricing](https://platform.stridee.com/pricing): 14-day free trial on every plan