# Garmin Coach to Edge 1030 Feasibility
Research date: 2026-06-16.
## Executive Summary
The most realistic path is an unofficial Garmin Connect API probe:
1. Read the user's Garmin Connect calendar/training-plan data.
2. Determine whether Garmin Coach cycling workout steps are exposed in the private training-plan/adaptive-plan JSON.
3. If step details are present, create a normal Garmin Connect cycling workout and schedule it.
4. Let the Edge 1030 sync that normal scheduled workout.
What is proven from source documentation/code:
- The Edge 1030 supports normal Garmin Connect structured workouts and scheduled workouts.
- `python-garminconnect` has methods for listing normal workouts, uploading a workout JSON, scheduling/unscheduling workouts, deleting workouts, listing calendar entries, and fetching training/adaptive plan details.
- The Garmin Connect workout JSON schema for normal workouts is partially represented by `python-garminconnect.workout`.
What is proven from this user's account probe:
- Garmin login/token reuse works with `python-garminconnect`.
- The account has an active `FBT_ADAPTIVE` Garmin Cycling Coach plan.
- The plan/calendar APIs expose dated Coach workout summaries, including name, date, sport type, duration, compact description, training effect label, and `workoutUuid`.
- The Garmin Connect browser uses `/gc-api/workout-service/fbt-adaptive/{workoutUuid}` to fetch the full Coach workout with `workoutSegments` and `workoutSteps`.
- The same detail is available to `python-garminconnect` through the Connect API path `/workout-service/fbt-adaptive/{workoutUuid}` without the browser `/gc-api` prefix.
What is still not proven:
- Whether the Edge 1030 accepts every cloned target type after sync.
- Whether the endpoint remains stable as Garmin changes private APIs.
- Whether a summary-derived approximation is good enough for the user's training use case.
The official Garmin Developer Training API is not a good fit for this use case. It is business/approval-gated, designed to publish third-party workouts/plans to users, and does not provide a documented way to read a personal Garmin Coach/adaptive plan workout from Garmin Connect.
## Feasibility Matrix
| Feature | Supported / Unsupported / Unknown | Tested / documented / guessed | API/package/source | Notes |
|---|---:|---:|---|---|
| Edge 1030 runs normal structured workouts from Garmin Connect | Supported | Officially documented; not tested here | Edge 1030 manual: Workouts, Starting a Workout | Manual says workouts can be created in Garmin Connect, transferred, scheduled, and run on device. |
| Edge 1030 shows scheduled workouts in calendar/training plan | Supported | Officially documented; not tested here | Edge 1030 manual: Training Calendar | Menu path documented as `Training > Training Plan > calendar icon`. |
| Official API can read Garmin Coach/adaptive workouts from personal account | Unsupported | Documented absence/inferred from API direction | Garmin Training API, Health API docs | Training API publishes workouts/plans to Garmin Connect; Health API reads health/activity metrics, not Coach workout definitions. |
| Official API can create/schedule workouts | Supported with approval | Officially documented | Garmin Training API | It can publish workouts/training plans to the Garmin Connect calendar. |
| Official API available for personal app | Unsupported for this use case | Officially documented | Garmin Developer Program FAQ | Program is for enterprise/business use and requires approval. |
| Authenticate unofficially with Garmin Connect | Supported/fragile | Tested with user's account | `python-garminconnect` login/tokenstore source | Login worked and wrote `.garmin-tokens`. Future Garmin SSO changes remain a risk. |
| `garth` as standalone auth library | Unsupported/fragile | Project README | `matin/garth` README | `garth` says it is deprecated and new logins may not work after Garmin auth changes. Prefer current `python-garminconnect` probe. |
| Store/reuse Garmin tokens | Supported in source | Source-supported; untested here | `python-garminconnect` login and demo | Token directory can be loaded/dumped. Probe defaults to `.garmin-tokens`. |
| List training plans | Supported | Tested with user's account | `Garmin.get_training_plans()` | Returned one `FBT_ADAPTIVE` plan: `2026 Fitness Plan test`. |
| Detect active Garmin Coach/adaptive plan | Supported/partial | Tested with user's account | `trainingPlanCategory == FBT_ADAPTIVE` | The active cycling Coach plan is detectable. |
| Fetch adaptive training-plan detail | Supported summary only | Tested with user's account | `get_adaptive_training_plan_by_id()` | Returns `taskList` with dated `taskWorkout` summaries, but no step arrays. |
| Fetch Coach workout step details for date | Supported/fragile private API | Tested with user's account | `/workout-service/fbt-adaptive/{workoutUuid}` | Returns full `workoutSegments`/`workoutSteps` via normal `python-garminconnect` token auth. Browser shows equivalent `/gc-api/workout-service/fbt-adaptive/{workoutUuid}`. |
| Read compact Coach workout summary | Supported | Tested with user's account | Adaptive plan `taskList[].taskWorkout` | Exposes fields such as `workoutName`, `estimatedDurationInSecs`, `workoutDescription`, `trainingEffectLabel`, `workoutPhrase`, `workoutUuid`. |
| List Garmin calendar entries | Supported | Tested with user's account | `get_scheduled_workouts(year, month)` | Calendar contains `fbtAdaptiveWorkout` entries with `workoutUuid` and `trainingPlanId`. |
| List normal workouts | Supported | Tested with user's account | `get_workouts()` | Returned normal cycling workouts. |
| Fetch normal workout by ID | Supported in source | Source-supported; untested here | `get_workout_by_id()` | Useful for schema inspection and clone comparisons. |
| Create/upload normal cycling workout | Supported in source | Source-supported; untested here | `upload_workout()`, `upload_cycling_workout()` | Probe includes a minimal dummy cycling workout upload test. |
| Schedule normal workout on date | Supported in source | Source-supported; untested here | `schedule_workout(workout_id, YYYY-MM-DD)` | This is the preferred path if upload works. |
| Delete generated workout | Supported in source | Implemented with guard; not account-tested | `delete_workout()` | Probe refuses to delete unless the ID is in the generated-prefix workout list and `--confirm` is passed. |
| Unschedule generated workout | Supported in source | Implemented with guard; not account-tested | `unschedule_workout()` | Probe refuses to unschedule unless the scheduled ID is in the generated-prefix calendar list and `--confirm` is passed. |
| Avoid duplicate clones | Partially supported | Implemented by name prefix/date | Probe code | Uses `GCClone YYYY-MM-DD` prefix. Full app should persist mappings in SQLite. |
| Generate local FIT workout file | Possible | Official SDK documented; not implemented in probe | Garmin FIT Python SDK | SDK has an encoder. Need workout-file-specific implementation and device testing. |
| Copy `.fit` to `GARMIN/NewFiles` | Unknown | Common Garmin behavior; not verified here | Not accepted as primary source | Treat only as manual fallback until tested on Edge 1030. |
| Use `GARMIN/Workouts/Schedule` for date scheduling | Unknown | Not proven | No reliable official source found | Garmin Connect calendar scheduling is safer. |
| Coach clone marks Garmin Coach workout complete | Unknown/likely unsupported | Guessed | Separate cloned workout | A clone is a normal workout, not the original Coach workout. Completion linkage may not happen. |
## Official Garmin API Findings
Garmin's official Training API is aimed at approved integrations that publish workouts and training plans into Garmin Connect. The Training API page says it can publish workouts/training plans to the Garmin Connect calendar, then users sync compatible devices.
This is directionally opposite of the required first step: reading a personal Garmin Coach/adaptive workout from Garmin Connect. I found no official endpoint for reading Garmin Coach plan workout definitions from a user's account.
The Developer Program FAQ states the Garmin Connect Developer Program is for enterprise/business use and applications are reviewed. That makes it unrealistic for a local personal tool unless the user already controls an approved business integration.
## Unofficial Garmin Connect Findings
The current `python-garminconnect` source exposes the important private endpoints:
- Workouts: `/workout-service`
- Calendar: `/calendar-service`
- Training plans: `/trainingplan-service/trainingplan`
Relevant methods in source:
- `get_workouts(start=0, limit=100)`
- `get_workout_by_id(workout_id)`
- `upload_workout(workout_json)`
- `upload_cycling_workout(workout)`
- `schedule_workout(workout_id, date_str)`
- `get_scheduled_workouts(year, month)`
- `get_scheduled_workout_by_id(scheduled_workout_id)`
- `delete_workout(workout_id)`
- `unschedule_workout(scheduled_workout_id)`
- `get_training_plans()`
- `get_training_plan_by_id(plan_id)`
- `get_adaptive_training_plan_by_id(plan_id)`
The package demo routes plans with `trainingPlanCategory == "FBT_ADAPTIVE"` to the adaptive endpoint. That is the strongest lead for Garmin Coach/adaptive plan extraction, but it does not prove that cycling Coach step details are present for this user's account.
Authentication is a risk. The current `python-garminconnect` package has token storage and MFA support, while the older `garth` project now says new logins may not work because Garmin changed auth. The probe therefore treats login itself as a testable capability.
The probe also includes a read-only metadata call to `/workout-service/workout/types` and a normal-workout-by-ID dump. Those are intended to compare Garmin's current accepted workout schema against any Coach/adaptive JSON found during account testing.
For account testing, `probe_garmin.py report --dump-json` runs the read-only discovery checks in one pass and writes `debug/probe_report.json`. That report is the most compact evidence bundle for deciding whether the proof-of-concept can continue to clone testing.
### Account Probe Evidence
The user's June 16, 2026 probe found an active Garmin Cycling Coach adaptive plan:
- Plan ID: `45692639`
- Category: `FBT_ADAPTIVE`
- Name: `2026 Fitness Plan test`
- Today's Coach workout: `Sprint`
- Workout UUID: `fe26ad37-69e8-495b-9d3d-2ab10fb64334`
- Estimated duration: 3120 seconds
- Compact description: `2x13x0:10@All Out`
The adaptive plan detail endpoint returned `taskList[].taskWorkout` summaries, not `workoutSegments` or `workoutSteps`. The calendar endpoint returned `itemType=fbtAdaptiveWorkout` entries with matching `trainingPlanId` and `workoutUuid`.
The missing endpoint was found in the browser network analyzer:
```text
/gc-api/workout-service/fbt-adaptive/{workoutUuid}
```
For the June 16 `Sprint` workout, that response contains `workoutSegments` and nested repeat groups. Testing showed that the equivalent Connect API route also works:
```text
/workout-service/fbt-adaptive/{workoutUuid}
```
The CLI now uses that Connect API route when a `workoutUuid` is available. The browser `/gc-api` route returned `403` with the package token session, so it remains a browser-specific diagnostic path.
This means exact Coach-to-normal-workout cloning is feasible with the unofficial API.
## Workout Schema Findings
`python-garminconnect.workout` encodes the normal Garmin Connect workout schema enough for a dummy cycling workout:
- Sport type: cycling has `sportTypeId=2`, `sportTypeKey="cycling"`.
- Step types include warmup, cooldown, interval, recovery, rest, repeat, other, and main.
- End conditions include lap button, time, distance, calories, power, heart rate, iterations, fixed rest, fixed repetition, and reps.
- Target types include no target, power zone, cadence, heart-rate zone, speed zone, pace zone, grade, heart-rate lap, power lap, and resistance.
The typed helpers only create simple time-based no-target steps. More exact Coach cloning will require preserving or constructing `targetType`, target values, and repeat groups from the raw Coach/adaptive JSON after the probe shows what Garmin returns.
Edge 1030 acceptance for target details is untested. The dummy workout intentionally starts with simple time-based no-target steps to separate "can upload/schedule/sync" from "can preserve all Coach targets".
## Direct Device / FIT Fallback
A local FIT export fallback is plausible but lower priority:
- Garmin's official FIT Python SDK can encode FIT files.
- FIT workout files can represent structured workout concepts through FIT profile messages, but the probe does not implement this yet.
- `fitparse`/`fitdecode` are primarily decoders and are not enough by themselves for reliable workout generation.
- Copying files to Garmin device folders is not a good primary automation path because scheduling behavior on `GARMIN/Workouts/Schedule` is not reliably documented for the Edge 1030.
The safer first proof is still Garmin Connect upload + calendar scheduling. A FIT export can become a manual fallback if Garmin Connect upload/schedule works but Coach extraction or automated scheduling remains blocked.
## Stop / Continue Decision
Do not build the web app yet.
Continue to Phase 2 probe because:
- Normal workout upload/scheduling is source-supported and can be tested with a dummy workout.
- Adaptive/Coach endpoint access is source-supported enough to probe.
Stop before Phase 3 unless the user confirms at least:
- The dummy scheduled workout appears on the Edge 1030.
- Garmin Coach/adaptive JSON contains enough step data to clone through the FBT adaptive endpoint, or the agreed fallback is summary-derived/manual template generation.
If the FBT adaptive endpoint changes or becomes unavailable, the best fallback architecture is:
- Keep Garmin Connect login/calendar/workout discovery.
- Fall back to parsing adaptive plan `taskWorkout` summaries and producing clearly labeled approximate normal workouts.
- Allow manual review/edit of generated steps before upload.
- Optionally export FIT workouts for manual device copy.
- Do not claim unattended exact Garmin Coach cloning unless browser endpoint authentication is stable.
## Sources
- Garmin Training API:
- Garmin Developer Program FAQ:
- Garmin Developer Program Overview:
- Garmin Health API:
- Edge 1030 Workouts manual:
- Edge 1030 Following a Workout From Garmin Connect:
- Edge 1030 Starting a Workout:
- Edge 1030 Training Calendar:
- `python-garminconnect`:
- `python-garminconnect` workout models:
- `garth` deprecation notice:
- Garmin FIT Python SDK: