Integrations
Strava
Your tool calls `ellivate.connection('strava')` and gets back a typed client whose `.fetch()` already has a valid, auto-refreshing OAuth token attached. The user clicks 'Connect with Strava' once in Ellivate; your tool reads their activities by calling Strava's API directly. Two scopes available today: `read` (profile) and `activity:read` (their own activities, public + non-private). Tools that display Strava data MUST include a 'View on Strava' link on each activity and a 'Powered by Strava' attribution somewhere visible — Strava's brand guidelines require it.
The pattern
# Python (Flask / FastAPI / Streamlit)
import ellivate
strava = ellivate.connection("strava")
try:
activities = strava.fetch_json(
"https://www.strava.com/api/v3/athlete/activities",
params={"per_page": 30},
)
except ellivate.EllivateConnectionNeeded:
# User hasn't connected Strava yet. Show them the connect button.
return render_template("connect_strava.html")
for a in activities:
print(a["name"], a["distance"], a["moving_time"])// JavaScript (Next.js server action / Vanilla HTML / any backed app)
import ellivate, { EllivateConnectionNeeded } from "./ellivate-client";
const strava = ellivate.connection("strava");
try {
const activities = await strava.fetchJson(
"https://www.strava.com/api/v3/athlete/activities?per_page=30"
);
// activities is the array Strava's API returns
for (const a of activities) {
console.log(a.name, a.distance, a.moving_time);
}
} catch (err) {
if (err instanceof EllivateConnectionNeeded) {
// User hasn't connected Strava yet — surface a connect prompt.
} else {
throw err;
}
}The connection handle is free — synchronous, no network call until you actually fetch. If the user hasn't connected Strava, EllivateConnectionNeeded fires the first time you call .fetch_json / .fetchJson. Catch it, show the user a “Connect with Strava” prompt, and they land back in your tool with the connection live.
Scopes available
| Scope | What it grants |
|---|---|
read | Basic profile — athlete ID, username, first name, last name, profile photo. Required for the connection to be labeled in the user's Connections dashboard with anything other than “Strava account.” |
activity:read | The user's public Strava activities plus any of their own non-private activities. Cannot see activities the user explicitly marked private. The primary functional scope for activity-aware tools. |
We deliberately did NOT include activity:read_all (which would let tools see explicitly-private activities) in the v1 scope catalog. If a tool genuinely needs it, that's a conversation about the privacy framing, not a config change.
Brand requirements (required for every Strava-using tool)
1. “View on Strava” link on each activity
When your tool displays a specific activity (name, summary, stats, anything), the activity name must link back to the activity's page on strava.com.
// activity.id is the numeric ID Strava returns on the activity object
const stravaUrl = `https://www.strava.com/activities/${activity.id}`;
// Render activity name as a link to stravaUrl, opens in new tab.2. “Powered by Strava” attribution
Somewhere visible on the surface that displays Strava data — a footer, a corner of the dashboard, a small line under the activity list. Plain text is acceptable; Strava also provides official wordmark assets if you want the branded look.
3. Capitalize “Strava”
Always “Strava” with capital S in user-facing copy. Lowercase only acceptable in code identifiers (e.g. ellivate.connection("strava")).
4. Don't imply partnership or endorsement
Words like “Strava partner,” “official Strava app,” or “built by Strava” are off-limits. Stick to neutral phrasing — “uses your Strava data,” “reads your Strava activities,” “via the Strava API.”
Example use cases
- Weekly activity digest — Time-scheduled tool that pulls the past 7 days of activities + uses
ellivate.reasonto write a short summary +ellivate.notifyfor push or email digest. - Training-load tracker — Aggregates moving time, elevation gain, suffer score over rolling windows; flags overtraining patterns.
- Streak counter — Counts consecutive days / weeks with any activity; pairs with
ellivate.notifyfor “don't break the chain” reminders. - Personal milestone dashboard — Cumulative distance per sport, fastest paces by distance, route heatmap from activity polylines.
All of these use the same one pattern above — ellivate.connection("strava").fetch_json(...) → process → display with the brand requirements above.
What your tool sees (and doesn't)
- ✅ Activities the user marked public OR didn't explicitly mark private (covered by
activity:read). - ✅ Basic profile data (username, name, photo URL).
- ❌ Activities explicitly marked private by the user.
- ❌ Other users' activities (you only see the athlete who connected).
- ❌ The user's direct messages, club memberships, or segment efforts unless granted via additional scopes (not in v1 catalog).
What data leaves Ellivate
Strava data flows from Strava's API directly to your tool's code via the connection proxy. Ellivate does not retain Strava activity data — your tool decides whether to persist it via ellivate.set, ellivate.collection, or ellivate.blob.
If your tool passes Strava data through ellivate.reason (e.g. “summarize this week's rides”), that data is included in the inference call to the configured LLM provider (Anthropic by default; OpenAI or Google AI if you've added those keys). See the privacy policy for the full data-flow disclosure.