Gareth Wilson Gareth Wilson

Guide to Strava Webhooks: Features and Best Practices

Published


Strava webhooks notify your application when athletes' activities and profiles change: an activity is created, an activity is updated or deleted, an athlete deauthorizes your app. If you're building on Strava, webhooks are how you react without polling every athlete's data on a timer.

This guide covers how Strava webhooks work, the events you'll handle, the subscription validation handshake (there's no per-event signature, so this matters), the thin-payload model, and the best practices for production.

What are Strava webhooks?

Strava uses a push subscription model. You create one subscription per API application, and Strava then POSTs events for every athlete who has authorized your app. Each event is thin: it carries an object_id and a few fields, and you fetch the full data from the REST API.

The important thing to understand up front: Strava webhook events are not cryptographically signed. There's no per-event HMAC or signature to verify. Authenticity is established once, at subscription time, through a validation handshake plus a verify_token you choose.

Strava webhook features

FeatureDetails
ConfigurationPOST /api/v3/push_subscriptions (one subscription per application)
ValidationGET handshake: echo hub.challenge as JSON within 2 seconds
Per-event signatureNone (no HMAC / shared-secret signature)
Shared tokenverify_token you choose, checked at subscription time
PayloadThin: object_type, aspect_type, object_id, owner_id, subscription_id, event_time, updates
Data fetchCall the REST API with the athlete's token
Acknowledgement200 within 2 seconds
SDKNo official SDK (community libraries only)

Common events

Strava events combine an object_type and an aspect_type:

EventFires when
activity / createAn athlete uploads or creates an activity
activity / updateAn activity's title, type, or privacy changes (see updates)
activity / deleteAn activity is deleted
athlete / updateAn athlete updates their profile, or deauthorizes your app (updates.authorized: "false")

The payload tells you what changed by ID; fetch the full activity or athlete from the REST API when you need the details.

Setting up Strava webhooks

Create a subscription by POSTing to https://www.strava.com/api/v3/push_subscriptions with your client_id, client_secret, callback_url, and a verify_token you choose:

curl -X POST "https://www.strava.com/api/v3/push_subscriptions" \
  -F client_id=$STRAVA_CLIENT_ID \
  -F client_secret=$STRAVA_CLIENT_SECRET \
  -F callback_url=https://your-app.example.com/webhook \
  -F verify_token=$YOUR_VERIFY_TOKEN

Strava immediately GETs your callback_url to validate it (the handshake below). Only one subscription is allowed per application.

Securing Strava webhooks

There's no per-event signature, so the trust mechanism has two parts.

The validation handshake

When you create the subscription, Strava sends a GET to your callback_url with hub.mode=subscribe, a random hub.challenge, and your hub.verify_token. Confirm the token matches, then respond within 2 seconds with a 200 and a JSON body echoing the challenge:

const VERIFY_TOKEN = process.env.STRAVA_VERIFY_TOKEN;

app.get("/webhook", (req, res) => {
  if (req.query["hub.mode"] === "subscribe" &&
      req.query["hub.verify_token"] === VERIFY_TOKEN) {
    return res.status(200).json({ "hub.challenge": req.query["hub.challenge"] });
  }
  res.sendStatus(403);
});

Event POSTs

Because event POSTs aren't signed, you can't cryptographically prove a given POST came from Strava. Reduce the risk that comes with an unauthenticated endpoint:

app.post("/webhook", express.json(), (req, res) => {
  res.sendStatus(200); // ack within 2 seconds

  const { object_type, aspect_type, object_id, owner_id } = req.body;
  // Treat the POST as an untrusted trigger: fetch the real data from the
  // Strava REST API using owner_id's stored token before acting on it.
  enqueue(() => syncFromStrava(object_type, object_id, owner_id));
});
  • Keep the callback URL secret and hard to guess (a long random path segment).
  • Treat every POST as an untrusted trigger and fetch the authoritative data from the REST API with the athlete's token, so a spoofed POST can't feed you fake data.
  • Add your own endpoint auth (a secret path or a header your infrastructure checks) since Strava adds none.

Strava webhook limitations and pain points

No per-event signature

The Problem: Strava doesn't sign event POSTs. Any request to your callback URL looks like a legitimate event, so you can't verify authenticity cryptographically.

Why It Happens: Strava's design authenticates the subscription once (handshake + verify_token) rather than signing each event.

Workarounds:

  • Treat POSTs as triggers and fetch real data from the REST API.
  • Keep the callback URL secret and add your own endpoint auth.

How Hookdeck Can Help: Hookdeck gives you a dedicated ingestion URL and can enforce its own authentication and filtering in front of your endpoint, adding a verification layer Strava itself doesn't provide.

One subscription per application

The Problem: You get exactly one subscription per app. You can't split events across multiple endpoints (for example, staging and production) at the Strava level.

Why It Happens: Strava's model is one subscription per application, fanning out all authorized athletes' events to it.

Workarounds:

  • Route to environments downstream of a single endpoint.
  • Manage subscription creation/deletion carefully during deploys.

How Hookdeck Can Help: Hookdeck receives the single subscription and fans events out to multiple destinations (staging, production, analytics) with routing rules, so one Strava subscription can feed many consumers.

Thin payloads and the 2-second window

The Problem: Payloads carry only IDs, so every event needs a REST API fetch, and you must ack within 2 seconds. Doing the fetch synchronously blows the window.

Why It Happens: Strava keeps payloads minimal and expects a fast ack.

Workarounds:

  • Return 200 immediately and fetch data asynchronously.
  • Respect the REST API rate limits when fetching.

How Hookdeck Can Help: Hookdeck acknowledges Strava within the window and durably queues events so your worker fetches from the API at a controlled rate.

Duplicates and deauthorization

The Problem: Events can repeat, and an athlete/update with updates.authorized: "false" signals a deauthorization you must handle (stop using that athlete's token). Missing it leaves you calling the API with a revoked token.

Why It Happens: At-least-once delivery plus deauthorization delivered as a regular event.

Workarounds:

  • Dedupe on object_id + event_time, and handle the deauthorization event explicitly.

How Hookdeck Can Help: Hookdeck deduplicates deliveries at the edge, so repeats don't double-process. See our guide to webhook idempotency.

Best practices

Nail the handshake

Echo hub.challenge as JSON with a 200 within 2 seconds, and check hub.verify_token matches the token you chose.

Treat POSTs as untrusted triggers

Since events aren't signed, fetch authoritative data from the REST API and keep the callback URL secret with your own endpoint auth.

Acknowledge fast, fetch asynchronously

Return 200 immediately and fetch full data off a queue, within the REST rate limits. See why to process webhooks asynchronously.

Handle deauthorization

Watch for athlete/update with updates.authorized: "false" and stop using that athlete's token.

Make Strava webhooks production-ready

Hookdeck adds the verification and filtering Strava lacks, fans out to multiple destinations, and durably queues every event

Conclusion

Strava webhooks use a one-subscription-per-app push model with a validation handshake and a verify_token, but no per-event signature, so you treat each POST as an untrusted trigger and fetch authoritative data from the REST API. Add thin payloads, a 2-second ack window, one subscription per app, and deauthorization delivered as an event, and there's real handling required.

Hookdeck adds the verification and filtering Strava doesn't provide, fans a single subscription out to multiple destinations, deduplicates, and durably queues every event at the edge, so your app processes trustworthy, unique events.

Get started with Hookdeck for free and handle Strava webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.