Sync a connected wearable provider now
- Sandbox: available
- Staging: available
- Production: unknown
Pulls the signed-in patient's latest data from a connected wearable provider now, instead of waiting for the daily sync, and writes it as Observations.
Auth: bearer token of a patient. Scope: the caller's project and Patient profile (from the token).
Behaviour
- Oura, WHOOP, Strava, Withings, Polar, Ultrahuman and Suunto can be synced. Other providers answer
501 SYNC_NOT_SUPPORTEDfor now; they can still be connected. - The first sync reads the last 30 days. Each later sync reads from 2 days before
syncedThrough, because providers revise recent records. - Each reading becomes one Observation about the patient, created once. A reading already held is updated in place, so a value the provider revised within the re-read window replaces the earlier one, and
createdcounts only new ones. - What is synced:
- heart rate, resting heart rate, heart-rate variability (RMSSD), SpO2, respiratory rate;
- daily steps;
- sleep duration and its light, deep, REM and awake parts.
- What each provider gives:
- Oura: per sleep period, heart rate, resting heart rate, HRV, respiratory rate and the sleep stages; daily SpO2 and steps.
- WHOOP: resting heart rate, HRV and SpO2 from each recovery; respiratory rate and the sleep stages; each day's average heart rate.
- Strava records workouts only, so it gives each workout's average heart rate.
- Withings gives heart-rate and SpO2 measurements, daily steps, and per night its sleep summary: the night's average heart rate, average respiratory rate and RMSSD, and the sleep durations.
- Polar gives each night's sleep stages, and from its nightly recharge the night's average heart rate, heart-rate variability (RMSSD) and breathing rate, dated by the night. Its daily steps are dated by the watch's local day.
- Ultrahuman: SpO2 (when given as one value), resting heart rate during sleep, daily steps and the sleep stages. Its per-sample heart rate and HRV are not synced. It is asked one day at a time, so a first sync makes 31 requests. It gives no respiratory rate.
- Suunto: daily steps only, for now.
heart_rateis not the same kind of value everywhere: for Oura, Withings and Polar it is a sleep or nightly average (Withings adds its heart-rate measurements), for WHOOP the day's average, and for Strava the average of one workout.- Codes are LOINC, except heart-rate variability: LOINC has no RMSSD code, so it is
https://api.ovok.com/fhir/CodeSystem/wearables|hrv-rmssd. - Every Observation carries the provider in
meta.tag(https://api.ovok.com/fhir/CodeSystem/wearables-source) and indevice.display. - Wearable Observations are not forwarded to Signals.
- A daily sync runs for every connection at 08:00 UTC; this route only brings it forward, at most once every 15 minutes.
- Withings also tells ovok when a patient has new data, and a sync of that connection follows about 30 seconds later. ovok subscribes each patient when they connect; nothing needs setting up in the Withings portal beyond the redirect URL.
- Withings Sleep Analyzer mats on ovok's own Withings device integration are separate: their bed presence and sleep series do not come through this connection, and this connection does not change them.
- A sync never reads further back than 30 days: when the provider or the connection is out of reach for longer, the oldest days are lost.
- Polar lists sleep and nightly recharge for its last 28 days only, so a first Polar sync holds 28 days of those and 30 of steps.
- Polar's access is long-lived and cannot be refreshed: once Polar stops accepting it, the sync answers
409 RECONNECT_REQUIRED.
Errors
404 CONNECTION_NOT_FOUND: the patient has not connected this provider, or disconnected it during the sync.404 PROVIDER_NOT_AVAILABLE: the project removed or switched off the provider.409 SYNC_IN_PROGRESS: a sync of this connection is already running.409 CONNECTION_CHANGED: the patient reconnected the provider while it was syncing; nothing was written over the new connection. Sync again.409 RECONNECT_REQUIRED: the provider no longer accepts the connection, for example after the patient removed the app's access. Connect it again withPOST /v1/wearables/connections/<provider>/authorize.GET /v1/wearables/connectionsshowsreconnectRequired: truemeanwhile.429 SYNC_TOO_SOON: the last sync was under 15 minutes ago. Retry after the seconds inRetry-After; the provider was not asked.500 SYNC_FAILED: the sync failed on our side; nothing moved, so try again later.502 PROVIDER_UNAVAILABLE: the provider failed, could not be reached, or answered something unreadable; nothing moved, so try again later.503 PROVIDER_RATE_LIMITED: the provider is limiting requests. Retry after the seconds inRetry-After.
Example
curl -X POST 'https://api.sandbox.ovok.com/v1/wearables/connections/oura/sync' \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NjcyNjg4MDAsImV4cCI6MTc2NzI3MjQwMH0.Ql3B_ztDvctlyZ2z0vp4sl3ExXRQGkzmxdmTAqxNzZk'bearerAuthorizationBearer <JWT>provider*string"oura""whoop""strava""withings""polar""suunto""garmin""ultrahuman"What this sync wrote, and up to when the data is now synced.
application/json- response
provider*string"oura""whoop""strava""withings""polar""suunto""garmin""ultrahuman"created*integerHow many Observations this sync wrote; readings already held are skipped.
syncedThrough*stringUp to when the data is now synced.
curl -X POST 'https://api.sandbox.ovok.com/v1/wearables/connections/oura/sync'{ "provider": "oura", "created": 0, "syncedThrough": "string"}Start connecting a wearable provider POST
Starts connecting the signed-in patient's account at a wearable provider. Open the returned `authorizationUrl` in a browser or an auth session (`ASWebAuthenticationSession`, Custom Tabs) whose callback scheme is your patient app's. **Auth:** bearer token of a patient. **Scope:** the caller's project and Patient profile (from the token). ## Flow 1. The patient consents on the provider's page. 2. The provider sends the browser to `GET /v1/wearables/oauth/<provider>/callback`. 3. ovok exchanges the code for tokens, stores them encrypted, and redirects the browser (`302`) to the project's patient app at `/wearables/<provider>?result=connected|denied|failed`. ## Behaviour - The URL is valid for 10 minutes and for one use. - Polar shares data only for users registered to the app, so ovok registers the patient with Polar right after the code exchange. A patient who has not accepted Polar's mandatory consents is refused, and the callback ends with `result=failed`. - Connecting a provider the patient is already connected to replaces the stored tokens. - A provider the project has not set up or has switched off answers `404 PROVIDER_NOT_AVAILABLE`. - A project with no usable patient app URL (`PATIENT_APP_URL`) answers `409 APP_URL_NOT_CONFIGURED`: the flow would have nowhere to return to. - When the authorization cannot be stored, the answer is `503 STATE_STORE_UNAVAILABLE` with `Retry-After`. ## Example ```bash curl -X POST 'https://api.sandbox.ovok.com/v1/wearables/connections/oura/authorize' \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NjcyNjg4MDAsImV4cCI6MTc2NzI3MjQwMH0.Ql3B_ztDvctlyZ2z0vp4sl3ExXRQGkzmxdmTAqxNzZk' ```
Disconnect a wearable provider DELETE
Disconnects the signed-in patient from a wearable provider: the connection is deleted and its tokens are no longer readable through the API. **Auth:** bearer token of a patient. **Scope:** the caller's project and Patient profile (from the token). ## Behaviour - Before the connection is deleted, ovok undoes what it set up at the provider where the provider allows it: Withings stops notifying ovok about the patient, and Polar deregisters the patient, which also revokes the app's access. Other providers keep the app's access until the patient removes it in the provider's own settings. - A failure at the provider never stops the disconnect; the connection is deleted all the same. - The resource history keeps the earlier versions, with the tokens still encrypted. - A provider the patient has not connected answers `404 CONNECTION_NOT_FOUND`. ## Example ```bash curl -X DELETE 'https://api.sandbox.ovok.com/v1/wearables/connections/oura' \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NjcyNjg4MDAsImV4cCI6MTc2NzI3MjQwMH0.Ql3B_ztDvctlyZ2z0vp4sl3ExXRQGkzmxdmTAqxNzZk' ```