Prerequisites
Before you begin, ensure you have:- Open Wearables instance running (self-hosted or cloud)
- API Key generated from the settings tab in Open Wearables Developer portal
- Your backend framework ready (examples use Python/FastAPI, but concepts apply to any language)
Environment Variables
Add these to your application’s environment:Authentication
SDK Authentication (Mobile Apps)
If you’re building a mobile app that pushes health data (e.g., Apple Health from iOS), use SDK tokens instead of API keys. SDK tokens are user-scoped JWT tokens that only authorize data push endpoints.- When to Use
- How It Works
Step 1: Create an Application
First, register an application to getapp_id and app_secret:
Step 2: Exchange Credentials for User Token
When a user logs into your mobile app, your backend exchanges the app credentials for a user-scoped token:Step 3: Use Token in Mobile App
The mobile app uses this token to push health data to the SDK sync endpoints:SDK tokens are only valid for
/sdk/ endpoints. All other API endpoints will return 401 for SDK tokens. Use API keys for those endpoints. The user_id in the URL must match the user the token was issued for.Integration Flow Overview
Step 1: User Registration
When a user registers in your application, create a corresponding user in Open Wearables.Create User
All fields in the create-user payload are optional - you can evenPOST an empty body and get back a valid user.
Response
null.
Update Your User Model
Add a field to store the Open Wearables user ID:Open Wearables does not enforce uniqueness on
email, first_name, or last_name - calling POST /api/v1/users twice with the same email will happily create two separate user records. Your backend is the source of truth for user identity and is responsible for ensuring you only create one Open Wearables user per user in your system (e.g. by storing open_wearables_user_id on your own user row and checking it before creating).Step 2: Connect Wearable Provider
Connect users to their wearable devices via OAuth.List Available Providers
The list of providers is dynamic - which ones are enabled depends on your instance’s configuration and whether OAuth credentials are set up. Fetch it at runtime rather than hardcoding a list:
Response:
Update Provider Setting
For providers wherelive_sync_configurable is true, the instance admin can switch the live sync mode between periodic REST polling and real-time webhook delivery.
live_sync_mode is included in the request body for a provider where live_sync_configurable is false (e.g. Garmin, which is webhook-only).
live_sync_mode value:
Returned when an invalid mode string or an explicit null is sent for live_sync_mode.
Passing
"live_sync_mode": null in the JSON body is invalid and returns 400. To leave the current mode unchanged, omit the field entirely from the request body.Pull mode and late revisions. In
pull mode each periodic sync starts from the connection’s last synced time and moves forward, so a day a provider revises after we passed it (e.g. Oura finalising a step count once the ring finishes uploading) is not re-fetched. Set the server-side PULL_SYNC_LOOKBACK env var (compact duration — 2d, 20h, 90m, 1d12h; disabled by default, capped per provider by its max_historical_days) to re-fetch a trailing window on every live pull. Re-fetched days are upserted, so unchanged values stay as they are. webhook mode is unaffected — providers push the exact object that changed.Get Authorization URL
Response
Frontend Integration Flow
1
User clicks 'Connect Garmin'
Your frontend initiates the connection flow.
2
Your backend calls the authorize endpoint
Request the authorization URL from Open Wearables.
3
Redirect user to authorization_url
The user authenticates with their wearable provider.
4
Provider redirects to Open Wearables callback
Open Wearables handles the OAuth callback automatically.
5
Open Wearables redirects to your app
Configure a
redirect_uri parameter to return users to your app.Custom Redirect URI
To redirect users back to your app after OAuth:Check Connection Status
Verify a user’s connected providers:Response
Step 3: Historical Backfill (Automatic)
You do not need to explicitly trigger a sync after OAuth completes. On the first successful provider connection, Open Wearables automatically dispatches a historical backfill:- Up to 90 days for pull-based providers (Polar, Suunto)
- Up to 30 days for Garmin (webhook-based backfill capped at 30 days from the user’s consent date - further back is not retrievable)
HISTORICAL_SYNC_ON_CONNECT flag (default: true). It’s a grace-period flag introduced in v0.4.3 - the default will flip to false in a future release and the flag will eventually be removed. Once you’re ready to control historical backfill yourself, set HISTORICAL_SYNC_ON_CONNECT=false and call POST /api/v1/providers/{provider}/users/{user_id}/sync/historical explicitly. See PR #897 for background.
Ongoing sync after the initial backfill happens automatically - webhooks push updates for providers that support them, and pull-based providers are polled on a schedule. Your integration only needs to connect the user; fetching data (next step) is the read side.
Step 4: Retrieve Health Data
All data endpoints require theX-Open-Wearables-API-Key header and return a PaginatedResponse wrapper:
cursor=<next_cursor> until has_more is false. The health-scores endpoint uses offset/limit instead of a cursor but returns the same envelope.
Activity summary
Daily aggregates: steps, distance, floors, active/total kcal, active/sedentary minutes, intensity minutes, heart-rate stats.
Sleep summary
Daily sleep: duration, efficiency, stages (awake/light/deep/rem minutes), interruptions, naps, avg HR/HRV/SpO2.
Body summary
Point-in-time body metrics grouped into
slow_changing (weight, height, BMI, body fat), averaged (resting HR, HRV over 1-7 days) and latest (temperature, blood pressure within a recency window).Timeseries
Granular samples for any
SeriesType (heart rate, steps, SpO2, etc.) with optional resolution bucketing (raw, 1min, 5min, 15min, 1hour).Workouts
Workout sessions with type, duration, calories, distance, avg/max heart rate, pace, elevation gain, and
source provider metadata.Health scores
Provider-computed scores (sleep, recovery, readiness, stress, body battery, strain) with optional
components breakdown. Filter by category or provider.Response Shapes
- ActivitySummary
- SleepSummary
- RecoverySummary
- BodySummary
- TimeSeriesSample
- Workout
- HealthScore
date and source is nullable.Complete Integration Example
Here’s a complete Python client class for Open Wearables integration:Troubleshooting
Docker container can't reach Open Wearables on localhost
Docker container can't reach Open Wearables on localhost
If your app runs in Docker and Open Wearables runs on the host machine:
docker-compose.yml
Multiple users created for the same email
Multiple users created for the same email
email has no unique constraint in Open Wearables - calling POST /api/v1/users twice with the same email will create two separate records. Your backend is the source of truth for user identity, so store the Open Wearables id (UUID) on your own user row the first time you create it, and reuse that ID for every subsequent call:external_user_id field is still DB-unique, but it is deprecated and no data-fetching endpoint accepts it - don’t use it for deduplication.Data endpoints return empty results right after OAuth
Data endpoints return empty results right after OAuth
Historical backfill is dispatched asynchronously on connect and can take anywhere from seconds to several minutes depending on the provider and the amount of history. Poll
GET /api/v1/users/{user_id}/connections - last_synced_at flips from null to a timestamp once the first sync finishes. For Garmin’s async export, full history can take hours.If HISTORICAL_SYNC_ON_CONNECT=false in your instance, no backfill runs automatically - call POST /api/v1/providers/{provider}/users/{user_id}/sync/historical yourself.Timeseries endpoint returns empty data
Timeseries endpoint returns empty data
start_time, end_time, and types are all required. types is repeated:SeriesType you’re requesting exists for this user - check GET /api/v1/users/{user_id}/summaries/data for a breakdown of what’s been ingested.OAuth callback not redirecting to my app
OAuth callback not redirecting to my app
Pass the
redirect_uri parameter when getting the authorization URL:Next Steps
API Reference
Complete API documentation with all endpoints.
Provider Setup
Configure OAuth credentials for each provider.
Data Model
Understand the unified health data model.
GitHub
View source code and contribute.

