Skip to content
AF
Free Tool

Is this webhook real?

Pick the vendor, paste the request you received and your secret, and this checks it the way that vendor documents. Oura, WHOOP, Terra and Polar AccessLink sign each delivery with HMAC-SHA256, each over a different string; Strava, Withings, Wahoo and the Google Health API instead rely on a validation handshake or a secret you chose. It then explains each field, states the response and time limit the vendor expects, and names the API call that fetches the data. Each rule was read from the vendor’s own page on 2026-10-31. It all runs in your browser: the secret never leaves the tab.

Vendor

Oura: HMAC-SHA256, keyed by your client secret, over the x-oura-timestamp value followed by JSON.stringify(body); hex, upper-case, in x-oura-signature.

What Oura expects back

  • Respond quickly (under 10 seconds) with a 2xx status, and process the event asynchronously.
  • Oura retries failed deliveries 10 times each for 4xx responses, 5xx responses and timeouts, for about an hour.
  • A 410 response cancels the subscription.

Then fetch the data

  • GET https://api.ouraring.com/v2/usercollection/{data_type}/{object_id} with the user's OAuth token — the single-document route the OpenAPI document lists for collections such as sleep, workout, session and tag.

Rule read from Oura API v2 OpenAPI document (1.43), “Webhooks for Real-Time Data Updates” on 2026-10-31. Full integration walkthrough: Oura guide.

Each vendor’s rule, in its own words

The verifier encodes exactly these sentences. Where a vendor’s page leaves something unstated, the block says so rather than filling it in.

Oura

HMAC signature

HMAC-SHA256, keyed by your client secret, over the x-oura-timestamp value followed by JSON.stringify(body); hex, upper-case, in x-oura-signature.

hmac.update(timestamp + JSON.stringify(body));

const calculatedSignature = hmac.digest('hex').toUpperCase();

Oura's sample signs JSON.stringify of the parsed body, not the raw bytes, so this tool re-serializes the body you paste the same way. If Oura's serializer and JavaScript's ever differ (key order, spacing, escaped characters), a genuine request can fail this check; the page says when the raw body would have matched instead.

What to send back

  • Respond quickly (under 10 seconds) with a 2xx status, and process the event asynchronously.
  • Oura retries failed deliveries 10 times each for 4xx responses, 5xx responses and timeouts, for about an hour.
  • A 410 response cancels the subscription.

Handshake: When you create a subscription Oura sends GET <callback>?verification_token=…&challenge=…; check the token and reply with JSON {"challenge": "…"}.

Source: Oura API v2 OpenAPI document (1.43), “Webhooks for Real-Time Data Updates” — read 2026-10-31 (via the site's source-snapshot workflow). Guide: Oura integration guide.

WHOOP

HMAC signature

HMAC-SHA256, keyed by your app's client secret, over the X-WHOOP-Signature-Timestamp value followed by the raw body; base64, in X-WHOOP-Signature.

To verify the signature, first prepend the timestamp header value to the raw http request body. Then, generate a SHA256 HMAC signature of that string using the secret key for your app, which can be found in the WHOOP Developer Dashboard. Finally, base64 encode the result, and compare it to the signature header.

What to send back

  • Return a successful 2XX; WHOOP recommends doing so within a second and processing asynchronously.
  • WHOOP retries failed deliveries five times over about one hour; anything other than a 2XX, or no response before timing out, counts as failed.
  • Duplicates are possible: use trace_id to detect them.

Source: WHOOP Developer Platform, “Webhooks” — read 2026-10-31 (via the site's source-snapshot workflow). Guide: WHOOP integration guide.

Terra

HMAC signature

terra-signature: t=<timestamp>,v1=<signature>. HMAC-SHA256, keyed by the endpoint's signing secret, over timestamp + "." + raw body; compare with every v1 value, ignore other schemes.

The signed_payload string is created by concatenating:

Compute an HMAC with the SHA256 hash function. Use the endpoint’s signing secret as the key, and use the signed_payload string as the message.

Terra generates signatures using a hash-based message authentication code (HMAC) with SHA-256. To prevent downgrade attacks, ignore all schemes that aren’t v1

What to send back

  • Respond with a 2XX within 8 seconds — Terra's own advice is to accept immediately (return 200) and process asynchronously.
  • Otherwise Terra retries up to 10 times with exponential backoff, starting at 30 seconds and roughly doubling, across about 8 hours; after that the event is dropped with no notification.
  • Terra requires the raw, unaltered body for verification: read it before any JSON parsing.

Source: Terra docs, “Webhooks” (data destinations) — read 2026-10-31 (via the site's source-snapshot workflow). Guide: Terra integration guide.

Polar AccessLink

HMAC signature

HMAC-SHA256 of the payload, keyed by the signature_secret_key returned when you created the webhook; hex, in Polar-Webhook-Signature. Event type in Polar-Webhook-Event.

Hash-based message authentication code (HMAC SHA-256) is used to sign every webhook payload Accesslink sends. The secret key for calculating signature is given to client when they create the webhook.

To verify sender client should calculate HMAC and compare it to the signature in payload header.

Polar's sample returns toHexString(mac.doFinal(data.getBytes())) without saying upper or lower case; its example header is lower-case, and this tool compares hex case-insensitively.

What to send back

  • Webhook delivery expects response 200 from the remote endpoint — including the PING sent when the webhook is created or its URL changes, or it is not created/modified.
  • A webhook is automatically deactivated if push notifications have not been successful for seven days; re-enable it with the activate webhook endpoint.

Source: Polar AccessLink API v3 reference, “Accesslink webhooks” — read 2026-10-31 (via the site's source-snapshot workflow). Guide: Polar AccessLink integration guide.

Strava

Validation handshake, events unsigned

No signature is documented for event POSTs. Subscription validation is a GET with hub.mode, hub.challenge and hub.verify_token; echo hub.challenge as JSON within two seconds.

The query string of this GET request will contain a "hub.challenge" field that you must use to validate your callback address.

Your callback address must respond within two seconds to the GET request from Strava’s subscription service.

What to send back

  • Validation GET: respond within two seconds with status 200 and the hub.challenge echoed in the body as application/json.
  • Event POST: acknowledge with 200 OK within two seconds; pushes are retried up to a total of three attempts if a 200 is not returned.
  • Strava's webhook page documents no signature header for event POSTs, so an event cannot be authenticated from the request alone.

Source: Strava Developers, “Webhook Events API” — read 2026-10-31 (via the site's source-snapshot workflow). Guide: Strava integration guide.

Withings

Shared token, no signature

No signature. Withings suggests a secret query parameter on the callback URL you subscribe (its example: ?token=<random-secret>) plus an allowlist of its notification IPs. The body is form-encoded.

When you set the callback URL on your subscription, append a query parameter only you and Withings know. Withings POSTs to this exact URL, so the parameter is delivered with every notification:

Your handler reads token from the incoming request and rejects any value that doesn't match the secret you configured. Combine this with the IP allowlist for defense in depth.

What to send back

  • Your endpoint returns HTTP 2xx within a few seconds.
  • If it does not return HTTP < 400, Withings retries the same notification in 5 cycles over about 5 hours (+10 s, +1 min, +1 h, +4 h), 2 HTTP attempts each.
  • After 20 days of unresolved failures the subscription is auto-cancelled; missed notifications are not redelivered — backfill via the Data API using lastupdate.

Source: Withings developer guide, “Notifications overview” — read 2026-10-31 (via the site's source-snapshot workflow). Guide: Withings integration guide.

Wahoo

Shared token, no signature

No signature. Wahoo sends the webhook_token you configured inside the JSON body; ignore any request without it.

Specify a token that you would like us to send. Any request that doesn't include this token should be ignored.

What to send back

  • Return HTTP 200. Without it Wahoo retries after 30 minutes, 4 hours, 24 hours and 72 hours.
  • Wahoo's page states no response time limit.
  • Duplicate webhooks are possible when files are updated or deleted, and the user's token needs the offline_data scope.

Source: Wahoo Cloud API documentation, “Webhooks” — read 2026-10-31 (via the site's source-snapshot workflow). Guide: Wahoo integration guide.

Google Health API

Shared token, no signature

The documented check is the Authorization header: Google sends the endpointAuthorization secret you configured with every notification. A GOOGLE-HEALTH-API-SIGNATURE header appears in Google's examples, but the page does not say how it is computed, so this tool does not check it.

The value of secret is sent in the Authorization header with each notification message. You can use this token to verify that incoming requests are from the Google Health API.

What to send back

  • Validate the Authorization header and respond to data change notifications with HTTP 204 No Content immediately; process the payload asynchronously.
  • Any other status code, or a timeout, and the Google Health API retries later — so make handling idempotent.
  • Verification handshake (on subscriber create or endpoint update): two POSTs with body {"type": "verification"}; answer the one carrying your Authorization header with 200 or 201, and the one without credentials with 401 or 403.

Source: Google Health API, “Webhook subscriptions” — read 2026-10-31. Guide: Google Health API reference.

Not included, and why

  • Garmin. Garmin’s public Health API page offers “Push or *Ping/Pull Architecture” but states no request format or signing rule, so there is no public rule to verify against. See the Garmin integration guide.
  • Fitbit Web API. Google’s Google Health API pages, read 2026-10-31, say the legacy Fitbit Web API and Fitbit OAuth endpoints will be turned off on November 2, 2026; its successor, the Google Health API, is covered above.
  • Suunto, COROS, Dexcom and others. Their webhook documentation was not read for this tool, so they are not listed. A vendor is added only once its own page states the rule.
How this works

The page makes no network request with what you paste. Signatures are recomputed with the browser’s WebCrypto API (crypto.subtle, HMAC with SHA-256) and compared in constant time; the secret field is a password input with autocomplete off, and nothing is written to storage.

Paste the body exactly as it arrived. Every signed scheme here except Oura’s signs the raw bytes, and a framework that parses and re-serializes JSON before your handler sees it is the most common reason a genuine request fails verification.

“Load an example” builds a request from the vendor’s own example payload, signed now with a demo secret, so it verifies; change one character of the body or signature and it stops verifying. The same verifier code is tested that way for every vendor on each change.

A free tool from AIFitnessAPI. Choosing between these APIs in the first place: integration guides for every major fitness API. More at free tools for health-app builders.