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.
npx claudepluginhub joshuarweaver/cascade-code-languages-python --plugin parkerhancock-wahoolibThis skill uses the workspace's default tool permissions.
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.
Conducts multi-round deep research on GitHub repos via API and web searches, generating markdown reports with executive summaries, timelines, metrics, and Mermaid diagrams.
Dynamically discovers and combines enabled skills into cohesive, unexpected delightful experiences like interactive HTML or themed artifacts. Activates on 'surprise me', inspiration, or boredom cues.
Generates images from structured JSON prompts via Python script execution. Supports reference images and aspect ratios for characters, scenes, products, visuals.
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.partnerships@wahoofitness.com (for entitlements) / developers@wahooligan.com