Real-Time Wearable Sync Architecture

Javier Mora
8 min

You finish your long run, stop your watch, and when you open MyAlbatross the workout is already there. We pop the hood to explain, in plain language, what happens in those seconds and how we beat the biggest enemy of any sports platform: the duplicated activity.

Sports watch syncing a workout with the MyAlbatross platform

You finish your long run. You stop your watch, stretch a little, and by the time you open MyAlbatross the workout is already there: your kilometres, your load, and your coach looking at it on their dashboard. It feels like magic, but behind it there is a small assembly line that has to work properly thousands of times a day.

In this post we pop the hood. We will explain, no engineering degree required, exactly what happens in those seconds: who notifies whom, where your workout waits its turn, what happens when something fails and, above all, how we beat the biggest enemy of any sports platform: the duplicated activity.

The Full Journey

The route has six stops, and none of them depends on the previous one being fast: each does its part and passes the baton to the next.

Diagram of the six steps from the watch to the coach

If something fails during processing, the workout goes back to the queue for another attempt; only after three failures is it set aside for review.

Webhooks

We don’t ask Garmin every five minutes “anything new?”. That would be like checking your mailbox every few minutes in case a letter has arrived. Instead, Garmin, Strava, Polar and Suunto call our API when there is news. We call that incoming notification a webhook: a web address of ours to which they send an automatic notice.

Each brand calls in a different way:

  • Garmin doesn’t send us the workout, it sends a note: “this athlete has a new file, you can pick it up here”. Then we are the ones who go and fetch the FIT file (the raw format the watch records, with every second of heart rate, pace and GPS).
  • Strava tells us an activity has been created or deleted, and we then ask it for the details.
  • Polar and Suunto sign every notification with a shared secret key (what is called an HMAC signature). That way we know the message comes from them and not from an impostor.

The golden rule when receiving a notification is to reply fast. If we take too long, the brand thinks we are down and calls again, or simply stops notifying us. So we process nothing at that moment: we say “received” (a 200 OK, in technical jargon) and leave the heavy lifting for later.

The Queue: Your Workout’s Waiting Room

Between “received” and “processed” there is a waiting room: a message queue (we use Amazon SQS). Each notification enters the queue and an independent worker (a Lambda function) pulls them out 10 at a time and hands them to our backend to be processed.

Why not process it directly? For three reasons:

  1. Peaks. On a Sunday at 11:00, hundreds of runners finish their long run at the same time. The queue absorbs the blow and workouts wait their turn for a few seconds instead of bringing the server down.
  2. Memory. The queue keeps each message for up to 7 days. If we restart a server or deploy a new version, the workout isn’t lost: it keeps waiting for us.
  3. Order. We use a FIFO queue (“first in, first out”) grouped by athlete. Your workouts are processed in order and one at a time, while other athletes’ workouts move forward in parallel. This prevents two of your workouts from stepping on each other when your load is recalculated.

When its turn comes, the workout goes through the assembly line: we download the file, decode it, store the original in S3 (our file storage), and recalculate everything that depends on it: training load, acute:chronic ratio, injury risk index (IEG) and your fitness status. If you had a planned workout that day, we link it to what you actually did.

Finally, we notify: the athlete receives a push notification on their phone and the coach sees the alert on their dashboard instantly, thanks to an open connection to the server.

Idempotency: Why Your Run Doesn’t Show Up Twice

Here comes the ugly word of the post: idempotency. It means something very simple: doing the same thing twice has the same effect as doing it once. Pressing the elevator button five times doesn’t bring five elevators.

We need it because in the world of webhooks, repeated notifications are not a bug, they are the norm. Brands would rather notify you twice than not at all. A slow network, an automatic retry, a history resync… and the same workout can knock on the door several times.

That’s why we have several gatekeepers, one behind the other:

  1. The queue itself. SQS automatically discards an identical message arriving within a 5-minute window.
  2. The notification registry. Every workout from every brand has a unique identifier (the Garmin activity ID, the Strava one…). Before processing anything we write down “athlete X, activity Y” in a table that the database enforces as unique. If the same pair arrives again, the database rejects it and we ignore it. Having the rule live in the database, and not just in the code, is key: two servers at once can’t sneak past each other.
  3. One brand per athlete. What if your Garmin also uploads the workout to Strava? It would be the same run with two different identifiers, impossible to detect by ID. Our product decision: each athlete connects a single data source. Less magic, zero duplicates across brands.

Diagram of the three anti-duplicate gatekeepers

Each gatekeeper stops a different kind of copy; only what passes all three reaches your calendar.

Retries: When Something Goes Wrong (and It Always Does)

We work with third-party servers, and third-party servers have bad days. Garmin can be slow, Strava can tell us “you’re going too fast, wait” and sometimes a file arrives corrupted. The question isn’t whether it will fail, but what we do when it does.

  • Automatic retry. If processing a workout fails unexpectedly, the message isn’t deleted: it goes back to the queue and is retried, up to 3 times.
  • The “odd cases” queue. If it still fails after 3 attempts, the message moves to a separate queue (a dead letter queue, or DLQ) where it is kept for 14 days. It isn’t lost, it doesn’t block the rest, and a person on the team can review it.
  • Expiring keys. To download your data we use a temporary permission (an OAuth token) that expires. We renew it before it expires, with a 5-minute margin, so the download doesn’t fail halfway.
  • The history. When you connect your watch we ask Garmin for your last 60 days of workouts. They arrive through the same webhook as new ones, so they pass through the same gatekeepers: any we already had are ignored.

Here idempotency and retries go hand in hand: since processing twice duplicates nothing, we can afford to retry without fear.

War Story: The Overzealous Gatekeeper

This summer an athlete wrote to us: “I did a workout with my Suunto and it doesn’t show up”. We checked the logs and the notification had arrived. The queue had delivered it. And yet, the workout wasn’t there.

What had happened was almost ironic. To avoid duplicates, our system marked “this workout is in progress” before downloading it. That download failed because of a momentary problem on the provider’s side. When the retry arrived, the gatekeeper looked at the list, saw the workout marked and said: “I already have this one, out.” The anti-duplicate system was blocking the very retry that was supposed to fix things.

In engineering we call this a poisoned sentinel: a “already processed” marker that gets written even though the process didn’t finish properly.

The fix (deployed on July 30, 2026) was to change the order of things:

  1. We mark the workout as “in progress”.
  2. If everything goes well, we mark it as completed.
  3. If something fails, we delete the marker, so the next retry can get in.

The lesson: idempotency isn’t about “never letting anything through twice”. It is about “never letting through twice what has already succeeded”. They sound the same, but they aren’t.

What We Learned

  • Reply fast, work later. The webhook only says “received”; the queue does the rest.
  • Duplicates are stopped in the database, not just in the code. Code can be wrong; a unique constraint can’t.
  • Retrying is only safe if processing twice does no harm. Idempotency and retries are two sides of the same coin.
  • A “done” marker is only written when it is done. Otherwise your anti-duplicate system ends up eating workouts.
  • Simplifying the product is engineering too. One data source per athlete saves us the hardest problem of all: knowing whether two different runs are actually the same one.

Next time you stop your watch and see your workout in MyAlbatross, now you know: it called the webhook, waited its turn, passed three checkpoints and recalculated your fitness status. All so your coach has the real picture, and not one run too many.

15-DAY FREE TRIAL

Want to see how it works with your own athletes?

Plan workouts with artificial intelligence, sync data from Garmin, Suunto, and Strava, monitor your athletes' injury risk, and receive alerts before problems appear.

Start your free trial No credit card required
Avatar Javier Mora
Javier Mora CTO & Co-founder MyAlbatross

Software engineer and running enthusiast.

Stay up to date on training and injury prevention

Tips, scientific news and practical content for running coaches. No spam, unsubscribe anytime.