Webhooks

Receive real-time notifications when activities are created, updated, or deleted. No more polling the API for changes.

How Webhooks Work

Instead of repeatedly polling the API to check for new activities, Strava sends HTTP POST requests to your server whenever relevant events occur.

Strava
POST /webhook
Your Server

Supported Events

Webhooks are triggered for the following events:

Object TypeEventDescription
activitycreateNew activity created or made public
activityupdateTitle, type, or privacy changed
activitydeleteActivity deleted or made private
athleteupdateAthlete deauthorized your app

Privacy Note: If your app only has activity:read scope (not activity:read_all), you'll receive a delete event when an activity is changed to "Only You" visibility, and a create event when it becomes public again.

Webhook Payload

Each webhook POST contains a JSON payload with the following structure:

{
  "object_type": "activity",
  "object_id": 12345678987654321,
  "aspect_type": "create",
  "updates": {},
  "owner_id": 12345678,
  "subscription_id": 123456,
  "event_time": 1704931200
}
FieldTypeDescription
object_typestring"activity" or "athlete"
object_idlongActivity ID or Athlete ID
aspect_typestring"create", "update", or "delete"
updatesobjectChanged fields (for update events)
owner_idlongAthlete ID who owns the object
subscription_idintegerYour webhook subscription ID
event_timelongUnix timestamp of the event

Creating a Webhook Subscription

Setting up webhooks is a two-step process: creating the subscription and verifying your callback URL.

Step 1: Create Subscription Request

curl -X POST https://www.strava.com/api/v3/push_subscriptions \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET \
  -d callback_url=https://your-app.com/webhook \
  -d verify_token=YOUR_RANDOM_STRING

Step 2: Verify Your Callback URL

Strava will immediately send a GET request to your callback URL with these parameters:

GET /webhook?hub.mode=subscribe&hub.challenge=abc123&hub.verify_token=YOUR_RANDOM_STRING

Your server must respond within 2 seconds with status 200 and echo the challenge:

{
  "hub.challenge": "abc123"
}

Express.js Example

const express = require('express');
const app = express();
app.use(express.json());

const VERIFY_TOKEN = process.env.STRAVA_VERIFY_TOKEN;

// Webhook verification (GET)
app.get('/webhook', (req, res) => {
  const mode = req.query['hub.mode'];
  const token = req.query['hub.verify_token'];
  const challenge = req.query['hub.challenge'];

  if (mode === 'subscribe' && token === VERIFY_TOKEN) {
    console.log('Webhook verified');
    res.json({ 'hub.challenge': challenge });
  } else {
    res.status(403).send('Forbidden');
  }
});

// Webhook events (POST)
app.post('/webhook', (req, res) => {
  const event = req.body;
  console.log('Received webhook:', event);

  // Process the event asynchronously
  processWebhookEvent(event);

  // Always respond quickly with 200
  res.status(200).send('OK');
});

async function processWebhookEvent(event) {
  if (event.object_type === 'activity') {
    if (event.aspect_type === 'create') {
      // Fetch the new activity
      const activity = await fetchActivity(event.object_id, event.owner_id);
      // Do something with it...
    } else if (event.aspect_type === 'delete') {
      // Handle deletion
      await deleteActivityFromDatabase(event.object_id);
    }
  } else if (event.object_type === 'athlete') {
    // Athlete deauthorized - clean up their data
    await removeAthleteData(event.owner_id);
  }
}

Managing Subscriptions

View Your Subscription

curl -X GET "https://www.strava.com/api/v3/push_subscriptions?client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET"

Delete Your Subscription

curl -X DELETE "https://www.strava.com/api/v3/push_subscriptions/SUBSCRIPTION_ID?client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET"

Limit: Each application can have only one webhook subscription. This single subscription receives events for all athletes who have authorized your app.

Webhook Best Practices

Respond Quickly

Return 200 OK within 2 seconds. Process events asynchronously.

Handle Retries

Strava retries failed deliveries up to 3 times. Make your handler idempotent.

Verify the Source

Validate the subscription_id matches yours to prevent spoofed events.

Fetch Full Data

Webhooks only contain IDs. Use the API to fetch complete activity/athlete data.

Handle Deauthorization

When you receive an athlete update, clean up their data from your system.