Documentation
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.
Supported Events
Webhooks are triggered for the following events:
| Object Type | Event | Description |
|---|---|---|
| activity | create | New activity created or made public |
| activity | update | Title, type, or privacy changed |
| activity | delete | Activity deleted or made private |
| athlete | update | Athlete 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
}| Field | Type | Description |
|---|---|---|
| object_type | string | "activity" or "athlete" |
| object_id | long | Activity ID or Athlete ID |
| aspect_type | string | "create", "update", or "delete" |
| updates | object | Changed fields (for update events) |
| owner_id | long | Athlete ID who owns the object |
| subscription_id | integer | Your webhook subscription ID |
| event_time | long | Unix 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_STRINGStep 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_STRINGYour 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.