Wearables

Wearable provider OAuth callback

  • Sandbox: available
  • Staging: available
  • Production: unknown
GET
/v1/wearables/oauth/{provider}/callback

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.

Path Parameters

provider*string
Value in"oura""whoop""strava""withings""polar""suunto""garmin""ultrahuman"

Query Parameters

state*string
Length1 <= length <= 200
code?string
Length1 <= length <= 4000
error?string
Lengthlength <= 200

Response Body

To the project's patient app at /wearables/<provider>?result=connected|denied|failed.

Example Request
curl -X GET 'https://api.sandbox.ovok.com/v1/wearables/oauth/oura/callback?state=string'
Example Response
Empty

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' ```

Receive a signed wearable provider notification POST

Where a wearable provider tells ovok that one of its users has new data. Register it in the provider's developer portal where the provider asks for one; you never call it yourself. **Auth:** none. The provider's signature over the body, or the secret token at the end of the URL, is the proof. **Scope:** the project in the path. ## Behaviour - The URL is the provider's `webhookUrl` from `GET /v1/wearables/providers`: `/v1/wearables/webhooks/<projectId>/<provider>`, followed by `/<token>` for a provider that does not sign its notifications. - The body is the provider's own, as JSON or as form fields, at most 64 KB. ovok reads only the provider's user ids from it. - A call without the provider's signature header, or without a URL token of the right shape, is refused before anything is looked up. - ovok writes no data here. For each patient connected to a named account, it queues one sync, which runs about 30 seconds later. Notifications about the same connection within one 30-second window join one sync; one arriving while that sync runs queues the next window's, so no notification is lost. - A sync that finds another sync of the same connection running is queued once more, 30 seconds later. - A notification about an account nobody in the project has connected is accepted too, with `outcome` `no_connection`: providers stop calling URLs that keep failing. ## Answers | Status | When | |---|---| | `202` | The notification is accepted; `outcome` is `queued`, or `no_connection` when no patient here is connected to the named accounts (nothing is queued). | | `200` | A setup call, such as Polar's `PING` when the webhook is created; `outcome` is `handshake`, nothing is queued. | | `400 MALFORMED_NOTIFICATION` | The body is neither a JSON object nor form fields, or does not parse. | | `401 INVALID_SIGNATURE` | The signature or the URL token is missing or does not match. `WWW-Authenticate` is sent. | | `404 PROJECT_NOT_FOUND` | There is no such project. | | `404 PROVIDER_NOT_AVAILABLE` | The project has not set up the provider, or has switched it off. | | `404 WEBHOOK_NOT_SUPPORTED` | ovok takes no notifications from this provider. | | `413` | The body is larger than 64 KB. | | `422` | The project id is not a UUID, or the provider is not one ovok supports. | | `429` | More than 300 calls a minute from one address. Retry after `Retry-After`. | | `503 SYNC_QUEUE_UNAVAILABLE` | The sync could not be queued. Retry after `Retry-After`. | ## Per provider - **Withings:** does not sign its notifications, so its URL ends with a token derived from a random secret ovok makes when the project saves its Withings credentials, keeps encrypted with them and never returns. ovok subscribes each patient at connect time with this URL, for measurements such as heart rate and SpO2 (`appli` 4), activity (16) and sleep (44); there is nothing to paste. Disconnecting revokes those three only. Changing the client secret keeps the URL; changing the client ID makes a new one. Until the secret exists, Withings has no `webhookUrl` and every call answers `401`. The body is form fields `userid`, `appli`, `startdate` and `enddate`; ovok reads `userid`. - **Polar:** ovok creates the webhook when the project saves its Polar credentials switched on, and deletes it when the project removes them; `webhookUrl` is for information only. Polar signs each notification with `Polar-Webhook-Signature`, an HMAC-SHA256 of the body under the webhook's signing key, which ovok keeps encrypted with the credentials. Notifications for `SLEEP` and `ACTIVITY_SUMMARY` queue a sync. - **Suunto:** in the Suunto developer portal, open your app's settings, paste `webhookUrl` as its notification URL and set a notification secret. Give ovok the same secret as `webhookSecret` with `POST` or `PATCH /v1/wearables/providers`. Suunto signs each notification with `X-HMAC-SHA256-Signature`, the HMAC-SHA256 of the body under that secret, and names the account in `username`. Until the secret is stored, every Suunto notification answers `401`. - **Ultrahuman:** sends no notifications; its data is synced daily and on demand.