Receive a signed wearable provider notification
- Sandbox: available
- Staging: available
- Production: unknown
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
webhookUrlfromGET /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
outcomeno_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 (
appli4), 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 nowebhookUrland every call answers401. The body is form fieldsuserid,appli,startdateandenddate; ovok readsuserid. - Polar: ovok creates the webhook when the project saves its Polar credentials switched on, and deletes it when the project removes them;
webhookUrlis for information only. Polar signs each notification withPolar-Webhook-Signature, an HMAC-SHA256 of the body under the webhook's signing key, which ovok keeps encrypted with the credentials. Notifications forSLEEPandACTIVITY_SUMMARYqueue a sync. - Suunto: in the Suunto developer portal, open your app's settings, paste
webhookUrlas its notification URL and set a notification secret. Give ovok the same secret aswebhookSecretwithPOSTorPATCH /v1/wearables/providers. Suunto signs each notification withX-HMAC-SHA256-Signature, the HMAC-SHA256 of the body under that secret, and names the account inusername. Until the secret is stored, every Suunto notification answers401. - Ultrahuman: sends no notifications; its data is synced daily and on demand.
projectId*stringThe project whose app at the provider sends the notification.
uuidprovider*string"oura""whoop""strava""withings""polar""suunto""garmin""ultrahuman"Queued: a sync of each connection to the named accounts runs in about 30 seconds. A setup call (Polar's PING) answers 200 with handshake.
application/json- response
outcome*stringqueued: a sync of the named accounts is queued. no_connection: no patient here is connected to the named accounts, nothing queued. handshake: a setup call, nothing queued.
"queued""no_connection""handshake"curl -X POST 'https://api.sandbox.ovok.com/v1/wearables/webhooks/497f6eca-6276-4993-bfeb-53cbbbba6f08/oura'{ "outcome": "queued"}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.
Check a wearable webhook URL HEAD
Answers whether a webhook URL is live, for a provider that checks it before it sends notifications (Withings does). You never call it yourself. **Auth:** none. A token at the end of the URL is not checked here. **Scope:** the project in the path. ## Answers - `200` with no body when the project has set up and switched on the provider, and ovok takes notifications from it. - Otherwise the same `404` and `422` answers as `POST` on the same URL.