Connect a patient's wearable
Send a signed-in patient through the provider's consent page and back to your app, with @ovok/core, React hooks or plain HTTP.
A patient connects their own account, from your patient app, while signed in as themselves. Every patient route reads the project and the Patient from the bearer token; a practitioner or admin token answers 403 PATIENT_PROFILE_REQUIRED.
Before you start
- The provider is set up and enabled in the project.
PATIENT_APP_URLis set. Ovok appends/wearables/<provider>to it as written, sohttps://app.example.comreturns tohttps://app.example.com/wearables/oura, andmyapp://returns tomyapp:///wearables/oura.- Your app can handle that URL: a route in a web app, or a deep link in a native app.
The flow
- Ask for a consent link.
authorizationUrlis valid for 10 minutes and for one use. Ask for a new one each time the patient taps "Connect". - Open it in the system browser or an auth session:
ASWebAuthenticationSessionon iOS, Custom Tabs on Android,openAuthSessionAsyncfromexpo-web-browser, or a full-page redirect on the web. Do not use an embedded WebView: the patient should see the provider's own page in a real browser. - Ovok handles the callback. You never call it. It exchanges the code, stores the tokens encrypted and redirects to your app with
result:connected: the tokens are stored;denied: the patient refused, or the provider returned an error;failed: the code exchange or the save failed, an admin disabled the provider meanwhile, or Polar refused to register the patient because they have not accepted Polar's data-sharing consents. Nothing stays connected.
- Re-read the connections.
resultis for display only.GET /v1/wearables/connectionsis the state of record.
The first data arrives with the next daily sync at 08:00 UTC. To show data straight away, sync on demand after connected.
Connecting a provider the patient is already connected to replaces the stored tokens. This is also how a patient clears reconnectRequired.
For Withings, Ovok also subscribes the patient to Withings' notifications at connect time, so new measurements and nights arrive within minutes of reaching Withings.
Polar: consents first
Tell Polar users to accept Polar's mandatory data-sharing consents in their Polar account before connecting. Without them, Polar refuses to register the patient and the flow returns result=failed.
With @ovok/core
@ovok/core 0.4.46 and later has the provider and connection operations. Install or update it:
npm install @ovok/core@latestStart the flow from a client signed in as the patient:
const { authorizationUrl } = await client.authorizeWearableConnection('oura');
window.location.assign(authorizationUrl);On the return route, re-read the list:
const params = new URLSearchParams(window.location.search);
const result = params.get('result'); // 'connected' | 'denied' | 'failed', for display only
const { connections } = await client.listWearableConnections();Disconnect:
await client.deleteWearableConnection('oura');The SDK does not wrap the sync route yet. Call it through the same client:
await client.post('/v1/wearables/connections/oura/sync', undefined);SDK types lag the API
In @ovok/core 0.4.46, the WearableConnection type declares only connectedAt. The API also returns syncedThrough and reconnectRequired; read them from the response until the types catch up.
React hooks
@ovok/core also exports React hooks for the same operations: useWearableConnections(), useAuthorizeWearableConnection() and useDeleteWearableConnection() for the patient, and useWearableProviders(), useCreateWearableProvider(), useUpdateWearableProvider() and useDeleteWearableProvider() for admin screens.
import { useWearableConnections } from '@ovok/core';
const { data, reload } = useWearableConnections();
// After the app returns from the provider:
reload();A successful deleteWearableConnection() refreshes mounted useWearableConnections() hooks that use the same client.
React Native SDK
@ovok/native has no wearable connect flow yet. In a React Native or Expo app, use the @ovok/core client as above and open authorizationUrl with expo-web-browser, with your PATIENT_APP_URL deep link as the return URL:
import * as WebBrowser from 'expo-web-browser';
const { authorizationUrl } = await client.authorizeWearableConnection('oura');
await WebBrowser.openAuthSessionAsync(authorizationUrl, 'myapp:///wearables/oura');
const { connections } = await client.listWearableConnections();With plain HTTP
export OVOK_TOKEN='<access token of a patient>'Start the flow:
curl --request POST \
--url 'https://api.sandbox.ovok.com/v1/wearables/connections/oura/authorize' \
--header "Authorization: Bearer ${OVOK_TOKEN}"{
"provider": "oura",
"authorizationUrl": "https://cloud.ouraring.com/oauth/authorize?response_type=code&client_id=…&redirect_uri=…&state=…&scope=…"
}Read the connections:
curl --request GET \
--url 'https://api.sandbox.ovok.com/v1/wearables/connections' \
--header "Authorization: Bearer ${OVOK_TOKEN}"{
"connections": [
{
"provider": "oura",
"connection": {
"connectedAt": "2026-10-21T09:00:00.000Z",
"syncedThrough": "2026-10-22T08:00:00.000Z",
"reconnectRequired": false
}
},
{ "provider": "whoop", "connection": null }
]
}Disconnect:
curl --request DELETE \
--url 'https://api.sandbox.ovok.com/v1/wearables/connections/oura' \
--header "Authorization: Bearer ${OVOK_TOKEN}"The answer is 204. Where the provider allows it, Ovok first undoes what it set up there: Withings stops notifying Ovok about the patient, and Polar deregisters the patient, which also revokes your app's access. Other providers keep your app's access: tell the patient they can remove it in their provider account. A failure at the provider never stops the disconnect.
Build the screen from the list
- Render the providers from
connections, not from a hard-coded list. It holds every provider the project has enabled, plus any provider the patient is still connected to after an admin disabled it. connection: null: offer Connect.reconnectRequired: true: the provider no longer accepts the connection, usually because the patient removed your app's access at the provider. Offer Reconnect, which is the same authorize call.syncedThrough: null: connected, not synced yet.
Errors
| Status | Code | What to do |
|---|---|---|
403 | PATIENT_PROFILE_REQUIRED | Sign in as the patient. Admins and practitioners cannot connect on a patient's behalf. |
404 | PROVIDER_NOT_AVAILABLE | The project has not set up the provider or disabled it. Hide it; re-read the list. |
404 | CONNECTION_NOT_FOUND | On disconnect: the patient is not connected to this provider. Re-read the list. |
409 | APP_URL_NOT_CONFIGURED | The project has no usable PATIENT_APP_URL. Show it as a setup problem and tell the project admin. |
422 | INVALID_STATE | On the callback: the link expired (10 minutes), was already used, or belongs to another provider. Start again from Connect. |
501 | FEATURE_NOT_CONFIGURED | Wearables are not set up on this Ovok server. Contact Ovok support. |
503 | STATE_STORE_UNAVAILABLE | Ovok could not store or read the pending authorization. Retry after Retry-After. |
Last updated on
Set up providers
Register an OAuth app at Oura, WHOOP, Strava, Withings, Polar, Suunto or Ultrahuman, then save it in the Ovok Console or through the API so your patients can connect.
Sync and read the data
When wearable data reaches your project, what each provider syncs, what the Observations look like, and how to query them apart from device readings.