{
  "info": {
    "_postman_id": "aifitnessapi-wahoo",
    "name": "Wahoo (aifitnessapi.com)",
    "description": "Wahoo: OAuth 2.0 and Webhook samples requests built from the vendor documentation aifitnessapi.com quotes. Generated by scripts/build-collections.mjs; no secrets included. https://aifitnessapi.com/collections",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "name": "OAuth 2.0",
      "description": "Wahoo's authorization-code flow as its documentation describes it, read 2026-11-05. Fill client_id, redirect_uri and scope in an environment of your own and keep client_secret there, never in a shared collection.\n\nThe same facts, with each source sentence, at https://aifitnessapi.com/integrate/wahoo-api and https://aifitnessapi.com/tools/oauth-playground.",
      "item": [
        {
          "name": "1. Authorization URL (open in a browser)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "https://api.wahooligan.com/oauth/authorize?response_type=code&client_id={{client_id}}&redirect_uri={{redirect_uri}}&scope={{scope}}&state={{state}}&code_challenge={{code_challenge}}&code_challenge_method=S256",
              "protocol": "https",
              "host": [
                "api",
                "wahooligan",
                "com"
              ],
              "path": [
                "oauth",
                "authorize"
              ],
              "query": [
                {
                  "key": "response_type",
                  "value": "code"
                },
                {
                  "key": "client_id",
                  "value": "{{client_id}}"
                },
                {
                  "key": "redirect_uri",
                  "value": "{{redirect_uri}}"
                },
                {
                  "key": "scope",
                  "value": "{{scope}}"
                },
                {
                  "key": "state",
                  "value": "{{state}}"
                },
                {
                  "key": "code_challenge",
                  "value": "{{code_challenge}}"
                },
                {
                  "key": "code_challenge_method",
                  "value": "S256"
                }
              ]
            },
            "description": "https://api.wahooligan.com/oauth/authorize. Authorization URL (base_url is api.wahooligan.com). Open this URL in a browser (in Postman: copy it from the request bar); the user signs in and Wahoo redirects to {{redirect_uri}} with ?code=… Put that value in the auth_code variable.\n\nSource: Wahoo Cloud API documentation, “Authentication”, https://cloud-api.wahooligan.com/\n\nScope: Space-separated, or set the app's scopes in the developer portal instead. Source: Wahoo Cloud API documentation, “Authentication”, https://cloud-api.wahooligan.com/\n\nDocumented scopes (joined with spaces):\n- email: Access your email address\n- user_read: Access your user data; without it the API returns 403\n- user_write: Update your user data\n- workouts_read: Access your workout history\n- workouts_write: Update your workout history\n- offline_data: Receive data even when the app is closed; needed for webhooks\n- power_zones_read: Listed on Wahoo's developer portal, without a description\n- power_zones_write: Listed on Wahoo's developer portal, without a description\n- plans_read: Listed on Wahoo's developer portal, without a description\n- plans_write: Listed on Wahoo's developer portal, without a description\n- routes_read: Listed on Wahoo's developer portal, without a description\n- routes_write: Listed on Wahoo's developer portal, without a description\n\nPKCE: Supported once the app's Confidential field is set to No; S256 or plain. With PKCE the token and refresh calls send code_verifier instead of client_secret. Generate a verifier and challenge in your browser at https://aifitnessapi.com/tools/oauth-playground and put them in code_verifier and code_challenge. Source: Wahoo Cloud API documentation, “Authentication”, https://cloud-api.wahooligan.com/"
          },
          "response": []
        },
        {
          "name": "2. Exchange the code for tokens",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "https://api.wahooligan.com/oauth/token?client_secret={{client_secret}}&code={{auth_code}}&redirect_uri={{redirect_uri}}&grant_type=authorization_code&client_id={{client_id}}",
              "protocol": "https",
              "host": [
                "api",
                "wahooligan",
                "com"
              ],
              "path": [
                "oauth",
                "token"
              ],
              "query": [
                {
                  "key": "client_secret",
                  "value": "{{client_secret}}"
                },
                {
                  "key": "code",
                  "value": "{{auth_code}}"
                },
                {
                  "key": "redirect_uri",
                  "value": "{{redirect_uri}}"
                },
                {
                  "key": "grant_type",
                  "value": "authorization_code"
                },
                {
                  "key": "client_id",
                  "value": "{{client_id}}"
                }
              ]
            },
            "description": "https://api.wahooligan.com/oauth/token. Token URL. Wahoo documents the parameters in the query string of the POST.\n\nSource: Wahoo Cloud API documentation, “Authentication”, https://cloud-api.wahooligan.com/\n\nWahoo documents the parameters in the query string of the POST.\n\nToken lifetime: Two hours. Source: Wahoo Cloud API documentation, “Authentication”, https://cloud-api.wahooligan.com/\n\nNote: From January 1, 2026, 10 unrevoked access tokens per user; unrevoked tokens are deleted 60 days after creation. Source: Wahoo Cloud API documentation, “Authentication”, https://cloud-api.wahooligan.com/\n\nNote: Old tokens are revoked once an API call is made with the refreshed token. Source: Wahoo Cloud API documentation, “Authentication”, https://cloud-api.wahooligan.com/"
          },
          "response": []
        },
        {
          "name": "2b. Exchange the code for tokens (PKCE app)",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "https://api.wahooligan.com/oauth/token?client_id={{client_id}}&code={{auth_code}}&redirect_uri={{redirect_uri}}&grant_type=authorization_code&code_verifier={{code_verifier}}",
              "protocol": "https",
              "host": [
                "api",
                "wahooligan",
                "com"
              ],
              "path": [
                "oauth",
                "token"
              ],
              "query": [
                {
                  "key": "client_id",
                  "value": "{{client_id}}"
                },
                {
                  "key": "code",
                  "value": "{{auth_code}}"
                },
                {
                  "key": "redirect_uri",
                  "value": "{{redirect_uri}}"
                },
                {
                  "key": "grant_type",
                  "value": "authorization_code"
                },
                {
                  "key": "code_verifier",
                  "value": "{{code_verifier}}"
                }
              ]
            },
            "description": "https://api.wahooligan.com/oauth/token. Token URL. Wahoo documents the parameters in the query string of the POST.\n\nSource: Wahoo Cloud API documentation, “Authentication”, https://cloud-api.wahooligan.com/\n\nA PKCE app sends code_verifier instead of client_secret. Supported once the app's Confidential field is set to No; S256 or plain. With PKCE the token and refresh calls send code_verifier instead of client_secret.\n\nToken lifetime: Two hours. Source: Wahoo Cloud API documentation, “Authentication”, https://cloud-api.wahooligan.com/\n\nNote: From January 1, 2026, 10 unrevoked access tokens per user; unrevoked tokens are deleted 60 days after creation. Source: Wahoo Cloud API documentation, “Authentication”, https://cloud-api.wahooligan.com/\n\nNote: Old tokens are revoked once an API call is made with the refreshed token. Source: Wahoo Cloud API documentation, “Authentication”, https://cloud-api.wahooligan.com/"
          },
          "response": []
        },
        {
          "name": "3. Refresh the access token",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "https://api.wahooligan.com/oauth/token?client_secret={{client_secret}}&client_id={{client_id}}&grant_type=refresh_token&refresh_token={{refresh_token}}",
              "protocol": "https",
              "host": [
                "api",
                "wahooligan",
                "com"
              ],
              "path": [
                "oauth",
                "token"
              ],
              "query": [
                {
                  "key": "client_secret",
                  "value": "{{client_secret}}"
                },
                {
                  "key": "client_id",
                  "value": "{{client_id}}"
                },
                {
                  "key": "grant_type",
                  "value": "refresh_token"
                },
                {
                  "key": "refresh_token",
                  "value": "{{refresh_token}}"
                }
              ]
            },
            "description": "Refresh: Refresh returns a new access and refresh token. The previous pair is revoked only when an API call uses the new access token, so refresh just before a request: apps are limited to 10 unrevoked tokens per user.\n\nSource: Wahoo Cloud API documentation, “Authentication”, https://cloud-api.wahooligan.com/"
          },
          "response": []
        },
        {
          "name": "3b. Refresh the access token (PKCE app)",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "https://api.wahooligan.com/oauth/token?client_id={{client_id}}&grant_type=refresh_token&refresh_token={{refresh_token}}&code_verifier={{code_verifier}}",
              "protocol": "https",
              "host": [
                "api",
                "wahooligan",
                "com"
              ],
              "path": [
                "oauth",
                "token"
              ],
              "query": [
                {
                  "key": "client_id",
                  "value": "{{client_id}}"
                },
                {
                  "key": "grant_type",
                  "value": "refresh_token"
                },
                {
                  "key": "refresh_token",
                  "value": "{{refresh_token}}"
                },
                {
                  "key": "code_verifier",
                  "value": "{{code_verifier}}"
                }
              ]
            },
            "description": "Refresh: Refresh returns a new access and refresh token. The previous pair is revoked only when an API call uses the new access token, so refresh just before a request: apps are limited to 10 unrevoked tokens per user.\n\nSource: Wahoo Cloud API documentation, “Authentication”, https://cloud-api.wahooligan.com/"
          },
          "response": []
        }
      ]
    },
    {
      "name": "Webhook samples",
      "description": "Wahoo's documented example notifications, sent to {{local_url}} (your local handler) unchanged. Signature: No signature. Wahoo sends the webhook_token you set in the app's webhook settings inside the JSON body; ignore any request without it. Source: Wahoo Cloud API documentation, “Webhooks”, https://cloud-api.wahooligan.com/\n\nHeaders that carry a signature hold the placeholder Wahoo prints, so a handler that verifies signatures will reject these: to send correctly signed requests, use https://aifitnessapi.com/webhooks/send-sample.mjs. Respond: An HTTP POST with Content-Type application/json; return HTTP 200. The users need the offline_data scope.\n\nPayload library with JSON Schemas: https://aifitnessapi.com/webhooks#wahoo.",
      "item": [
        {
          "name": "Workout summary",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{local_url}}",
              "host": [
                "{{local_url}}"
              ]
            },
            "description": "Sent when a workout summary is available for a user whose token has the offline_data scope.\n\nWahoo's sample workout summary webhook message. Source: Wahoo Cloud API documentation, “Webhooks”, https://cloud-api.wahooligan.com/\n\nWahoo's example has the placeholder <webhook_token> where the token goes; the mock sender puts your token there and sends the rest unchanged.",
            "body": {
              "mode": "raw",
              "raw": "{\n\"event_type\" : \"workout_summary\" ,\n\"webhook_token\" : \"{{webhook_token}}\" ,\n\"user\" : {\n\"id\" : 60462\n},\n\"workout_summary\" : {\n\"id\" : 8297 ,\n\"ascent_accum\" : \"450.00\" ,\n\"cadence_avg\" : \"52.00\" ,\n\"calories_accum\" : \"1500.00\" ,\n\"distance_accum\" : \"24909.71\" ,\n\"duration_active_accum\" : \"179.00\" ,\n\"duration_paused_accum\" : \"85.00\" ,\n\"duration_total_accum\" : \"275.20\" ,\n\"heart_rate_avg\" : \"124.23\" ,\n\"power_bike_np_last\" : \"150.01\" ,\n\"power_bike_tss_last\" : \"304.90\" ,\n\"power_avg\" : \"94.59\" ,\n\"speed_avg\" : \"10.75\" ,\n\"work_accum\" : \"104148000.01\" ,\n\"created_at\" : \"2018-10-23T20:43:50.000Z\" ,\n\"updated_at\" : \"2018-10-23T20:43:50.000Z\" ,\n\"file\" : {\n\"url\" : \"https://server.com/4_Mile_Segment_.fit\"\n},\n\"workout\" : {\n\"id\" : 56519 ,\n\"starts\" : \"2015-08-12T09:00:00.000Z\" ,\n\"minutes\" : 12 ,\n\"name\" : \"Friday Fun\" ,\n\"created_at\" : \"2018-10-23T20:41:55.000Z\" ,\n\"updated_at\" : \"2018-10-23T20:41:55.000Z\" ,\n\"plan_id\" : null ,\n\"workout_token\" : \"123\" ,\n\"workout_type_id\" : 40\n}\n}\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    }
  ],
  "variable": [
    {
      "key": "client_id",
      "value": "",
      "type": "string",
      "description": "Your app's client ID, from the vendor's developer console."
    },
    {
      "key": "client_secret",
      "value": "",
      "type": "string",
      "description": "Keep it in a Postman or Bruno environment marked secret. This collection ships it empty."
    },
    {
      "key": "redirect_uri",
      "value": "",
      "type": "string",
      "description": "The redirect URI registered with the vendor."
    },
    {
      "key": "scope",
      "value": "",
      "type": "string",
      "description": "Scopes to request, joined as the vendor documents (see the authorization request)."
    },
    {
      "key": "state",
      "value": "",
      "type": "string",
      "description": "A random value you check on the redirect."
    },
    {
      "key": "code_verifier",
      "value": "",
      "type": "string",
      "description": "PKCE verifier (generate one at /tools/oauth-playground)."
    },
    {
      "key": "code_challenge",
      "value": "",
      "type": "string",
      "description": "PKCE challenge for that verifier."
    },
    {
      "key": "auth_code",
      "value": "",
      "type": "string",
      "description": "The ?code= value from the redirect."
    },
    {
      "key": "refresh_token",
      "value": "",
      "type": "string",
      "description": "The refresh_token from the token response."
    },
    {
      "key": "local_url",
      "value": "http://localhost:3000/webhooks/wahoo",
      "type": "string",
      "description": "Where your webhook handler listens."
    },
    {
      "key": "webhook_token",
      "value": "",
      "type": "string",
      "description": "Wahoo: the webhook token you set in the app's webhook settings."
    }
  ]
}
