Honestly, this whole thing annoyed me enough that I spent my time (and a good pile of AI tokens) on solving it properly – because I don’t believe Garmin will row back this time. March was a technical slip; September looks like a business decision, and those don’t get reverted by forum threads.
So instead of the upload-and-delete dance (which, as @MedTechCD noted, always ends in duplicates because Intervals de-dupes by file hash), I wrote a small open-source bridge that keeps the official import on and adds the stripped data into the existing activity: it fetches the original FIT from Garmin Connect, compares it with what Intervals received, and writes only the missing fields and streams through the API – Stamina curve, Recovery Time, VO₂max, Performance Condition, Training Effect, Sweat Loss, grade-adjusted speed, whatever your custom fields define. No duplicates, nothing deleted, training load untouched. Backfill of the past is a single command, or let it run and it handles new activities within a minute of the import.
![]()
While I was at it: it also syncs the daily wellness data the official sync never delivers – night SpO₂, respiration, sleeping HR, Body Battery, HRV details, sleep stages, stress, readiness, logged nutrition and total burn, scores, race predictions, fitness age – with 19 charts for all of it in the chart library (“Garmin Bridge”).
Linux, container, or Windows with one command (pip install garmin-intervals-bridge).
More in the repo – what gets synced, the evidence from the live writes, and the caveats (Garmin’s endpoints are private; a streams write sets icu_intervals_edited): github.com/futureweb/garmin-intervals-bridge.
Screenshots and a longer write-up in the separate thread.
@david – a webhook on new activity upload would make this instant instead of a one-minute poll, and cheaper for your API. Win/win?

