Wearable Data Deduplication: How to Handle Users With Multiple Devices
Key Takeaways
- Users with multiple wearable devices or apps produce overlapping data. The same workout can arrive from Garmin, Strava, and Apple Health as three separate records.
- Open Wearables stores records from each provider separately, identified by provider and source metadata. Deduplication within a provider happens via unique constraints at the database level.
- Cross-provider deduplication is handled through a configurable priority system: provider priorities and device type priorities determine which source is used when multiple providers cover the same data.
- A user who shares one provider account across multiple Open Wearables profiles is handled by a sync coordination layer that prevents duplicate API calls.
- Your application queries normalized data through the API. The priority system determines what you receive. You configure the priorities; Open Wearables applies them.
Multi-device users are common. An athlete with a Garmin watch connected to Strava will produce workout records from both. A user with an iPhone running Apple Health and a Polar watch will produce overlapping sleep and step data. A corporate wellness user might have their employer's wellness app pulling from Google Health Connect while also using a Whoop.
Each of these configurations creates the same problem: the same real-world event appears multiple times in your data from different sources. How you handle this determines whether your health AI produces accurate insights or noisy ones.
How Duplicates Happen
There are two distinct deduplication problems.
Within-provider deduplication happens when the same record arrives multiple times from the same provider. This can happen due to webhook retries, sync overlap, or race conditions in background processing. Open Wearables handles this automatically at the database layer using unique constraints. The handle_duplicates decorator in the codebase catches UniqueViolation database errors, queries for the existing record using the violated unique constraint, and returns it instead of failing. The insert is idempotent.
Cross-provider deduplication happens when different providers report the same real-world event. A run recorded on a Garmin watch that syncs to Strava produces two records with slightly different timestamps, different field values, and different source metadata. These are not database duplicates: they are two valid records from two valid sources. Deciding which one to use is an application-level decision handled by the priority system.
The Priority System
Open Wearables includes a configurable priority system that determines which source takes precedence when multiple providers cover the same data.
There are two levels of priority:
- Provider priority: a ranked order of providers. If a user has both Garmin and Strava connected, and both report the same workout, the provider priority determines which one appears in your query results.
- Device type priority: a ranked order of device categories (watch, phone, ring, etc.). If a user's Garmin watch and their iPhone both contribute step data through Apple Health, the device type priority determines which one is used for daily activity aggregates.
The priority system exposes API endpoints for reading and updating these priorities:
GET /api/v1/priorities/providers
PUT /api/v1/priorities/providers/{provider}
PUT /api/v1/priorities/providers
GET /api/v1/priorities/device-types
PUT /api/v1/priorities/device-types/{device_type}
PUT /api/v1/priorities/device-types
Each record in the Open Wearables data model includes a source field with provider and device metadata. Your application always knows where each data point came from, regardless of priority configuration.
Shared Provider Accounts
A less common but real scenario: multiple Open Wearables user profiles linked to the same external provider account. This happens in team or testing setups where multiple developers share one Garmin or Polar account.
If each profile triggered its own sync independently, the same provider API call would happen multiple times in parallel, producing duplicate data and unnecessary API load.
Open Wearables handles this through a sync coordination layer backed by Redis. When a sync starts, the first profile to initiate claims the primary role using a Redis lock (SET NX). Other profiles with the same external provider account become secondaries. The primary makes the API call and the data is fanned out to secondary profiles. Each secondary receives a sync status event indicating it received data from the primary rather than making its own call. This coordination is scoped per provider, per provider user ID, and per sync type, so concurrent syncs of different types do not interfere.
What Your Application Sees
When you query the Open Wearables API, you receive data that has already passed through the priority system. For most endpoints, you get one record per event per day, from the highest-priority source that has data for that event.
The source field on every record tells you which provider and device contributed it. If you need to inspect or override the priority logic, the raw records from all providers are accessible through lower-level API endpoints.
Data from different providers is never merged into a single record. Open Wearables does not combine a Garmin heart rate with a Strava GPS trace into one unified workout. Each provider's data is stored as a separate record. The priority system selects which record to surface in response to a given query.
Related articles
- How to Handle OAuth, Webhooks, and Token Refresh Across Multiple Wearable Providers
- How to normalize wearable data across providers
- How to Structure Health Data for AI: What Your LLM Actually Needs
- How to sync wearable data from multiple devices
FAQ
Does Open Wearables merge records from different providers into one?
No. Records from different providers are always stored separately. Each record carries a source field identifying its provider and device. The priority system determines which record is returned for a given query, but the underlying records remain distinct.
What happens when Garmin and Strava both report the same run?
Both records are stored. The provider priority configuration determines which one appears in your workout query results. If Garmin has higher priority than Strava, the Garmin record is returned. The Strava record remains in the database and is accessible if you query for it directly.
Can I configure which provider takes precedence for each user individually?
The priority configuration is per-deployment, not per-user. All users in your Open Wearables instance use the same provider and device type priority order. If you need per-user priority logic, you implement that in your application layer using the source metadata on each record.
What if a user connects the same provider account twice?
The OAuth connection flow associates an external provider user ID with an Open Wearables user. If the same external account is connected again, the existing connection is updated rather than creating a duplicate. Multiple Open Wearables users sharing the same external provider account are handled by the sync coordination layer, which prevents duplicate API calls.
How does deduplication work within a single provider?
Within a single provider, records are deduplicated using unique database constraints. If the same event arrives twice (due to webhook retries, sync overlap, or a race condition), the second insert detects the constraint violation, queries for the existing record, and returns it. The operation is idempotent from the caller's perspective.