Authenticate with and call the Wahoo Cloud API. Use when working with Wahoo fitness data: users, plans, workouts, routes, power zones, workout summaries, or webhooks. Triggers on "wahoo", "wahooligan", "ELEMNT", "KICKR", or fitness device API integration.
How this skill is triggered — by the user, by Claude, or both
Slash command
/parkerhancock-wahoolib:wahoo-apiThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Apply this skill whenever you must talk to Wahoo’s Cloud API: onboarding, fetching user/workout data, creating structured workout plans, scheduling workouts on devices, uploading workout result files, or wiring up offline webhooks. The API is OAuth2-based and expects base64-encoded JSON/FIT payloads for plans and routes.
Apply this skill whenever you must talk to Wahoo’s Cloud API: onboarding, fetching user/workout data, creating structured workout plans, scheduling workouts on devices, uploading workout result files, or wiring up offline webhooks. The API is OAuth2-based and expects base64-encoded JSON/FIT payloads for plans and routes.
client_id, client_secret, configured redirect URI, and note whether the app is Sandbox or Production.user_read, workouts_write, plans_write, offline_data, etc.). Sandbox apps are rate limited to 25 requests / 5 minutes, 100 / hour, 250 / day.https://api.wahooligan.com/oauth/authorize?client_id=<ID>&redirect_uri=<REDIRECT>&scope=<space-separated-scopes>&response_type=code.code for tokens:POST https://api.wahooligan.com/oauth/token with client_id, client_secret (or PKCE code_verifier), grant_type=authorization_code, redirect_uri, code.access_token, refresh_token, and expires_in. Refresh tokens expire whenever you refresh again; replace stored values immediately.https://api.wahooligan.com/v1/* with Authorization: Bearer <access_token> and Content-Type: application/json.external_id values stay stable to avoid duplicates.wahoolib package is installed in this environment. Use uv run wahoolib ... to drive OAuth flows, manage plans/routes/workouts, or deauthorize tokens without re-implementing requests manually.wahoolib (bundled with this plugin at src/wahoolib/). Import wahoolib.WahooAPIClient or wahoolib.WahooOAuthClient inside scripts to work with typed models. Install with uv pip install -e <plugin-root> if not already available.uv run wahoolib <command> from anywhere. Common patterns:
uv run wahoolib auth url – build authorization URLs.uv run wahoolib plans create --plan-file plan.json --external-id plan_123 --provider-updated-at 2024-10-20T12:00:00 – upload a structured workout plan.uv run wahoolib workouts upload --fit-file ride.fit --token-file token.json – upload FIT results.WAHOO_CLIENT_ID, WAHOO_CLIENT_SECRET, and WAHOO_REDIRECT_URI if set. Export them once to avoid repeating --client-id, etc.auth exchange --output token.json writes a ready-to-use file). Pass via --token-file token.json on subsequent commands.code_challenge/code_verifier and setting the app’s “Confidential?” flag to “No”.curl -X POST https://api.wahooligan.com/oauth/token \
-d client_id="$WAHOO_CLIENT_ID" \
-d client_secret="$WAHOO_CLIENT_SECRET" \
-d grant_type=refresh_token \
-d refresh_token="$WAHOO_REFRESH_TOKEN"
PKCE refresh requests omit the secret but include code_verifier.user_read, user_write, emailworkouts_read, workouts_writeplans_read, plans_writeroutes_read, routes_writepower_zones_read, power_zones_writeoffline_data (required for webhooks)X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.type, duration, targets (power, HR), cadence.encoded_plan=$(base64 -w0 plan.json)
curl -X POST https://api.wahooligan.com/v1/plans \
-H "Authorization: Bearer $WAHOO_ACCESS_TOKEN" \
-d "plan[file]=data:application/json;base64,$encoded_plan" \
-d "plan[filename]=plan.json" \
-d "plan[external_id]=fitness-plan-123" \
-d "plan[provider_updated_at]=2025-10-19T18:00:00Z"
id. Reuse the same external_id when updating.GET /v1/plans/:id or list with GET /v1/plans?external_id=....curl -X POST https://api.wahooligan.com/v1/workouts \
-H "Authorization: Bearer $WAHOO_ACCESS_TOKEN" \
-d "workout[name]=Trainer Ride" \
-d "workout[workout_token]=ride-2025-10-20" \
-d "workout[workout_type_id]=61" \
-d "workout[starts]=2025-10-20T12:00:00Z" \
-d "workout[minutes]=75" \
-d "workout[plan_id]=$PLAN_ID"
plan_ids to the array of plan IDs.encoded_fit=$(base64 -w0 results.fit)
/v1/workout_file_uploads with optional target_workout_id to map the summary back./v1/workout_file_uploads/:token until status is complete. Then read workout_id / workout_summary_id.GET /v1/workouts/:id/workout_summary.Routes:
POST /v1/routes with base64 FIT data, metadata (distance, ascent, start_lat/lng).PUT /v1/routes/:id.GET /v1/routes optionally filtering by external_id.Power Zones:
POST/PUT /v1/power_zones.zone_1 ... zone_7, ftp, critical_power) plus classification IDs.GET /v1/power_zones/:id; list all: GET /v1/power_zones.GET /v1/user requires user_read; email scope adds email.PUT /v1/user (height, weight, name, gender).DELETE /v1/permissions. Optionally configure the developer portal flags to auto-delete plans/workouts on deauth.webhook_enabled, URL, and verification token.offline_data.{
"event_type": "workout_summary",
"webhook_token": "...",
"user": {"id": 12345},
"workout_summary": {...}
}
GET /v1/workouts/:id).Content-Type: application/json except when sending form-encoded or multipart data (plans, routes, uploads).external_id everywhere possible to enforce idempotency and make subsequent updates easier.fitness_app_id values in workout summaries—IDs < 1000 indicate Wahoo-owned workouts (uneditable).external_id never created it.X-RateLimit-Reset seconds elapse; exponential backoff unnecessary if you obey the schedule.starts is within 6 days, the plan exists, and the device synced after creation.workout_summary.id and event_type; Wahoo may resend on retries.[email protected] (for entitlements) / [email protected]Guides reception of code review feedback: verify before implementing, avoid performative agreement, push back with technical reasoning when needed.
Design banners for social media, ads, website heroes, and print with multiple art direction options and AI-generated visuals.
npx claudepluginhub joshuarweaver/cascade-code-languages-python --plugin parkerhancock-wahoolib