Skip to content

Releases: the-momentum/open-wearables

0.7.0 - Google Health API & SensorBio

Choose a tag to compare

@KaliszS KaliszS released this 12 Aug 13:06
c79410a

Highlights

We encourage you to use our official Docker images:

Google Health API & SensorBio

In our latest release, we’re introducing two new integrations with providers. Both integrations will be further developed, but it’s important to note two things:

  • The Google Health API replaces the Fitbit API, which is soon to be deprecated by Google
  • SenorBio does not currently support webhooks
image

Admin UI Dashboard improvements

On less efficient infrastructure, the front-end dashboard could take up to several seconds to load data. We optimised this by making improvements to React and removing unnecessary information. In return, we added more details about the volume of stored data and its specific types (see screenshot below). We also improved the display of large numbers (previously, even millions were shown in thousands). The dashboard should now load instantly, even with very large volumes of data and limited RAM.

image

More information on data sources for the frontend

On the frontend, each piece of data (workouts, sleep sessions, menstrual cycles, activity) will display more information. Until now, we’ve only shown the provider from which the data was saved. Now, the original provider will also be displayed (if applicable – for example, Apple allows data to be synchronised from devices belonging to other providers) along with the device from which the data was retrieved (currently, in the example described earlier, for Apple this will simply be an iPhone, but we also intend to start recording information about the original device soon, provided the providers allow it).

You’ll probably often notice gaps in this information; we intend to rectify them in the next release, as this requires changes to the way data is fetched from the sources.

image image image image

The option to delete data associated with a selected provider

From now on, the user profile includes an option to delete data from a selected provider. This also automatically disconnects the connection.

image image

SDK applications management in Settings

From now on, you can manage SDK applications via the frontend settings (Admin UI).

image

Logging improvements

The first change is an improvement to the SDK’s data-saving process. Previously, a single incorrect row would result in the loss of the entire batch. Now, instead, we display the sync status for the problematic rows and log their exact errors to Sentry.

image image

In addition, we have added a lot of new information to the logs, which may be useful for debugging.
HTTP logs from app:

  • response_body (an error details)
  • request_bytes (how many bytes we are expecting from the payload)
  • error_cause_type (can be helpful to decide whether an error parsing the body response_body is caused by connection or decode error)
  • error_cause_msg
  • content_type (should be application/json)
  • content_encoding (for example gzip)
{"timestamp": "2026-07-30T17:41:05.671050+00:00", "level": "error", "message": "http_request", "provider": null, "method": "POST", "path": "/api/v1/sdk/users/00000000-0000-0000-0000-0000000000bb/sync", "status": 400, "duration_ms": 17.7, "request_bytes": 3, "response_body": "There was an error parsing the body", "content_type": "application/json", "error_cause_type": "UnicodeDecodeError", "error_cause_msg": "'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte"}
{"timestamp": "2026-07-30T17:41:05.681616+00:00", "level": "error", "message": "http_request", "provider": null, "method": "POST", "path": "/api/v1/sdk/users/00000000-0000-0000-0000-0000000000bb/sync", "status": 400, "duration_ms": 0.6, "request_bytes": 62, "response_body": "There was an error parsing the body", "content_type": "application/json", "content_encoding": "gzip", "error_cause_type": "UnicodeDecodeError", "error_cause_msg": "'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte"}
{"timestamp": "2026-07-30T17:41:05.732281+00:00", "level": "error", "message": "http_request", "provider": null, "method": "POST", "path": "/api/v1/sdk/users/00000000-0000-0000-0000-0000000000bb/sync", "status": 400, "duration_ms": 0.9, "request_bytes": 1000000, "response_body": "There was an error parsing the body", "content_type": "application/json", "error_cause_type": "ClientDisconnect", "error_cause_msg": ""}

Logs from celery worker:

  • first_error_loc (position of the first broken row in S3 payload)
  • data_type (SDK related, example HKQuantityTypeIdentifierWalkingStepLength)
  • success (SDK related, example true)
  • record_count (SDK related, int)
  • task_type (SDK related, example foreground)
  • low_power (SDK related, example false)
  • thermal_state (SDK related, example nominal)
  • types (sync status related, example ["basal_energy", "energy", "heart_rate", "steps"])
{"timestamp": "2026-07-30T15:14:21.143378+00:00", "level": "info", "message": "Apple data import completed", "provider": "apple", "action": "apple_sdk_import_complete", "batch_id": "89d47924-a674-48bd-9001-32c3ec3065e2", "user_id": "3c213ef4-38f3-4c0c-89d3-c782a741125d", "incoming_records": 1750, "incoming_workouts": 125, "incoming_sleep": 125, "records_saved": 2023, "workouts_saved": 124, "sleep_saved": 124, "validation_ms": 56.5}
{"timestamp": "2026-07-30T15:14:21.143502+00:00", "level": "warning", "message": "Apple SDK dropped invalid records", "provider": "apple", "action": "apple_sdk_records_dropped", "batch_id": "89d47924-a674-48bd-9001-32c3ec3065e2", "user_id": "3c213ef4-38f3-4c0c-89d3-c782a741125d", "dropped_count": 3, "first_error_loc": "records[0].value", "first_error_msg": "Input should be a valid decimal"}
{"timestamp": "2026-08-11T11:20:50.019232+00:00", "level": "info", "message": "SDK log events received",
 "provider": "apple", "action": "sdk_logs_received", "batch_id": "823f86a1-b76a-4f48-....",
 "user_id": "d771d1c3-3098-....", "event_count": 2,
 "event_types": ["historical_data_type_sync_end", "device_state"], "sdk_version": "0.13.0",
 "data_type": "HKQuantityTypeIdentifierWalkingStepLength", "success": true, "record_count": 966,
 "task_type": "foreground", "low_power": false, "thermal_state": "nominal"}
{"timestamp": "2026-08-11T11:21:33.975802+00:00", "level": "info", "message": "Sync status",
 "provider": "apple", "action": "sync_status", "status": "success", "source": "sdk",
 "stage": "completed", "run_id": "a899503a-e7bb-....", "user_id": "d771d1c3-...",
 "items_processed": 1439, "types": ["basal_energy", "energy", "heart_rate", "steps"],
 "detail": "Apple batch saved"}

What's Changed

Google Health API

SensorBio

Oura

  • chore(backend): Use Oura 30-sec sleep stage granularity over 5-min by @czajkub in #1327

Data model

Frontend improvements

  • fix(frontend): emit root stylesheet from client build by @aritsyg in #1315
  • fix: include icon_url on user connections by @Gasiek in #1347
  • feat(backend): add missing Apple and Samsung provider icons by @bartmichalak in #1369
  • fix(frontend): load theme colors under Tailwind v4 by @MichalZak17 in #1376
  • refactor: optimise dashboard by @KaliszS in #1388
  • refactor(frontend): optimise react by @KaliszS in #1389
  • refactor: data points count K->M->B->T by @KaliszS in #1390
  • feat(frontend): event records in dashboard by @KaliszS in #1391
  • refactor(frontend): replace hsl(var(--x)) workarounds with theme utilities by @MichalZak17 in #1397
  • refactor(frontend): migrate Tailwind theme to CSS-first @theme b...
Read more

0.6.3 - Nap detection & HTTP logs middleware

Choose a tag to compare

@KaliszS KaliszS released this 15 Jul 15:16
3f0a7f8

Highlights

We encourage you to use our official Docker images:

Changes to HTTP logs

As HTTP logs usually account for more than 80% of all logs, we have disabled them by default for production deployments and added a new variable to control their flow (ACCESS_LOG_LEVEL). How it works:

  • ACCESS_LOG_LEVEL=all -> All HTTP logs are available, as before
  • ACCESS_LOG_LEVEL=errors -> only 4xx & 5xx log entries are available
  • ACCESS_LOG_LEVEL=off -> no HTTP logs
  • no ACCESS_LOG_LEVEL (None value):
    • if ENVIRONMENT=production -> errors level is set automatically
    • else all level is set automatically

We have also changed the structure of the HTTP logs:

  • before:
[2026-07-15 11:41:55,669 - uvicorn.access] (INFO) HOSTNAME:PORT - "GET /api/v1/users?page=1&limit=9&sort_by=created_at&sort_order=desc HTTP/1.1" 200
  • after:
{"timestamp": "2026-07-15T13:31:03.742767+00:00", "level": "info", "message": "http_request", "provider": null, "method": "OPTIONS", "path": "/api/v1/users?limit=5&sort_by=created_at&sort_order=desc", "status": 200, "duration_ms": 3.5}

Warning

By default, ACCESS_LOG_LEVEL is set to None, so if you have the ENVIRONMENT variable set to production, you will stop logging most messages. If you wish to retain the current behaviour, you must add the ACCESS_LOG_LEVEL variable and set it to all.

Note

For the time being, this change only affects HTTP logs from uvicorn. The new variable does not alter the behaviour of logs from Open Wearables.

Nap detection for Garmin & Oura

We have added support for naps based on data provided by Garmin and Oura. For the time being, this feature works correctly only for these two providers, but we will endeavour to develop our own algorithms to detect naps for providers that do not themselves flag which sleep sessions are naps.

image

Flag to enable/disable outgoing webhooks

We have added a new variable, OUTGOING_WEBHOOKS_ENABLED, which allows you to enable or disable outgoing webhooks. By default, it is set to false, so you need to add it and set it to true for the webhooks to start working.

Note

Please note that this has nothing to do with incoming webhooks from providers. It only concerns events that Open Wearables sends to client backends.

image

Strava workout samples

Once the INGEST_WORKOUT_SAMPLES variable is set to true (it was added in previous versions whilst working on Garmin workout samples and is set to false by default), Strava will start logging workout data at a high frequency. You can find out more about this mechanism in the entry “Ingest workout samples” for Garmin in the 0.6.0 release note: https://github.com/the-momentum/open-wearables/releases#release-0.6.0

Redis switched from RDF to AOF mode

We have changed the way Redis works to avoid errors in certain edge cases. You can read the details here: #1188. We have, of course, ensured that the data stored in Redis is preserved during the update.

The Docker Dev image has been reduced in size by around 40%

We achieved this by sealing the .dockerignore file and removing old and unused layers from the Dockerfile. This, of course, only applies to the local development version; nothing changes in the production build.

What's Changed

Outgoing webhooks

Apple

  • fix(backend): convert Health Connect blood glucose from mmol/L to mg/dL by @knowald in #1123

Oura

Suunto

  • fix(integrations): accept float totalDistance in Suunto workout schema by @thecodearrow in #1253

Garmin

  • fix(backend): remove dead sleep parameters from Garmin by @KaliszS in #1275
  • feat: Add nap detection for Oura, Garmin by @czajkub in #1149

Strava

Fitbit

  • fix(backend): convert Fitbit workout distance from kilometers to meters by @knowald in #1139

API improvements & optimisation

Observability & deployments

  • fix(backend): enable Redis AOF persistence so the svix queue survives restarts by @knowald & KaliszS in #1188
  • fix(frontend): remove redundant layers & add .output to .dockerignore by @KaliszS in #1296
  • feat(backend): middleware for uvicorn logs by @KaliszS in #1297
  • feat(backend): timestamp for HTTP middleware logs by @KaliszS in #1301

MCP

  • refactor(mcp): typed exception hierarchy + first test harness by @kjsteuer in #894

Docs & chores

New Contributors

Full Changelog: 0.6.2...0.6.3

0.6.2 - Data integrity

Choose a tag to compare

@KaliszS KaliszS released this 26 Jun 13:12
a07818f

Highlights

Counting steps and energy

We have updated the summaries for step count and energy (kcal). The data in the data_point_series table now includes a new is_daily_total column. This is because data providers supply data at varying levels of granularity. Previously, an error occurred during aggregation when some providers sent a daily entry alongside granular entries (e.g. Garmin in 15-minute chunks). From now on, these will be correctly flagged via the new column. The current logic for displaying totals in summaries means that the daily record will be used first. Only if this does not exist will all other entries from that day be summed.

Warning

The next time Open Wearables is launched, an automated script will set the value to true in a new column for the relevant rows in the data points. Intraday aggregates are not flagged as false by the script, so as not to place too much load on the application at start-up (although all new records will be). Therefore, the value none in the is_daily_total column should be treated as equivalent to false.

Active minutes

From now on, active_minutes will be treated as a separate series type and retrieved directly from all providers that supply this data. Until now, we have defined active minutes as the number of minutes in which more than 30 steps were recorded. This definition will remain the fallback for providers who do not supply this data. Our science team is developing a new, improved method for calculating this parameter, which will be implemented in the near future.

Warning

This change will not affect any data that has already been saved to the database. To retrieve correct active minutes for this data, you will need to resynchronise it. With Garmin, you must first delete the data, whereas with other providers, it will be overwritten during historical synchronisation.

Tip

Providers that directly support active minutes include: Garmin, Oura, Polar and Ultrahuman.

Filter sleep sessions by priority

Important

One API endpoint (/users/{user_id}/events/sleep) has been updated. However, this is a backwards-compatible change as a new parameter has been added that is set by default to not alter the existing behaviour of the endpoint.

We have added an optional filter_by_priority flag to the /events/sleep endpoint. This flag is disabled by default and, when enabled, filters out duplicate sleep sessions. On our frontend, this option is available as a toggle, as shown in the screenshot below. The screenshot illustrates a situation in which Oura connects to both Apple Health and Open Wearables, meaning sleep sessions are sent to the database directly from Oura and via Apple Health.

image image

Garmin's respiratory rate

We have corrected the way this parameter is retrieved. Until now, we had been searching for it in raw payloads under the wrong name, meaning it was not being saved to the database.

Improved data summary per user

We have enhanced the visual design of the data summary section and added time filters, enabling you to filter your data summaries by day or by a specified time period.

image

Manual invitation links

Thanks to our contributor, we can now manually copy invitation links, which is useful if you don't have an email account configured.

image

connection.revoked outgoing webhook

A new event is now available in outgoing webhooks, which will notify you when connections with providers are revoked.

What's Changed

Data integrity

  • fix(backend): take daily data OR sum intraday data by @KaliszS in #1232
  • fix(backend): active minutes as a new series type by @KaliszS in #1242
  • fix: correctly filter sleep sessions by priority on frontend by @czajkub in #1249

Oura

Apple Health

Garmin

  • fix: garmin respiratory rate + new data points batch insert chunk size by @KaliszS in #1235

Other Improvements

  • fix: Mark connection as revoked on failed token refresh + connection.revoked outgoing webhook by @czajkub in #1203
  • fix(backend): split iOS & Android coverage by @KaliszS in #1233
  • feat: support manual invite links when email is not configured by @edsammy in #1230
  • fix(frontend): optimise activity tab by @KaliszS in #1245
  • refactor(frontend): improve data summary per user by @KaliszS in #1246

Docs & chores

New Contributors

Full Changelog: 0.6.1...0.6.2

0.6.1 - Data Coverage & Observability

Choose a tag to compare

@KaliszS KaliszS released this 19 Jun 14:00
bbc5686

Highlights

Data Coverage tab

We have added a new Data Coverage tab to the admin panel which shows the data that we support for each provider. This information is always up to date.

Note

Although it includes OpenWearables capabilities, it does not display which data has already been synchronised with the system.

image image

Observability, deployments & logs

The focus of this release was on making debugging and troubleshooting easier by improving the visibility of logs and sync statuses. These changes include the following:

  • adding a workflow to publish an image to Docker Hub
  • linked webhook logs to users and/or traces (including failed attempts to receive a webhook).
  • the application version has been added to Sentry logs
  • improved reliability when saving sync statuses from webhooks to Redis (new skippedstatus added).
  • new metadata has been added to sync statuses, dividing new items into inserted and updated.
image image

Lookback for pull syncs

Although we recommend using webhooks, we have added lookbacks as a safeguard against data gaps if you need to use periodic pull syncs for any reason. This could happen if a user has not opened their wearables app for a long time, causing the live sync window to expire. This issue can now be resolved by syncing data from a specified number of minutes, hours or days in the past.

In order to use this feature, you will need to set the PULL_SYNC_LOOKBACK environment variable, specifying a string containing a number and a time unit. For example, 2d would mean 2 days, 3h would mean 3 hours, and 15m would mean 15 minutes. In periodic pull mode, live sync by default retrieves data recorded from the start of the day on which it is called until the present moment. Setting the lookback extends the start of the sync window by the specified duration and unit of time.

Note

PULL_SYNC_LOOKBACK is disabled by default.

image

This session is in committed state error

Thanks to our new contributor, the bug that caused ORM objects to be downgraded has been fixed.

What's Changed

Observability, deployments & logs

  • ci: add manual Docker Hub image publish workflow by @kczpl in #1179
  • fix(backend): build data source display name from reloaded provider strings by @knowald in #1154
  • chore(backend): add user/trace correlation to webhook logs by @bartmichalak in #1183
  • feat(backend): set Sentry release from version + commit SHA by @bartmichalak in #1196
  • docs: remove publishing the images section by @bartmichalak in #1205
  • fix(backend): webhooks syncs being saved into sync logs by @KaliszS in #1209
  • refactor: improved sync metadata logging by @KaliszS in #1211
  • fix(integrations): drop non-retriable 4xx webhook deliveries instead of retrying forever by @slavarosin in #1053

Docs & chores

Suunto

  • fix(backend): report accurate Suunto sleep sync stats, skip unsaveable sessions by @bwickner in #1182
  • fix(integrations): handle Suunto workout webhook edge cases by @HugoHeneault in #1042
  • fix(backend): suunto tests by @KaliszS in #1189

Garmin

  • fix(backend): persist body composition muscle mass from Garmin webhooks by @bartmichalak in #1194
  • fix(backend): persist blood pressure from Garmin webhooks by @bartmichalak in #1193

Polar

Whoop

  • refactor(backend): construct default Whoop sync window as UTC-aware datetimes by @knowald in #1157

Fitbit

  • fix(integrations): coerce Fitbit string start/end dates to datetime before strftime by @slavarosin in #1054

Other improvements

New Contributors

Full Changelog: 0.6.0...0.6.1

0.6.0 - Women's Health & FIT files (Garmin)

Choose a tag to compare

@KaliszS KaliszS released this 13 Jun 22:53
b0ff3d8

Highlights

Women's Health

We now support menstrual cycle tracking for Garmin wearables. The new women's health tab is only available if the user has fetched any related data. Otherwise, it is invisible.

image

Ingest workout samples (high data granularity)

We have added an option to enable second-by-second granularity for certain data from Garmin, such as heart rate, speed, cadence, power, latitude, longitude, elevation and running dynamics (which require a FIT file), as well as air temperature (also requires FIT file to be available).

Caution

This option is disabled by default. To enable it, you must set the INGEST_WORKOUT_SAMPLES environment variable to true.

Warning

According to our tests, this will increase database memory usage by approximately 170 MB per user per month.

FIT file parser and saving to S3

A parser for FIT files has been implemented. Currently, there is no option to upload these files manually; they are processed automatically via Garmin webhooks. However, the same parser will eventually support other providers.

Garmin currently extracts the following data from FIT files and saves it to the database:

  • training segments (laps, swim lengths, etc.)
  • time spent in each heart rate zone during a workout
  • time spent in each power zone during a workout
  • air temperature (INGEST_WORKOUT_SAMPLES required)
  • running vertical oscillation (INGEST_WORKOUT_SAMPLES required)
  • running vertical ratio (INGEST_WORKOUT_SAMPLES required)
  • running stride length (INGEST_WORKOUT_SAMPLES required)
  • running ground contact time (INGEST_WORKOUT_SAMPLES required)
  • running stance time balance (INGEST_WORKOUT_SAMPLES required)

Note

We have added a new environment variable called STORE_FIT_FILES, which is set to false by default. When this is set to true, raw FIT files from Garmin will be saved to a previously configured S3 bucket. See the S3 configuration guide for more information: https://openwearables.io/docs/dev-guides/raw-payload-storage

Warning

Storing raw FIT files in S3 results in an additional data usage of between a few MB and a dozen or so MB per user per month.

Many Oura bugs fixed

We have fixed several bugs relating to the Oura integration. We have added time zone offset saving to the database, supported sleep summary metrics, improved data retrieval for current day summaries and migrated HRV from SDNN to RMSSD. Existing SDNN values in the database will be migrated by an automatic script that runs when the application starts.

Added redis TLS support

To enable TLS support (rediss://), set the new environment variable REDIS_SSL to true.

The day prefix for sleep start times that began before midnight

From now on, the start time of a sleep session in the sleep section will be prefixed with the previous day’s name if it was before midnight. If a sleep session ends after midnight, it will be displayed under the following day in the admin UI (as it always has been). Please note that this is dynamic and will adjust to the user’s time zone.

image

Support for synchronising the same data across multiple accounts

We have added dedicated support for users who need to synchronise their devices with several Open Wearables accounts for testing purposes. No configuration is required to use this feature. Simply pair the relevant accounts with your wearables account and synchronise any of them. A badge will appear in each account to indicate which accounts are linked, along with the relevant sync statuses. This shows that, for a given account, the source of the currently syncing data is a linked Open Wearables account. This means that, instead of repeatedly syncing the same data - which is virtually impossible with Garmin during backfilling, for example - it will be synced once and copied to the other accounts. More information: https://openwearables.io/docs/architecture/multi-account-sync

image image image

What's Changed

Garmin FIT files & high granularity data support

  • feat(backend): add migration with new jsonb fields for workout_details by @KaliszS in #1079
  • feat(backend): add average_cadence support for Garmin by @KaliszS in #1080
  • feat(backend): Garmin workout samples by @KaliszS in #1082
  • feat(backend): FIT files parser by @KaliszS in #1096
  • fix(backend): fit map stride_length -> step_length by @KaliszS in #1097
  • feat(backend): Garmin mappings for elevation, GPS & running dynamics + optional saving FIT files to s3 by @KaliszS in #1099
  • fix(backend): stop storing Garmin average HR as heart_rate_min by @knowald in #1121
  • feat(backend): Garmin support for workout segments (laps, swim lengths etc) by @KaliszS in #1118
  • fix(backend): null workout heart_rate_min rows copied from average HR by @knowald in #1133
  • feat(backend): HR & Power Zones [Garmin] by @KaliszS in #1175

Garmin Women's Health

Oura bugs

Apple Health bugs

Suunto bugs

  • feat(integrations): emit resting_heart_rate series from sleep HRMin by @HugoHeneault in #1055

Polar bugs

  • feat(backend): map INDOOR_CYCLING, JUMP_ROPE, KICKBOXING_MARTIAL_ARTS by @kozikow in #1110

Infrastructure improvements

  • fix: mount redis volume at /data so persistence works by @knowald in #1127
  • feat(backend): support TLS (rediss://) for Redis connections by @kczpl in #1134

Other improvements

  • fix(frontend): stop activity & sleep dates shifting a day for users west of UTC by @bartmichalak and @KaliszS in #1160
  • refactor: fixed scopes db saving & improved UI by @KaliszS in #1088
  • feat: Add webhook management endpoints by @czajkub in #1049
  • fix: update last_synced_at on webhook receive by @czajkub in #1145
  • feat: sharable syncs (the same data) between different OW users by @KaliszS in #1147
  • feat(frontend): OpenWearables version visible under logout button by @KaliszS in #1165

Docs & chores

  • fix(docs): update GitHub repository links in iOS SDK documentation by @bartmichalak in #1064
  • docs: update available providers in readme by @bartmichalak in #1069
  • chore: bump the npm-minor-patch group across 1 directory with 4 updates by @dependabot[bot] in #1057
  • chore: update sentry-sdk[fastapi] requirement from >=2.59.0 to >=2.60.0 in /backend by @dependabot[bot] in #1061
  • refactor(backend): update deps (redis v8) by @KaliszS in #1087
  • chore: bump the npm-minor-patch group in /frontend with 13 updates by @dependabot[bot] in #1102
  • docs(mcp): remove formatted duration fields absent from sleep tool output by @knowald in #1122
  • docs: clarify migrations auto-run on deploy temporarily by @bartmichalak in #1129
  • docs: note developer program is on hold by @bartmichalak in #1130
  • docs: drop "| Open Wearables" suffix from page titles by @bartmichalak in #1131
  • docs: add Fitbit and Google Health Connect to the coverage matrix by @knowald in #11...
Read more

0.5.2 - Polar refactor

Choose a tag to compare

@KaliszS KaliszS released this 20 May 08:52
a2060c7

Highlights

Polar got refactor & webhook support

Polar now synchronises sleep and timeseries data. By default, the Periodic Pullsync method is enabled, but webhooks can be activated in Settings > Providers.

image

Expanded Sport Type Support across Multi-Provider Mappings

Expanded the WorkoutType enum to explicitly support specific activities that were previously bundled into WorkoutType.OTHER. Over 70 provider-specific mappings across Apple, Garmin, Suunto, and WHOOP have been updated. New explicitly supported sports include: Cricket, lacrosse, racquetball, bowling, archery, fencing, fishing, wakeboarding, parkour, curling, and more.

Garmin fetches new fields

allDayRespiration and moveIQActivities have been added to the list of Garmin-specific attributes that we support.

Strict RMSSD enforcement for HRV-CV Resilience Score

Temporarily disabled the automatic fallback to SDNN when overnight RMSSD data is missing within the sleep window. Until SDNN-based calculations are fully validated, users without RMSSD data will now receive a null score instead of a potentially misleading fallback value.

What's Changed

  • refactor(backend): updated all python dependencies by @KaliszS in #1023
  • refactor(frontend): updated all TypeScript dependencies by @KaliszS in #1025
  • fix(backend): garmin allDayRespiration & moveIQActivities by @KaliszS in #1026
  • feat(backend): add recovery summary endpoint by @KaliszS in #1027
  • chore: disable SDNN fallback in HRV-CV score calculation pending validation by @FBegiello in #1040
  • fix: commit each data type individually and rollback session on exception for Oura and Whoop by @czajkub in #1022
  • fix: Cast to local date for summary endpoints by @czajkub in #1033
  • fix(backend): validation for date parameters in OpenAPI specs by @KaliszS in #1052
  • fix(users): skip invalid user rows during email validation by @zxjt2z42px-creator in #1032
  • fix: Handle duplicate EventRecordDetail inserts gracefully with savepoint by @czajkub in #1047
  • refactor(frontend): update dependencies by @KaliszS in #1056
  • fix: Add missing workout types, rename series type to SDK by @czajkub in #1048
  • feat: Polar refactor by @czajkub in #1041
  • fix(integrations): emit outgoing webhook for suunto live workout push by @HugoHeneault in #1045

New Contributors

Full Changelog: 0.5.1...0.5.2

0.5.1 - Oura & Strava Webhooks

Choose a tag to compare

@KaliszS KaliszS released this 08 May 15:07
08d8af3

Highlights

Oura & Strava got webhook support

By default Periodic pull only is enabled, but webhooks can be activated in Settings > Providers.

image

Live sync status is now available

It's already avilable in our Admin Panel (there is global Syncs tab, but all of statues are also visible in user profiles).

If you need to integrate this feature with your backend, checkout our integration guide: https://openwearables.io/docs/api-reference/guides/sync-status-stream

Sync statuses are also available as outgoing webhook events. This is the preferred solution if you need to permanently store this data, because we only store it in Redis for 24 hours.

image image

New Admin Panel Dashboard design

image

RMSSD HRV is now included in Body Summary

image

What's Changed

  • chore: bump typescript from 5.9.3 to 6.0.2 in /frontend by @dependabot[bot] in #993
  • fix: add hrv rmssd to body summary by @czajkub in #949
  • refactor: migrate all recovery scores from data_point_series to health_score by @czajkub in #950
  • refactor: polish Oura webhooks and adjust them to new webhook pattern by @czajkub in #990
  • refactor(frontend): Dashboard v2 by @KaliszS in #1003
  • feat: sync status (fastapi SSE) by @KaliszS in #986
  • feat: Sleep score recalculation after sleep session merge by @czajkub in #884
  • refactor(frontend): Dashboard redesigned by @KaliszS in #1015
  • fix(backend): scope x100 ratio->percent conversion to Apple provider only by @kjsteuer in #917
  • refactor: Strava webhooks by @czajkub in #1010

Full Changelog: 0.5.0...0.5.1

0.5.0 - Product Hunt release!

Choose a tag to compare

@KaliszS KaliszS released this 29 Apr 11:25
464c61e

Highlights

Product Hunt release

Important

We launched our campaign on Product Hunt today and would really appreciate your support in the form of an upvote. Thanks! https://www.producthunt.com/products/open-wearables

Webhooks Ecosystem

A full outgoing webhook infrastracture has been implemented (background processing, managment UI and other improvements since 0.4.3). We have also added more incoming webhooks support for specific providers: Suunto & Whoop. More incoming!

image image

Health Data Precision (Sleep & Heart)

Significant effort went into refining sleep calculations (local time zone grouping, excluding awake time) and heart metrics (HRV-CV precision), greatly increasing the reliability of the analytics.

Developer Experience (DX) Evolution

Improvements of seed data generator and MCP server makes it much easier for developers to build, test and debug applications on top of Open Wearables.

What's Changed

Integrations

  • feat: Add incoming webhook handlers for Whoop by @czajkub in #940
  • feat: Suunto webhooks + provider capabilities reworked by @KaliszS in #942
  • feat(backend): accept Health Connect exercise vocabulary by @kjsteuer in #864
  • fix(backend): fall back to source.app_id / bundle_identifier for original_source_name by @kjsteuer in #863
  • feat(backend): recognize Health Connect power/speed/cadence/total_calories vocabulary by @kjsteuer in #866
  • fix: Oura data extraction improvements by @czajkub in #889
  • chore(backend): align Strava webhook verify token with other providers by @knowald in #977

External Webhooks

  • fix(backend): full sample data in timeseries webhook payloads by @KaliszS in #904
  • fix: svix webhooks improvements by @KaliszS in #923
  • fix: emit webhooks from bulk_create_details for SDK imports by @bartmichalak in #926

Algorithms & Data Processing

  • fix(sleep): group sleep summaries by local start date instead of UTC … by @FBegiello in #852
  • fix: sleep calculation overflow by @FBegiello in #855
  • fix: prevent sleep duration duplication during merge and log skips by @czajkub in #842
  • fix: display sleep duration correctly via endpoint by @czajkub in #883
  • feat: add resilience score celery task by @czajkub in #845
  • fix(backend): align health scores and workout fields with real provider formats by @FBegiello in #892
  • fix: sleep duration excludes awake time, add time in bed column by @bartmichalak in #929
  • fix: align sleep score date to wake-up date via direct record linkage by @FBegiello in #900
  • fix: save sleep efficiency correctly for Apple and Garmin by @czajkub in #944
  • fix: improve HRV-CV storage precision and add 0-100 scoring by @FBegiello in #919
  • fix: improve HRV-CV storage precision and add 0-100 scoring (retry) by @bartmichalak in #958
  • fix(backend): correct peak_expiratory_flow_rate unit to L/min by @bartmichalak in #979

Dashboard & User Experience

Documentation

  • docs: fix e2e guide pagination field + provider list endpoint by @farce1 in #886
  • fix(integrations): subscribe the Celery worker to the webhook_sync queue by @srirsiva in #962
  • docs: trim descriptions to ≤155 chars and separate display title from og:title by @blackbudda in #888
  • docs: add Apple Health integrations list by @bartmichalak in #890
  • docs: document grace-period flag for historical sync on connect by @bartmichalak in #897
  • docs: add Seed Data Generator dev guide by @bartmichalak in #903
  • docs: refresh backend e2e integration guide by @bartmichalak in #909
  • docs: add Health Scores documentation by @anjazych in #908
  • docs: note cloud_only flag for web integration flows by @bartmichalak in #922
  • docs: fix typo "envionment" in Makefile help text by @knowald in #967
  • docs: fix Whoop Developer Dashboard link by @bartmichalak in #982

Infrastructure, Backend & Chores

  • perf(backend): offload webhook processing to background tasks by @sLeeNguyen in #685
  • refactor(backend): move historical sync dispatch into provider strategy by @bartmichalak in #817
  • chore: update sentry-sdk[fastapi] requirement from >=2.42.1 to >=2.57.0 in /backend by @dependabot[bot] in #872
  • feat(mcp): add get_timeseries tool for granular sample queries by @kjsteuer in #882
  • chore: bump jsdom from 28.1.0 to 29.0.0 in /frontend by @dependabot[bot] in #869
  • chore: bump the npm-minor-patch group across 1 directory with 15 updates by @dependabot[bot] in #898
  • chore: bump vite from 7.3.1 to 8.0.0 in /frontend by @dependabot[bot] in #870
  • fix: svix-server railway deployment by @KaliszS in #891
  • chore: deprecate external_user_id in OpenAPI schema by @bartmichalak in #905
  • chore(dashboard): remove legacy seed_data_service module by @FBegiello in #912
  • chore: remove unfinished Python SDK by @bartmichalak in #906
  • fix: slow tests after adding lifespan with webhook event registration by @KaliszS in #924
  • feat: continuous time-series in seed generator by @bartmichalak in #931
  • chore: bump the npm-minor-patch group in /frontend with 5 updates by @dependabot[bot] in #913
  • chore: bump @vitejs/plugin-react from 5.2.0 to 6.0.1 in /frontend by @dependabot[bot] in #914
  • fix(backend): move faker to dependencies group by @KaliszS in #937
  • feat(backend): add trace_id & user_id to logs by @KaliszS in #941
  • chore: bump the npm-minor-patch group in /frontend with 6 updates by @dependabot[bot] in #946
  • chore: optimize backend docker build caching and startup time...
Read more

0.4.3

Choose a tag to compare

@KaliszS KaliszS released this 14 Apr 18:15
e1bac42

Highlights

Grace-period feature flag for automatic historical sync on provider connect

A HISTORICAL_SYNC_ON_CONNECT flag has been added, which is set to True by default and ensures that historical data is synchronised when pairing devices with Open Wearables (as was the case prior to version 0.4.2). In about two months’ time, we will remove it, and from then on, syncing historical data will require pressing a separate button by default in our UI portal or by accessing separate endpoint /api/v1/providers/{provider}/users/{user_id}/sync/historical when you integrate your backend. You can switch to this functionality now by setting this variable in .env to False, which we recommend.

Open Wearables Health Scores (Sleep & Resilience)

Warning

Our scores are still in beta, so there may be bugs & the algorithms may change

We are officially introducing our own proprietary scores! This release brings the new Sleep Score and Resilience Score, calculated directly by Open Wearables. To support this, we've added background Celery tasks that automatically calculate daily sleep scores for your users. Huge thanks to @anjazych, @FBegiello and @czajkub for driving these new composite metrics.

Outgoing Webhooks

Warning

This is also beta functionality, so bugs may still occur.

By popular demand, we've added support for outgoing (external) webhooks. Your application can now listen to events and data streams from Open Wearables in real-time, greatly improving sync architecture and reducing the need for polling. Full documentation for this feature is now available here: https://openwearables.io/docs/api-reference/guides/webhooks

Dashboard & Observability Upgrades

We've made several quality-of-life improvements for developers. You'll find a new SDK logs endpoint to help with sync observability, and a handy "seed data generator" built straight into the dashboard settings to speed up your testing and development workflows.

What's Changed

Integrations

  • feat: Save health scores from Garmin and Whoop by @czajkub in #818
  • fix: resolve source name correctly for workouts/sleep by @czajkub in #815

API & Backend

  • feat: introduce Sleep Score by Open Wearables by @FBegiello in #787
  • feat: introduce Resilience Score by Open Wearables by @FBegiello in #803
  • feat: add celery tasks for calculating daily sleep scores by @czajkub in #826
  • feat(backend): outgoing (external) webhooks by @KaliszS in #821
  • feat: add SDK logs endpoint for sync observability by @bartmichalak in #819
  • feat(backend): grace-period feature flag for automatic historical sync by @KaliszS in #850

Frontend & Dashboard

Docs & SEO

Chores

New Contributors

Full Changelog: 0.4.2...0.4.3

0.4.2

Choose a tag to compare

@bartmichalak bartmichalak released this 09 Apr 10:32
d355117

Highlights

Garmin webhooks & historical sync

Garmin has been updated to work with the incoming webhook streaming architecture introduced in v0.4.1. Live sync now uses webhooks as the priority path, and there's a new historical sync option in the UI - backfill up to 365 days of data. Connection cards in the portal now show provider-specific historical limits and a history sync action. Thanks to @KaliszS for driving this one 🎉

Breaking change: Historical data no longer syncs automatically on provider connection. Users now trigger historical sync explicitly through the UI.

More: Webhook Handler

Health Scores API

New GET /users/{user_id}/health-scores endpoint by @czajkub. Supports filtering by category, provider, data source, and date range, plus pagination and timeseries metadata. There's also a bulk-create path for ingesting scores from providers. This lays the groundwork for surfacing composite health metrics in the app.

Cleaner API Reference

The Mintlify API docs got a major cleanup - endpoints are now tagged as External, Internal, or System, and the API Reference tab only shows the public-facing ones. No more scrolling past webhook receivers and debug routes to find the endpoints you actually need.

Redis memory leak fix

Celery's pidbox reply keys were accumulating in Redis indefinitely. Added TTL and expiration (300s) to control queues so Redis cleans them up automatically. If you've noticed Redis memory creeping up over time, this should take care of it.


What's Changed

Integrations

  • feat: Garmin incoming webhooks & UI portal changes by @KaliszS in #764
  • fix: handle lowercase pulseox webhook payloads by @saschabuehrle in #766
  • fix(backend): skip push-only providers in sync_vendor_data task by @haoyu-haoyu in #774
  • refactor(backend): remove Auto Health Export connector endpoint and all related logic by @bartmichalak in #779
  • chore: add read:body_measurement scope to Whoop config in .env.example by @bartmichalak in #811

API & Backend

  • feat: add health score model by @czajkub in #793
  • feat(backend): add service and endpoint for health scores by @czajkub in #804
  • refactor(backend): categorize API endpoints and curate docs API reference by @bartmichalak in #778
  • fix: set TTL on Celery pidbox reply keys to prevent Redis memory leak by @bartmichalak in #559
  • refactor(backend): sync data router by @KaliszS in #812

Docs

  • feat(docs): updated provider integration guide [webhooks] by @KaliszS in #807

Frontend

  • chore: bump the npm-minor-patch group in /frontend with 10 updates by @dependabot[bot] in #809

New Contributors

Full Changelog: 0.4.1...0.4.2