One API for every wearable.Read and write.
Garmin, COROS, Polar, Wahoo, Apple Watch and Strava — normalized into one FIT file, so you interpret one format instead of six APIs. Then push structured workouts straight back to the athlete’s watch.
Private beta opens September 1, 2026. Your $20 converts into month one — you pay early, not extra. Only 50 slots. Or walk through the console first.
link one of your users — no provider credential involved
// Your own id for your own user. No Stridee account needed.
const link = await stridee.post('/v1/connect', {
provider: 'coros',
external_user_id: 'user_4821',
return_uri: 'https://app.yourapp.com/settings/devices',
});
// Redirect their browser. Good for 30 minutes.
res.redirect(link.connect_url);Your users can connect these today

Coverage
What works, and what doesn’t yet
Coverage differs because provider APIs differ. Where a provider doesn’t send something, the field is absent rather than estimated — a null you can branch on is worth more than a number nobody measured. Nothing below is marked live unless an endpoint serves it today.
| Provider | Connect | Activities | Daily summaries | Workout push |
|---|---|---|---|---|
COROSPACE, APEX, VERTIX | ||||
StravaHistory import |
Wahoo is the one provider that reports what the athlete actually granted, so a connection’s scope comes back verbatim. COROS and Polar don’t, and we leave the field null rather than filling it with the scopes we requested.
Apple Watch and Strava aren’t connectable through the API yet — HealthKit is on-device by design, and Strava history import runs in the Stridee apps rather than behind /v1/connect. Naming either in a connect call is a 400 today, and both are on the roadmap below.
Next on the roadmap
Not shipped yet. The founding 50 decide the order — say which one you need and it moves up.
Connections
Three calls, and none of them is an OAuth flow you built.
The version you’d build yourself starts with a partner application, waits weeks for approval, and ends with a client secret in your environment, a PKCE verifier parked somewhere both halves of the flow can reach, and a callback in your router — repeated per provider, each with its own quirks. The registration here is ours: one client per provider, one callback, one consent screen we host.
Register where they come back to
One HTTPS return URI in the console, matched as a prefix. We won’t send your users to a URL you haven’t claimed — an endpoint that redirects a browser wherever a caller says is a phishing link wearing our domain.
Send us your id for them
Whatever your database already calls them. You get back a URL to redirect their browser to, and a user_id you can keep or throw away. Call it twice with the same id and you get the same person back, which is what makes retrying safe.
Their activities start arriving
account.connected tells your backend the link exists — independently of the redirect, so a user who closes the tab mid-flow still connected. Every finished workout after that lands as activity.created.
The differences stay on our side
One provider issues a refresh token, one rotates both every two hours, one issues neither and never expires, one requires PKCE and reports what the athlete granted through a separate call that can change afterwards. None of that reaches your integration — and we never hand you a provider’s own id for an athlete, so you and another developer serving the same person can’t join your user tables against each other.
POST /v1/connect
POST /v1/connect HTTP/1.1
Host: api.stridee.fit
Content-Type: application/json
{
"provider": "coros",
"external_user_id": "user_4821",
"return_uri": "https://app.yourapp.com/settings/devices"
}Credentials
Nothing here is a secret you have to keep.
No API key on the way in, no shared webhook secret on the way back. Both directions are keypairs: you sign requests with a private key that never leaves your machine, and we seal deliveries to a public key whose private half never reaches us.
There is no API key
Requests carry an Ed25519 signature over the request itself — RFC 9421 HTTP Message Signatures — made with a private key that never leaves your machine. A bearer token is a thing that can rest in a log line, a proxy, a backup or a CI variable. A signature never travels, so there is nothing in any of those places to steal.
Every delivery is sealed to your key
JWE compact, ECDH-ES over X25519 with A256GCM. You hold the private half; we hold the public one. HTTPS protects a body as far as whatever terminates TLS — usually a CDN or a logging sidecar — and past that hop the plaintext exists in exactly two places, ours and yours. There is no cleartext mode and no shared secret to rotate.
And signed with ours
Encryption answers who may read a delivery. It cannot answer who sent it, because sealing a body to a public key is what a public key lets anyone do. So every delivery also carries a detached JWS you verify against the JWK Set we publish — fetch it, don’t paste it, and our rotation never becomes your redeploy.
Echo the nonce back
One line, and it buys what a status code cannot. A fresh nonce rides inside each ciphertext, and only something holding your private key can return it. A lapsed domain, an undeployed key, a proxy answering in front of your service — all of them return a healthy 200, and all of them stop echoing.
And you can prove it before a user exists
Send ping mints a real event, seals it to your key and POSTs it to your URL — the same path a real delivery takes, so a green one means something. It reports two answers, not one: that your endpoint replied, and that it could actually open the body.
RFC 9421 — there is no API key to send
POST /v1/connect HTTP/1.1
Host: api.stridee.fit
Content-Digest: sha-256=:zeuXewdQlhgzXOqle0t2/j7Jxy9QEC903PurkiKOxvs=:
Signature-Input: sig1=("@method" "@target-uri" "content-digest");created=1770124811;keyid="5a8f31d6-0c94-4b27-a3e5-71fd2809bc4e";nonce="g1UIt9b3k_FkSpsX2KVCGA";alg="ed25519"
Signature: sig1=:wqcAqbmYJ2ji2glfAMaRy4gruYYnx2nEFN2HN6jrnDnQ…:
# The key never travels. There is nothing in a log to steal.Claim your slot for $20.
A deposit, but not one you lose. Your $20 is charged today, holds one of only fifty beta slots, and converts into your first month when the private beta opens on September 1, 2026. You are paying early, not paying extra.
- Your $20 converts into month one — you pay early, not extra
- One of only 50 beta slots, reserved by name
- $20/month for the whole API — every provider, every endpoint
- Production keys on September 1, 2026, when the private beta opens
- Every provider we support, and the ones we add next
- A private Discord channel with the engineers who wrote the integrations
If we never ship, or you change your mind before we hand you keys, email hi@stridee.fit and we refund you. No forms, no conditions.
Founding slots
50
Only 50 founding slots exist. When they are gone, they are gone.
Private beta opens September 1, 2026
Charged today. $20/month for the whole API.
Claim my slotStripe checkout · converts to month one · refundable until we hand you keys
Not ready to claim?
Come talk to us in Discord first. We answer questions there before anywhere else.
Join the DiscordBefore you claim
Your first month, paid early. It is a deposit, charged today, but not one you lose — it reserves one of fifty beta slots and converts into your first month when the private beta opens on September 1, 2026. You pay early, not extra. If we never ship, or you change your mind before you get keys, email us and we refund you.
$20 a month, flat, for the whole API — every provider, every endpoint. Flat rather than priced per connected user, and no contract.
The private beta opens on September 1, 2026, and that is when founding partners get production keys. Your $20 is charged when you claim your slot and converts into your first month.
Garmin, COROS, Polar and Wahoo. Those are the four a POST /v1/connect call can name — anything else comes back as a 400, because the path exists and the argument does not. Apple Watch and Strava carry wordmarks across the product and sync inside the Stridee apps, but they have no connect path behind the API yet; both are on the roadmap, and founding partners decide the order.
No, and that is deliberate. Requests are authenticated with an Ed25519 signature over the request itself — RFC 9421 HTTP Message Signatures — using a key that never leaves your machine. Every request names your key id, covers the method, the URL and a digest of the body, and carries a fresh nonce we remember for five minutes so a captured request cannot be replayed. GET /v1/whoami exists to prove your client signs correctly before you call anything real.
An activity.created event carrying the provider’s own summary — sport, start time, device and the provider’s activity id — sealed to your key. The FIT file is not in it: it is a pre-signed provider URL that expires, and re-hosting your athletes’ activity files is a storage and retention decision rather than something to slip into a webhook payload. There is no canonical re-encoded FIT today, and no laps or per-second stream endpoint yet.
Not yet. workout.pushed and workout.completed are designed and documented, and nothing emits them today — there is no workouts endpoint in the shipped API. It is the thing we most want to get right, and the founding fifty are who we are building it with. Nothing on this page counts it as live.
Four things, in this order, and each is cheaper than the one after it. Verify the detached JWS against our published JWKS before you parse anything. Check the delivery is recent and that you have not seen its webhook-id before — retries reuse it. Decrypt the JWE with your private key and check the user_id inside is yours, since a webhook URL is a public endpoint and anyone can seal a body to a public key. Then return a 2xx carrying the nonce you just decrypted. Decrypt in the request path, before you ack — you cannot enqueue the raw bytes and return 200 blind.
Any 2xx is an ack. Non-2xx responses and timeouts are retried with exponential backoff, and sustained failures mark the endpoint degraded and then failing in the console — deliveries are queued while you fix it, not dropped. Every event is kept for 30 days, and each delivery records the key it was sealed to, so a replay after a rotation is still decryptable with the key it was actually encrypted to.
The full documentation is public and needs no account. Still have questions? Ask us in Discord
