All integrations

Wahoo API integration

ELEMNT head units, RIVAL watches and KICKR trainers, behind one connect call — and the one provider on the platform that tells you exactly what the athlete agreed to share.

From 32.50 € a month billed annually, no per-athlete fee, 30 days to change your mind.

link a wahoo athlete — no wahoo credential involved

// Your own id for your own user. You hold no wahoo client secret.
const link = await stridee.post('/v1/connect', {
  provider: 'wahoo',
  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);

A Wahoo workout finishes. An activity.created event carrying Wahoo’s own summary, sealed to your public key.

  • Sport as Wahoo classified it, mapped onto one vocabulary
  • Start time, duration and the Wahoo workout id
  • The device — ELEMNT BOLT, RIVAL, KICKR
  • The scopes the athlete actually granted, verbatim

The Wahoo integration you’re not writing. The registration, the consent screen and the token refresh are ours.

01

Register an application, host a callback

The Wahoo Cloud API is a normal OAuth2 integration, which sounds fine until it is the fourth normal OAuth2 integration you are operating. Each one is a client secret in your environment, a redirect URI to keep in sync across environments, and a callback route in your router that is security-sensitive and rarely tested.

02

Subscribe to the right events, then keep them subscribed

Wahoo pushes workouts to a webhook you own, which means a public endpoint, signature checks, idempotency against retries and a story for what happens while you are deploying. That work is identical for every provider and it is not any part of what your product does.

03

Notice that Wahoo answers a question the others don’t

Wahoo reports what the athlete actually granted, so we pass it through verbatim rather than echoing the scopes we requested. COROS and Polar do not, and there we return null. Reconciling that difference is exactly the class of per-provider judgement call that makes six integrations cost far more than six times one.

All of it is one POST /v1/connect naming wahoo, and a webhook handler you write once for every provider.

Connect a device

link a wahoo athlete — no wahoo credential involved

// Your own id for your own user. You hold no wahoo client secret.
const link = await stridee.post('/v1/connect', {
  provider: 'wahoo',
  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);

What Wahoo covers. Nothing is marked live unless an endpoint serves it today.

  • ConnectLive today
  • ActivitiesLive today
  • WellnessThe provider doesn’t expose it
  • Workout pushLive today

Every Wahoo device. The link is with the athlete’s account, not a watch, so models released after you ship reach you unchanged.

Cycling computers

  • ELEMNT BOLT
  • ELEMNT ROAM
  • ELEMNT ACE

Watches

  • RIVAL

Trainers

  • KICKR
  • KICKR CORE
  • KICKR BIKE

Sensors

  • TICKR heart rate
  • RPM cadence
Pricing

Two weeks free. Then from 32.50 € a month.

Checkout is the signup. No waitlist, no sales call.

  • Garmin, COROS, Polar, Wahoo, Zepp, Hammerhead and Fitbit behind one connect call
  • Every endpoint on every plan — never priced per athlete
  • 14 days free, and no card charged until they are up
  • 30-day money-back guarantee

Cancel any time from the Stripe billing portal, or email [email protected] within 30 days and we refund you.

Plans start at

32.50 €/month, billed annually

Or 39 €/month, month to month. Paying annually gets you two months free.

Hacker, Startup, Scale and Enterprise differ on one thing: how many of your users may connect a watch. Everything else is on every plan.

See the plans

14-day free trial · cancel any time

We create the account on the email you pay with, automatically. Sign in at platform.stridee.com/login. Nothing to wait for.

Wahoo API questions

No. That is the point of the integration. We hold the Wahoo Cloud API registration, the client credentials and the redirect URI; you call POST /v1/connect with your own id for your own user and redirect their browser to the URL that comes back. There is no Wahoo secret in your environment, no callback route in your router, and no approval queue between you and your first connected athlete.

Two weeks free, then from 32.50 € a month billed annually — or 39 € a month with no year to commit to — for the whole API, every provider and every endpoint, with no per-athlete metering. Plans differ on one thing: how many of your users may connect a watch. Hacker is 250, for a project and its first users; Startup covers up to 5,000 of them; Scale up to 250,000; Enterprise removes the ceiling; and none of them meters calls, activities or endpoints. The usual price for a unified wearable API starts in the hundreds of dollars a month and charges again for each connected user, which is a pricing model that punishes exactly the thing you are trying to do. There is a 30-day money-back guarantee on any charge, and cancelling is a button in the Stripe billing portal; access ends when the period does.

You get push, not polling. When an athlete finishes a workout, Wahoo notifies us and we send you an activity.created delivery — sealed to your public key as a JWE and signed with a detached JWS you verify against our published JWKS. Latency is whatever Wahoo takes to process the upload from the watch, which is theirs to own and typically a matter of minutes. You never write a polling loop and never hold a rate-limit budget.

There is no API key anywhere in this, in either direction. You authenticate to us with an Ed25519 signature over the request itself — RFC 9421 HTTP Message Signatures — using a private key that never leaves your machine, so there is no bearer token sitting in a log line, a CI variable or a backup. And we never hand you the provider's own credentials either.

Yes — anything that lands in the athlete’s Wahoo account reaches you, whether it was recorded on an ELEMNT head unit outdoors, a RIVAL watch or a KICKR trainer indoors. The delivery names the device, so you can tell them apart without guessing from the data.

On Wahoo, yes, and verbatim — it reports the granted permissions verbatim, as Fitbit does, so a connection’s scope field is populated rather than null. On COROS and Polar we return null instead of filling the field with the scopes we requested, because inventing that answer is worse than admitting we do not have it.

Yes, up to five years of it. Wahoo publishes no backfill endpoint — there is nothing to ask for a replay of — so we page the athlete’s workout list ourselves and deliver each past workout through the same fan-out as a live one; on your side they are ordinary activities and nothing marks them as historical. The caveat is timing. Wahoo’s rate limit is per application rather than per athlete, and their list omits the file URL, so every past workout costs a request of its own. We take a bounded slice at a time and resume on a schedule, which means recent workouts land first and a heavy user’s full history can take up to a few days to finish arriving. New workouts are never delayed by it.

The full documentation is public and needs no account. Still have questions? Ask us in Discord

One integration, not seven. Same connect call, same event shape, same plan. Your second provider is a string change.

GarminCOROSPolarZeppHammerheadFitbit