Wearables API reference

Every cloud wearables route in full, with its caller, parameters, request, answer, status codes, side effects and per-provider behaviour.

Every cloud wearables route, in full. Examples use the sandbox host, https://api.sandbox.ovok.com.

Routes

MethodPathCallerWhat it does
GET/v1/wearables/providersProject adminLists every provider and the project's setup of it.
GET/v1/wearables/providers/:providerProject adminReads one provider's setup.
POST/v1/wearables/providersProject adminSaves the project's OAuth app at a provider.
PATCH/v1/wearables/providers/:providerProject adminChanges a saved app, or switches it on or off.
DELETE/v1/wearables/providers/:providerProject adminRemoves a saved app.
GET/v1/wearables/connectionsPatientLists the patient's connections.
POST/v1/wearables/connections/:provider/authorizePatientReturns the provider's consent page.
POST/v1/wearables/connections/:provider/syncPatientPulls the patient's latest data now.
DELETE/v1/wearables/connections/:providerPatientDisconnects the patient.
GET/v1/wearables/oauth/:provider/callbackThe provider, through the patient's browserFinishes a connection and returns to the patient app.
POST/v1/wearables/webhooks/:projectId/:provider[/:token]The providerTakes a new-data notification and queues a sync.
HEAD/v1/wearables/webhooks/:projectId/:provider[/:token]The providerAnswers whether the webhook URL is live.

Conventions

Authentication. Admin and patient routes take a bearer token: Authorization: Bearer <access token>. The project, and for patient routes the Patient, come from the token; no route takes a project or patient parameter. The OAuth callback and the webhooks take no token.

export OVOK_TOKEN='<access token>'

:provider is one of oura, whoop, strava, withings, polar, suunto, garmin, ultrahuman. Any other value answers 422 VALIDATION_FAILED.

Errors are Problem Details (application/problem+json). Branch on type and status, never on detail, and keep traceId when you report a problem:

{
  "type": "https://docs.ovok.com/errors/provider-not-available",
  "status": 404,
  "detail": "oura is not set up or not enabled for this project.",
  "instance": "/v1/wearables/connections/oura/authorize",
  "traceId": "877c7c38-e426-4c3d-bac0-0c4725a9df73",
  "errors": []
}

Secrets (secret, subscriptionKey, webhookSecret, provider tokens) are encrypted before they are stored and never returned by any route. No wearables route sends email.

Provider setup

These routes manage the project's OAuth app at each provider. The caller needs a project admin token, and must also be a Practitioner, an admin, or hold the System Owner access policy. Reading is admin-only too, because the answer names the project's client IDs. Every write is logged with the acting user, the project and the provider.

The provider object

GET, POST and PATCH answer with this object; GET /v1/wearables/providers with a list of them.

FieldTypeMeaning
providerstringThe provider.
configuredbooleanThe project holds credentials for it that this server can read. Credentials copied from another project read as not configured.
enabledbooleanConfigured and switched on. Only then can patients connect it.
clientIdstring or nullThe OAuth client ID, or null when not configured.
redirectUristring or nullThe callback URL to register at the provider. The same for every project on the server; null when the server has no public base URL.
webhookUrlstring or nullWhere the provider sends notifications of new data, unique to the project. Paste it in Suunto's portal; Ovok registers Withings' and Polar's itself. Withings' ends in a secret token: keep it private. null when the provider sends no notifications (Oura, WHOOP, Strava, Garmin, Ultrahuman), is not set up, or the server has no public base URL.
webhookReadyboolean or nulltrue when Ovok holds the secret that checks the provider's notifications: Polar's signing key from its webhook setup, the secret Ovok makes for Withings, or Suunto's webhookSecret. false until then. null when the provider sends no notifications.

List providers

GET /v1/wearables/providers

Returns every supported provider, set up or not, always in this order: oura, whoop, strava, withings, polar, suunto, garmin, ultrahuman. Use it to find the callback URL to register and to check webhookReady.

Auth: project admin. Parameters: none.

curl --request GET \
  --url 'https://api.sandbox.ovok.com/v1/wearables/providers' \
  --header "Authorization: Bearer ${OVOK_TOKEN}"

200, shortened to three of the eight entries:

{
  "providers": [
    {
      "provider": "oura",
      "configured": true,
      "enabled": true,
      "clientId": "<client id>",
      "redirectUri": "https://api.sandbox.ovok.com/v1/wearables/oauth/oura/callback",
      "webhookUrl": null,
      "webhookReady": null
    },
    {
      "provider": "polar",
      "configured": true,
      "enabled": true,
      "clientId": "<client id>",
      "redirectUri": "https://api.sandbox.ovok.com/v1/wearables/oauth/polar/callback",
      "webhookUrl": "https://api.sandbox.ovok.com/v1/wearables/webhooks/<project id>/polar",
      "webhookReady": true
    },
    {
      "provider": "suunto",
      "configured": false,
      "enabled": false,
      "clientId": null,
      "redirectUri": "https://api.sandbox.ovok.com/v1/wearables/oauth/suunto/callback",
      "webhookUrl": null,
      "webhookReady": false
    }
  ]
}

Status codes

StatusCodeWhat to do
200The list.
401The token is missing or invalid. Sign in again.
403PROJECT_CONTEXT_REQUIREDThe session has no project. Sign in to the project.
403The caller is not a project admin.

Side effects: none.

Get a provider

GET /v1/wearables/providers/:provider

Returns one provider object, set up or not.

Auth: project admin.

Path parameterTypeRequiredMeaning
providerstringYesThe provider.
curl --request GET \
  --url 'https://api.sandbox.ovok.com/v1/wearables/providers/withings' \
  --header "Authorization: Bearer ${OVOK_TOKEN}"

200:

{
  "provider": "withings",
  "configured": true,
  "enabled": true,
  "clientId": "<client id>",
  "redirectUri": "https://api.sandbox.ovok.com/v1/wearables/oauth/withings/callback",
  "webhookUrl": "https://api.sandbox.ovok.com/v1/wearables/webhooks/<project id>/withings/<token>",
  "webhookReady": true
}

Status codes

StatusCodeWhat to do
200The provider. A provider not set up answers 200 with configured: false.
401The token is missing or invalid. Sign in again.
403PROJECT_CONTEXT_REQUIREDThe session has no project.
403The caller is not a project admin.
422provider is not supported.

Side effects: none.

Set up a provider

POST /v1/wearables/providers

Saves the project's OAuth app at a provider, so the project's patients can connect it. Register the app at the provider first, with the provider's redirectUri from List providers. Set up providers has a quickstart per provider.

Auth: project admin.

Body fieldTypeRequiredMeaning
providerstringYesThe provider.
clientIdstring, 1–500 charactersYesThe app's OAuth client ID.
secretstring, 1–2000 charactersYesThe app's OAuth client secret. Never returned.
subscriptionKeystring or null, 1–2000 charactersSuunto onlySuunto's API-management subscription key. Required for Suunto, refused for every other provider. Default null. Never returned.
webhookSecretstring or null, 1–2000 charactersNo; Suunto onlyThe notification secret set beside the notification URL in Suunto's portal. Refused for every other provider. Default null. Never returned.
enabledbooleanNoWhether patients may connect it. Default true.

Unknown fields are refused.

curl --request POST \
  --url 'https://api.sandbox.ovok.com/v1/wearables/providers' \
  --header "Authorization: Bearer ${OVOK_TOKEN}" \
  --header 'Content-Type: application/json' \
  --data '{"provider":"oura","clientId":"<client id>","secret":"<client secret>"}'

Suunto, with its subscription key and notification secret:

curl --request POST \
  --url 'https://api.sandbox.ovok.com/v1/wearables/providers' \
  --header "Authorization: Bearer ${OVOK_TOKEN}" \
  --header 'Content-Type: application/json' \
  --data '{"provider":"suunto","clientId":"<client id>","secret":"<client secret>","subscriptionKey":"<subscription key>","webhookSecret":"<notification secret>"}'

201, with Location: /v1/wearables/providers/oura:

{
  "provider": "oura",
  "configured": true,
  "enabled": true,
  "clientId": "<client id>",
  "redirectUri": "https://api.sandbox.ovok.com/v1/wearables/oauth/oura/callback",
  "webhookUrl": null,
  "webhookReady": null
}

Per provider

ProviderWhat differs
SuuntosubscriptionKey is required. webhookSecret is optional: without it, webhookReady is false, Suunto's notifications answer 401, and Suunto syncs daily and on demand only.
WithingsOvok makes a random secret for the project's webhook URL token, stored encrypted with the credentials. The client ID of Ovok's legacy Withings device integration is refused.
PolarWith enabled: true, Ovok then creates the client's webhook at Polar for SLEEP and ACTIVITY_SUMMARY events, pointing at webhookUrl, and stores its signing key. Polar first sends the URL a PING, which Ovok answers. If the client already has a webhook for this project, Ovok points it at webhookUrl and switches it on, replacing it when Ovok lacks its signing key. A webhook pointing at another project, or anywhere else, is left alone.
OthersNo extra fields or provider calls.

Status codes

StatusCodeWhat to do
201Stored. Location names the provider. For Polar, check webhookReady: false means the webhook setup failed or belongs to another project; saving again retries.
401The token is missing or invalid. Sign in again.
403PROJECT_CONTEXT_REQUIREDThe session has no project.
403The caller is not a project admin.
409PROVIDER_CONFIGUREDThe provider is already set up. Change it with PATCH.
409Someone changed the project while this request wrote; nothing was stored. Read the provider again and retry.
422SUBSCRIPTION_KEY_REQUIREDSuunto needs subscriptionKey.
422SUBSCRIPTION_KEY_NOT_USEDOnly Suunto takes subscriptionKey. Remove it.
422WEBHOOK_SECRET_NOT_USEDOnly Suunto takes webhookSecret. Remove it.
422CLIENT_ID_RESERVEDThe Withings client ID belongs to Ovok's legacy device integration. Register an app of your own.
422A field is missing, empty, too long or unknown. errors names it.
500The credentials could not be confirmed as stored. Read the provider, then retry.
501FEATURE_NOT_CONFIGUREDThis server cannot encrypt secrets, so nothing was stored. Contact Ovok support.

Side effects: stores the encrypted credentials on the project and logs the change; for Withings, makes the webhook secret; for Polar, creates or updates the webhook at Polar. A failed Polar webhook setup never undoes the stored credentials.

Change a provider

PATCH /v1/wearables/providers/:provider

Changes a provider already set up. Fields you leave out keep their value. Send at least one.

Auth: project admin.

Path parameterTypeRequiredMeaning
providerstringYesThe provider.
Body fieldTypeRequiredMeaning
clientIdstring, 1–500 charactersNoA new OAuth client ID.
secretstring, 1–2000 charactersNoA new client secret.
subscriptionKeystring, 1–2000 charactersNo; Suunto onlyA new subscription key.
webhookSecretstring, 1–2000 charactersNo; Suunto onlyA new notification secret. Change it here when you change it in Suunto's portal.
enabledbooleanNofalse stops new connections and the sync of existing ones; connected patients stay connected and can disconnect. true switches it back on.
curl --request PATCH \
  --url 'https://api.sandbox.ovok.com/v1/wearables/providers/oura' \
  --header "Authorization: Bearer ${OVOK_TOKEN}" \
  --header 'Content-Type: application/json' \
  --data '{"secret":"<new client secret>"}'

200 with the provider object after the change.

Per provider

ProviderWhat differs
WithingsChanging secret keeps webhookUrl. Changing clientId makes a new webhook secret, so a new webhookUrl; patients connect again to the new app anyway. The legacy device integration's client ID is refused.
SuuntoChanging clientId keeps the stored webhookSecret. Send a new one in the same request if the new app signs with another secret.
PolarChanging clientId drops the webhook's signing key, which belonged to the old client; it gets a new one when the webhook is set up again. Every change with enabled: true, this one included, sets the webhook up again, as POST does. enabled: false leaves the webhook at Polar; its notifications answer 404 until Polar is switched back on.

Status codes

StatusCodeWhat to do
200Changed.
401The token is missing or invalid. Sign in again.
403PROJECT_CONTEXT_REQUIREDThe session has no project.
403The caller is not a project admin.
404PROVIDER_NOT_CONFIGUREDThe provider is not set up. Set it up with POST.
409Someone changed the project while this request wrote; nothing was stored. Read the provider again and retry.
422SUBSCRIPTION_KEY_NOT_USEDOnly Suunto takes subscriptionKey.
422WEBHOOK_SECRET_NOT_USEDOnly Suunto takes webhookSecret.
422CLIENT_ID_RESERVEDThe Withings client ID belongs to Ovok's legacy device integration.
422The body is empty, a field is invalid, or a field is unknown.
500The change could not be confirmed as stored. Read the provider, then retry.
501FEATURE_NOT_CONFIGUREDThis server cannot encrypt secrets, so nothing was stored.

Side effects: stores the encrypted credentials and logs the change; for Polar with enabled: true, updates the webhook at Polar.

Remove a provider

DELETE /v1/wearables/providers/:provider

Removes the project's credentials. Patients can no longer connect the provider, and existing connections stop syncing; they stay listed until the patient disconnects.

Auth: project admin.

Path parameterTypeRequiredMeaning
providerstringYesThe provider.
curl --request DELETE \
  --url 'https://api.sandbox.ovok.com/v1/wearables/providers/oura' \
  --header "Authorization: Bearer ${OVOK_TOKEN}"

204 with no body.

Status codes

StatusCodeWhat to do
204Removed.
401The token is missing or invalid. Sign in again.
403PROJECT_CONTEXT_REQUIREDThe session has no project.
403The caller is not a project admin.
404PROVIDER_NOT_CONFIGUREDThe provider is not set up; nothing to remove.
409Someone changed the project while this request wrote. Read it again and retry.
422provider is not supported.

Side effects: deletes the stored credentials and logs the change; for Polar, deletes the client's webhook at Polar when it points at this project's webhookUrl. A failure at Polar never stops the removal. Patients' grants at the provider are not revoked.

Patient connections

These routes act on the signed-in patient's own connections. Any session that is not a patient's answers 403 PATIENT_PROFILE_REQUIRED: an admin cannot connect, sync or disconnect on a patient's behalf.

List connections

GET /v1/wearables/connections

Returns one entry per provider the project has enabled, plus any provider the patient is still connected to after the project disabled or removed it. Render your connect screen from it.

Auth: patient. Parameters: none.

curl --request GET \
  --url 'https://api.sandbox.ovok.com/v1/wearables/connections' \
  --header "Authorization: Bearer ${OVOK_TOKEN}"

200:

{
  "connections": [
    {
      "provider": "oura",
      "connection": {
        "connectedAt": "2026-10-21T09:00:00.000Z",
        "syncedThrough": "2026-10-22T08:00:00.000Z",
        "reconnectRequired": false
      }
    },
    { "provider": "withings", "connection": null }
  ]
}
FieldTypeMeaning
providerstringThe provider.
connectionobject or nullnull when the patient has not connected it.
connection.connectedAtdate-timeWhen the patient last connected it.
connection.syncedThroughdate-time or nullUp to when its data is synced; null before the first sync.
connection.reconnectRequiredbooleantrue when the provider no longer accepts the connection. Connecting again clears it.

Status codes

StatusCodeWhat to do
200The connections.
401The token is missing or invalid. Sign in again.
403PATIENT_PROFILE_REQUIREDNot a patient session.
403PROJECT_CONTEXT_REQUIREDThe session has no project.

Side effects: none.

Start a connection

POST /v1/wearables/connections/:provider/authorize

Returns the provider's consent page for the signed-in patient. Open it in the system browser or an auth session, never an embedded WebView. After consent the provider sends the browser to the OAuth callback, which ends at your patient app. Connect a patient walks through the flow.

Auth: patient. Body: none.

Path parameterTypeRequiredMeaning
providerstringYesThe provider to connect.
curl --request POST \
  --url 'https://api.sandbox.ovok.com/v1/wearables/connections/oura/authorize' \
  --header "Authorization: Bearer ${OVOK_TOKEN}"

200:

{
  "provider": "oura",
  "authorizationUrl": "https://cloud.ouraring.com/oauth/authorize?response_type=code&client_id=…&redirect_uri=…&state=…&scope=…"
}

authorizationUrl is valid for 10 minutes and one use; ask for a new one each time. The return address, from the project's PATIENT_APP_URL, is fixed when you call the route.

Per provider

ProviderWhat differs
StravaThe URL forces Strava's consent screen, so a forwarded link cannot connect silently.
GarminThe URL carries a PKCE challenge.
OthersThe provider's own scopes, listed in Set up providers.

Status codes

StatusCodeWhat to do
200Open authorizationUrl.
401The token is missing or invalid. Sign in again.
403PATIENT_PROFILE_REQUIREDNot a patient session.
403PROJECT_CONTEXT_REQUIREDThe session has no project.
404PROVIDER_NOT_AVAILABLEThe project has not set up the provider, or disabled it. Hide it and re-read the list.
409APP_URL_NOT_CONFIGUREDThe project has no usable PATIENT_APP_URL. A project admin sets it.
422provider is not supported.
501FEATURE_NOT_CONFIGUREDThis server has no public base URL for the callback. Contact Ovok support.
503STATE_STORE_UNAVAILABLEOvok could not store the pending authorization. Retry after Retry-After.

Side effects: stores a one-time authorization for 10 minutes. Nothing is sent to the provider yet.

Sync a connection

POST /v1/wearables/connections/:provider/sync

Pulls the patient's latest data from the provider now and writes it as Observations, instead of waiting for the daily sync at 08:00 UTC. Sync and read the data describes the readings.

Auth: patient. Body: none.

Path parameterTypeRequiredMeaning
providerstringYesThe connected provider.
curl --request POST \
  --url 'https://api.sandbox.ovok.com/v1/wearables/connections/oura/sync' \
  --header "Authorization: Bearer ${OVOK_TOKEN}"

200:

{ "provider": "oura", "created": 42, "syncedThrough": "2026-10-22T08:00:00.000Z" }
FieldTypeMeaning
providerstringThe provider.
createdintegerObservations this sync created. Readings already held are updated in place and not counted.
syncedThroughdate-timeUp to when the data is now synced.

What is read: the last 30 days on the first sync, then from 2 days before syncedThrough, never further back than 30 days. At most one on-demand sync per connection every 15 minutes, counted from syncedThrough, so also just after the daily sync.

Per provider

ProviderWhat differs
Oura, WHOOP, Strava, WithingsAs above.
PolarSleep and nightly recharge go back 28 days only. Polar's token cannot be refreshed: once Polar refuses it, the answer is 409 RECONNECT_REQUIRED.
UltrahumanAsked one day at a time, so a first sync makes 31 requests.
SuuntoDaily steps only.
GarminNot synced yet: 501 SYNC_NOT_SUPPORTED.

Status codes

StatusCodeWhat to do
200Synced.
401The token is missing or invalid. Sign in again.
403PATIENT_PROFILE_REQUIREDNot a patient session.
403PROJECT_CONTEXT_REQUIREDThe session has no project.
404CONNECTION_NOT_FOUNDThe patient is not connected to this provider, or disconnected during the sync. Re-read the list.
404PROVIDER_NOT_AVAILABLEThe project disabled or removed the provider.
409SYNC_IN_PROGRESSA sync of this connection is running, possibly the daily one. Re-read the list in a minute.
409RECONNECT_REQUIREDThe provider no longer accepts the connection. Ask the patient to connect again.
409CONNECTION_CHANGEDThe patient reconnected during the sync; nothing was written over the new connection. Sync again.
422provider is not supported.
429SYNC_TOO_SOONThe connection synced under 15 minutes ago. Retry after Retry-After seconds; the provider was not asked.
500SYNC_FAILEDThe sync failed on Ovok's side. Nothing moved; try again later.
501SYNC_NOT_SUPPORTEDOvok does not sync this provider yet. The connection is kept.
502PROVIDER_UNAVAILABLEThe provider failed, could not be reached, or answered something unreadable. Nothing moved; try again later.
503PROVIDER_RATE_LIMITEDThe provider is limiting requests. Retry after Retry-After seconds.

Side effects: calls the provider's data API; refreshes and re-stores the access token when it expires within 5 minutes or the provider refuses it; creates or updates Observations; moves syncedThrough only after the writes. When the provider refuses the grant, marks the connection reconnectRequired.

Disconnect

DELETE /v1/wearables/connections/:provider

Deletes the patient's connection. Its tokens are no longer readable through the API. Observations already written stay.

Auth: patient.

Path parameterTypeRequiredMeaning
providerstringYesThe provider to disconnect.
curl --request DELETE \
  --url 'https://api.sandbox.ovok.com/v1/wearables/connections/oura' \
  --header "Authorization: Bearer ${OVOK_TOKEN}"

204 with no body.

Per provider

ProviderWhat differs
WithingsOvok first revokes its notification subscriptions for the patient (appli 4, 16 and 44, on its own callback URL only).
PolarOvok first deregisters the patient at Polar, which also revokes your app's access.
OthersNothing is revoked at the provider. The patient removes your app's access in their provider account.

A failure at the provider never stops the disconnect.

Status codes

StatusCodeWhat to do
204Disconnected.
401The token is missing or invalid. Sign in again.
403PATIENT_PROFILE_REQUIREDNot a patient session.
403PROJECT_CONTEXT_REQUIREDThe session has no project.
404CONNECTION_NOT_FOUNDThe patient is not connected to this provider. Re-read the list.
422provider is not supported.

Side effects: calls Withings or Polar as above; deletes the connection. Its resource history keeps earlier versions, with the tokens still encrypted.

OAuth callback

GET /v1/wearables/oauth/:provider/callback

Where the provider sends the patient's browser after consent. Register it as the app's redirect URL at the provider; never call it yourself.

Auth: none. The one-time state from Start a connection identifies the patient and project.

ParameterInTypeRequiredMeaning
providerpathstringYesThe provider.
statequerystring, 1–200 charactersYesThe one-time state from the authorization URL.
codequerystring, up to 4000 charactersOn consentThe provider's authorization code.
errorquerystring, up to 200 charactersOn refusalThe provider's error, such as access_denied.

With a valid state, the answer is always 302 to <PATIENT_APP_URL>/wearables/<provider>?result=<result>:

HTTP/1.1 302 Found
Location: https://app.example.com/wearables/oura?result=connected
resultMeaning
connectedThe tokens are stored.
deniedThe patient refused, or the provider sent an error.
failedThe code exchange or the save failed, the project disabled the provider meanwhile, or Polar refused to register the patient. Nothing stays connected.

result is for display only; read List connections for the state of record.

Per provider

ProviderWhat differs
WithingsIts token answer is unwrapped from Withings' { status, body } envelope. After connecting, Ovok subscribes the patient to Withings' notifications. A failed subscription is logged and never fails the connect; the daily sync is the backstop.
PolarAfter the code exchange, Ovok registers the patient with Polar. A patient who has not accepted Polar's mandatory consents is refused: result=failed, nothing connected.
GarminThe code exchange sends the PKCE verifier.

Status codes

StatusCodeWhat to do
302To the patient app with result.
422INVALID_STATEThe state expired, was already used, is unknown, or was started for another provider. The patient starts again from your app.
422state is missing, a parameter is too long, or provider is not supported.
503STATE_STORE_UNAVAILABLEOvok could not read the pending authorization. The state is kept, so the same callback can be retried after Retry-After.

Side effects: spends the state; exchanges the code with the provider; stores the encrypted tokens and the provider's account id on the connection, replacing an earlier connection to the same provider; calls Withings or Polar as above.

Webhooks

Withings, Polar and Suunto tell Ovok when a patient has new data. Use the provider's webhookUrl from List providers; never call these routes yourself.

ProviderURLProofRegistered by
Withings…/webhooks/<project id>/withings/<token>The secret token at the end of the URLOvok, per patient at connect time
Polar…/webhooks/<project id>/polarPolar-Webhook-Signature: HMAC-SHA256 of the body under the webhook's signing keyOvok, when the project saves Polar
Suunto…/webhooks/<project id>/suuntoX-HMAC-SHA256-Signature: HMAC-SHA256 of the body under webhookSecretYou, in Suunto's portal

Oura, WHOOP, Strava, Ultrahuman and Garmin send no notifications to Ovok.

Receive a notification

POST /v1/wearables/webhooks/:projectId/:provider, or POST /v1/wearables/webhooks/:projectId/:provider/:token for Withings

Takes a provider's new-data notification and queues a sync of each connection it names. It never writes data itself.

Auth: none. The provider's signature, or the URL token for Withings, is the proof. A call without a signature header, or without a token of the right shape, is refused before anything is looked up.

ParameterInTypeRequiredMeaning
projectIdpathUUIDYesThe project whose app sends the notification.
providerpathstringYesThe provider.
tokenpathstring, up to 128 charactersWithings onlyThe secret token from Withings' webhookUrl.
bodybodyJSON object or form fields, at most 64 KBYesThe provider's own notification. Ovok reads only the provider's user ids from it.

What Withings sends, for illustration only:

curl --request POST \
  --url 'https://api.sandbox.ovok.com/v1/wearables/webhooks/<project id>/withings/<token>' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data 'userid=12345678&appli=44&startdate=1729551600&enddate=1729580400'

202:

{ "outcome": "queued" }
outcomeStatusMeaning
queued202A sync of each matching connection is queued, about 30 seconds later. Notifications about the same connection within one 30-second window share one sync.
no_connection202No patient in the project is connected to the named account. Nothing is queued. Accepted so the provider keeps the URL.
handshake200A setup call, such as Polar's PING when the webhook is created. Nothing is queued.

Per provider

ProviderWhat differs
WithingsUnsigned, so the URL ends with a token: base64url of an HMAC-SHA256 under the project's Withings webhook secret. The body is form fields userid, appli, startdate and enddate; Ovok reads userid. Changing the client secret keeps the URL; changing the client ID makes a new one.
PolarSigned with Polar-Webhook-Signature under the signing key Ovok stored at webhook setup. SLEEP and ACTIVITY_SUMMARY notifications queue a sync. PING answers 200 handshake without a signature check.
SuuntoSigned with X-HMAC-SHA256-Signature under the webhookSecret you saved. The account is username in the body. Until the secret is stored, every call answers 401.

Status codes

StatusCodeWhat it means
202Accepted: queued or no_connection.
200A handshake.
400MALFORMED_NOTIFICATIONThe body is neither a JSON object nor form fields, or does not parse.
401INVALID_SIGNATUREThe signature or URL token is missing or wrong. WWW-Authenticate is sent. For Suunto, check webhookSecret.
404PROJECT_NOT_FOUNDThere is no such project.
404PROVIDER_NOT_AVAILABLEThe project has not set up the provider, or disabled it.
404WEBHOOK_NOT_SUPPORTEDOvok takes no notifications from this provider.
413The body is larger than 64 KB.
422projectId is not a UUID, or provider is not supported.
429More than 300 calls a minute from one address. Retry-After is sent.
503SYNC_QUEUE_UNAVAILABLEThe sync could not be queued. Retry-After: 30; the provider retries, and the daily sync is the backstop.

Side effects: queues one delayed sync per matching connection. A queued sync that finds the connection already syncing is queued once more, 30 seconds later. The notification itself writes no data.

Check a webhook URL

HEAD /v1/wearables/webhooks/:projectId/:provider, or with /:token

Answers whether the project takes notifications from the provider, for providers that check a URL before using it (Withings does).

Auth: none. The token, when present, is not checked here.

Path parameterTypeRequiredMeaning
projectIdUUIDYesThe project.
providerstringYesThe provider.
tokenstring, up to 128 charactersNoIgnored.
curl --head 'https://api.sandbox.ovok.com/v1/wearables/webhooks/<project id>/suunto'

Status codes

StatusCodeWhat it means
200The project has set up and enabled the provider, and Ovok takes notifications from it. No body.
404PROJECT_NOT_FOUNDThere is no such project.
404PROVIDER_NOT_AVAILABLEThe project has not set up the provider, or disabled it.
404WEBHOOK_NOT_SUPPORTEDOvok takes no notifications from this provider.
422projectId is not a UUID, or provider is not supported.
429More than 300 calls a minute from one address.

Side effects: none.

Last updated on

On this page