My Garmin watch is diligent about strength training in exactly one way: it knows when I lifted. It records my heart rate for the whole session and it can even feel each set start and stop — a burst of work, a rest, another burst. What it cannot tell you is what any of those bursts were. Open a strength activity in Garmin Connect and you get a tidy list of sets, every one of them labelled "Unknown", with no reps and no weight.
Meanwhile the app I actually log my lifts in — Strong — knows precisely what I did, set by set, and has no way to put that on my Garmin. It exports to Apple Health, but Apple Health has no data model for "barbell bench press, 8 reps, 75 kg", so all that survives the trip is duration and a calorie estimate. The set data evaporates.
So I have two systems each holding half of the same workout, and a stubborn refusal between them. This is the kind of gap that nags at me until I close it.
The obvious approaches are all slightly wrong
The first instinct is to build a file and upload it. Garmin accepts FIT files, and the FIT format genuinely supports strength sets — exercise category, reps, weight, the lot. So: take Strong's export, write a FIT, upload it.
Except an upload creates a new activity. Now I have two: the one my watch recorded, with the heart rate, and a second one with the sets and no heart rate, sitting next to it with the same start time. Garmin often rejects the second as a duplicate; when it doesn't, you've got a mess. The thing I want to enrich is the activity that already exists.
The next idea is to merge: download the original FIT, splice the Strong sets into it, re-upload, delete the original. I actually built this. It works. But it's fragile in the way that "delete the real thing and replace it with one you generated" is always fragile — and Strong doesn't record a wall-clock time per set, so aligning its sets onto the watch's timeline is guesswork.
I was most of the way down that road when I went looking at what Garmin Connect's own web interface does. Because you can edit sets there, by hand, in the browser. Which means there's an endpoint.
Edit it in place
There is: setActivityExerciseSets. You GET an activity's sets, change the array, PUT it back. It's the API behind the "edit sets" button, and it does exactly what I want — it edits the activity that already exists. The heart rate, the GPS, the timing, the activity's whole identity: untouched. I'm only rewriting the one thing the watch left blank.
This reframes the entire problem. I'm not building files or merging timelines or deleting anything. I'm filling in blanks. The watch already detected the set structure — the bursts and rests, with real timestamps — and I just drop Strong's exercise, reps and weight into the slots it found. When the watch didn't break the session into sets (sometimes it just logs one long block), I synthesise the sets from Strong instead. Either way: one activity, enriched, reversible.
A few things had to be reverse-engineered to make it real, and each had a small trap:
- Strong's API is private. There's no public API, but the app talks to a backend, and prior work had mapped the shape of it. Logging in and paging workouts was straightforward once I had the contract. The fiddly part was that Strong returns logs oldest-first with no sort option, so getting recent workouts means paging forward to the end.
- The set fields aren't named what you'd guess. Weights come through as
BARBELL_WEIGHT,DUMBBELL_WEIGHT,WEIGHTED_BODYWEIGHT,ASSISTED_BODYWEIGHT— each meaning something slightly different — plusREPSandDURATIONfor the timed holds. - Garmin stores set weight in grams. I found this out the honest way: I wrote
60000to a test set and it showed up as 60 kg. (It's the FIT spec underneath, where weight is an integer; grams keeps it whole.) - The exercise names are a fixed enum. Garmin validates the exercise
categoryandnameagainst its own list and rejects anything it doesn't recognise. So Strong's free-text exercise names get mapped to the nearest FIT category — bench press, squat, deadlift, row, curl — and where there's no exact match, it falls back to the muscle-group category, which still renders the reps and weight. I validated the whole mapping against the live API before trusting it.
The bit I'm happiest with
The actual day-to-day interface is a little terminal UI. Two panes — my Strong workouts on the left, my Garmin strength activities on the right. Move the cursor to a workout and it auto-highlights the closest activity by start time and shows the gap (usually a minute or two). Hit m and it enriches it. Anything already done is green with a tick, so I can see at a glance what's synced and what isn't. Every write backs up the original first, so u puts it back.
It's a deeply unglamorous tool that does one thing, and now my lifts show up on my Garmin the way runs always have — heart rate and the actual sets, in one place, with no duplicate activity polluting the history.
The code is on GitHub: strong-garmin-sync. Usual disclaimers apply — it leans on a private Strong API and an unofficial Garmin Connect client, it's for getting at your own data, and it could break the moment either side changes anything. It's not affiliated with Strong or Garmin. But it scratches the itch, and the itch is gone.