Connect a device
Link one of your users to their watch, without holding a provider client secret or implementing a callback.
Your user has a Garmin watch and you want their workouts. Built yourself, that is a partner application, weeks of approval, a client secret, a PKCE verifier and an OAuth callback — per provider, each with its own quirks.
Here it is three calls, and none of them involve a provider credential. The registration is ours: one client per provider, one callback URL, one consent screen. What you get back is your user, linked.
Who's who
Three parties, and keeping them apart is most of understanding this page.
| You | The developer. Your Stridee account, your keys, your webhook endpoints. |
| Your user | An athlete who signed up for your product. They have no Stridee account and will never be asked for one. |
| The connection | One of your users' authorization at one provider. |
You refer to your users by your own id — whatever your database already calls them.
That is the only identifier you send and the only one you have to store. We mint a user_id
and hand it back, but nothing breaks if you throw it away: it is on every connection you
list, and GET /v1/accounts looks it up from your id.
1. Register a return URI
Where your user's browser should land once they have finished consenting. Add it under
Return URIs in the console before your first connect call.
It is matched as a prefix, so register the stem once and vary the rest per user:
https://app.yourapp.com/settings/devicesHTTPS only, no wildcards, and it has to name a host — we won't send your users to a URL you haven't claimed. Skip this step and your users finish on a plain page we host that says the connection worked: fine for a first try, not for production.
2. Start a link
POST /v1/connect HTTP/1.1
Host: api.stridee.com
Content-Type: application/json
{
"provider": "coros",
"external_user_id": "user_4821",
"return_uri": "https://app.yourapp.com/settings/devices"
}{
"connect_url": "https://api.stridee.com/connections/start?state=…",
"user_id": "4c9a7e15-6d3b-42f8-91c0-8e5b2a7d04f6",
"expires_at": "2026-08-05T11:44:02Z"
}Redirect your user's browser to connect_url. It is a screen a person has to read — don't
fetch it server-side, and don't cache it. It stops working after 30 minutes, matching how
long the provider's own authorization code stays good.
connect_url is a page we host, showing your logo beside ours and carrying our terms; one
click later your user is at the provider. That click is required and there is no setting to
skip it: your user is about to send their heart rate, sleep and GPS traces through
infrastructure they have never heard of, and every provider agreement behind this API
assumes they were told who receives it. Set the name and logo they see under
Branding — it previews the real page as you type.
Calling this twice for the same external_user_id returns the same user_id rather than a
second identity, so retrying is safe. provider is any of the seven on
Provider support & limits; anything else is a 400. Everything below is
the same whichever you name — the differences in how each provider handles tokens and
consent are ours to carry.
3. Your user comes back
They approve, and we send them to your return_uri with two parameters appended:
https://app.yourapp.com/settings/devices?status=success&user_id=4c9a7e15-6d3b-42f8-91c0-8e5b2a7d04f6status | |
|---|---|
success | Connected. The tokens are stored and data will start arriving. |
denied | They declined at the provider. Nothing was linked and nothing was shared. |
error | Something went wrong on our side or the provider's. Nothing was saved. |
Handle all three — denied is a normal outcome, not a failure.
Don't treat this redirect as the source of truth. It tells you where to send the browser
next; the account.connected event is what tells your backend the connection exists. A
user who closes the tab mid-redirect still connected.
4. Get the event
With stridee listen running, this arrives on your local handler the
moment they finish:
{
"id": "9f2c1e7a-4b83-4d21-9a6e-3c5f0d8b71a4",
"type": "account.connected",
"created": "2026-08-05T11:16:44Z",
"webhook_id": "2d7b45c1-8e0a-4f36-b512-9c7d3a6e04f8",
"nonce": "Kd4nWpLbEa9xTvRm2Cj7Lz0Bq2vNhCz7",
"user_id": "4c9a7e15-6d3b-42f8-91c0-8e5b2a7d04f6",
"provider": "coros",
"data": {
"connection_id": "a2f81b60-3c47-49d5-b8e2-70a4c9f13d85"
}
}user_id is the value /v1/connect returned, which is how you map it back to your own
user. From then on their workouts arrive as activity.created — see
Events for the shape and
Getting the file for the recording.
That is the whole path. Users & connections covers what comes after: listing, disconnecting and forgetting users.
Something wrong or missing on this page? Tell us in Discord. Need something the API doesn’t do yet? Request it on the roadmap.