Skip to content

Repository files navigation

Strava AI Summary GitHub-repository banner

Strava AI Summary

Stravvary (from Strava + summary) is a lightweight Vercel application built without a framework.

It uses Strava Webhooks to automatically detect newly uploaded runs. After each run, it generates an AI-powered coaching analysis using the OpenAI API and appends the summary to the activity's description on Strava.

How it works

  1. You finish a run → Strava sends a create event to /api/webhook
  2. The function acks within Strava's 2-second limit, then continues in the background (waitUntil):
    • refreshes your Strava access token
    • fetches the activity and skips it unless it's a run (Run/TrailRun/VirtualRun)
    • pulls this run's heart-rate zones and your last ~5 weeks of activities to build a training context (weekly/monthly load, a rolling 28-day baseline, intensity distribution, pace vs. your normal)
    • asks OpenAI for a short Finnish coaching analysis of the run in that context: pacing/splits, interval execution, best efforts and zone distribution
    • appends it to the description, marked with ⚙️ so it never double-processes

Only create events are processed. update events are ignored on purpose, otherwise editing the description would trigger an infinite loop. If processing fails, nothing is written to Strava and the run can be retried (see step 5 below).

Get started

1. Create a Strava API app

Go to https://www.strava.com/settings/api and create an app. Note the Client ID and Client Secret, and set the "Authorization Callback Domain" to your Vercel domain (e.g. your-app.vercel.app, you can come back and update this after deploying).

2. Deploy to Vercel

Deploy with Vercel

The deploy flow clones this repository to your account and prompts for the required environment variables:

Variable Value
STRAVA_CLIENT_ID from step 1
STRAVA_CLIENT_SECRET from step 1
STRAVA_VERIFY_TOKEN any random string you invent, used for the webhook handshake and to authenticate /api/retry
OPENAI_API_KEY from https://platform.openai.com/api-keys
Optional environment variables
Variable Value
OPENAI_MODEL defaults to gpt-5.6-terra; point it at e.g. gpt-5.4-mini for a cheaper option
OPENAI_REASONING_EFFORT none/low/medium/high/xhigh, defaults to high
STRAVA_ATHLETE_ID if set, only that athlete's activities are processed
RUNNER_DOB runner's date of birth (YYYY-MM-DD) for an age-based max-HR estimate; defaults to 2002-01-30
TELEGRAM_BOT_TOKEN Telegram bot token (from @BotFather) to also receive the summary/errors as messages
TELEGRAM_CHAT_ID the chat to notify, set alongside TELEGRAM_BOT_TOKEN

All of them are documented in .env.example.

Deploying with the CLI instead
npm i -g vercel   # if you don't have it
vercel deploy --prod

Then add the variables above in the Vercel dashboard (Project → Settings → Environment Variables) and redeploy.

3. Authorize your Strava account

Open https://your-app.vercel.app in a browser, click Connect with Strava, authorize, and copy the STRAVA_REFRESH_TOKEN it shows. Add it to the environment variables in the Vercel dashboard (Project → Settings → Environment Variables) and redeploy.

Don't use the token shown on Strava's "My API Application" settings page: it only has read scope and will NOT work. The auth flow requests the scopes the app actually needs (activity:read_all, activity:write, profile:read_all). The last scope lets the analysis use your configured HR zones instead of an age-based estimate.

4. Create the webhook subscription

Clone the repository Vercel created for you, copy .env.example to .env, fill in STRAVA_CLIENT_ID, STRAVA_CLIENT_SECRET, STRAVA_VERIFY_TOKEN and APP_BASE_URL, then run:

scripts/webhook.sh          # create the subscription
scripts/webhook.sh list     # show the current subscription
scripts/webhook.sh delete <id>

Strava calls your endpoint with a validation GET (answered by api/webhook.ts) and returns a subscription id. Strava allows only one subscription per API app, so if create reports one already exists, list then delete <id> and retry.

Equivalent raw curl commands
# create
curl -X POST https://www.strava.com/api/v3/push_subscriptions \
  -F client_id=YOUR_CLIENT_ID \
  -F client_secret=YOUR_CLIENT_SECRET \
  -F callback_url=https://your-app.vercel.app/api/webhook \
  -F verify_token=YOUR_STRAVA_VERIFY_TOKEN

# list
curl -G https://www.strava.com/api/v3/push_subscriptions \
  -d client_id=YOUR_CLIENT_ID -d client_secret=YOUR_CLIENT_SECRET

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

5. Go for a run 🏃

Record a run and the coaching summary appears in the activity description within a few seconds.

If processing ever fails (e.g. a transient OpenAI error), nothing is written to Strava. You can reprocess the activity manually with:

GET https://your-app.vercel.app/api/retry?id=<activityId>&token=<STRAVA_VERIFY_TOKEN>

It's safe to call repeatedly: the ⚙️ marker check makes an already-summarized activity a no-op, so a retry can never double-post. If Telegram notifications are configured, every failure message includes this link ready-made, so one tap retries the run.

Example summary

Example of a generated Finnish coaching summary appended to a Strava activity description

About

🔶 Personal automatic AI summaries for your Strava runs in Finnish.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages