Disconnect a wearable provider
- Sandbox: available
- Staging: available
- Production: unknown
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
curl -X DELETE 'https://api.sandbox.ovok.com/v1/wearables/connections/oura' \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NjcyNjg4MDAsImV4cCI6MTc2NzI3MjQwMH0.Ql3B_ztDvctlyZ2z0vp4sl3ExXRQGkzmxdmTAqxNzZk'bearerAuthorizationBearer <JWT>provider*string"oura""whoop""strava""withings""polar""suunto""garmin""ultrahuman"The connection is gone and its tokens are no longer readable. Where the provider allows it, its notifications are stopped first.
curl -X DELETE 'https://api.sandbox.ovok.com/v1/wearables/connections/oura'Sync a connected wearable provider now POST
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_SUPPORTED` for 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 `created` counts 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_rate` is 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 in `device.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 with `POST /v1/wearables/connections/<provider>/authorize`. `GET /v1/wearables/connections` shows `reconnectRequired: true` meanwhile. - `429 SYNC_TOO_SOON`: the last sync was under 15 minutes ago. Retry after the seconds in `Retry-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 in `Retry-After`. ## Example ```bash curl -X POST 'https://api.sandbox.ovok.com/v1/wearables/connections/oura/sync' \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NjcyNjg4MDAsImV4cCI6MTc2NzI3MjQwMH0.Ql3B_ztDvctlyZ2z0vp4sl3ExXRQGkzmxdmTAqxNzZk' ```
Wearable provider OAuth callback GET
Where a wearable provider sends the patient's browser after consent. Register it as the redirect URL of your app at the provider; you never call it yourself. **Auth:** none. The one-time `state` from `POST /v1/wearables/connections/<provider>/authorize` identifies the patient and project. ## Behaviour - `state` is taken once. An expired, reused or unknown `state`, or one started for another provider, answers `422 INVALID_STATE`. - With a valid `state`, the answer is always `302` to the project's patient app at `/wearables/<provider>?result=…`: - `connected`: the tokens are stored; - `denied`: the patient refused, or the provider sent an `error`; - `failed`: the code exchange or the save failed, the project switched the provider off meanwhile, or the provider refused to register the patient (Polar, when the patient has not accepted its consents). Nothing stays connected. - `result` is for display only: the app should read `GET /v1/wearables/connections` for the state of record. - When the pending authorization cannot be read, the answer is `503 STATE_STORE_UNAVAILABLE` with `Retry-After`; the `state` is kept, so the same callback can be retried.