Webhooks
WHOOP webhooks allow you to get notified when an event takes place for users that have authenticated with your app. By subscribing to webhooks, you will be alerted when a user's data was updated rather than needing to constantly make API requests to check for updates.
Setting Up Webhooks
You will first need to create an HTTPS URL endpoint to receive webhooks. This endpoint should be capable of accepting POST requests with a body consisting of the webhook data format and be able to perform webhook signature validation.
With your webhook endpoint set up, you can create or update an app to start accepting webhooks at that URL. Simply add your HTTPS URL to the Webhook URL section at the bottom of the page and save the app.
That's it! The next time a WHOOP user that has authenticated with your app triggers one of the supported webhook events, you will receive a webhook alerting you of the change.
Webhook Specifications
Webhooks are sent over HTTPS as a POST request to your configured URL. The body of the webhook request includes the following fields:
user_id | int64 The WHOOP User for the event |
id | int64 Identifier of the object that triggered this webhook |
type | string Enum: "workout.updated" "workout.deleted" "sleep.updated" "sleep.deleted" "recovery.updated" "recovery.deleted" The type of event that triggered this webhook |
trace_id | string Trace ID for the event that triggered this webhook |
{- "user_id": 10129,
- "id": 10235,
- "type": "workout.updated",
- "trace_id": "d3709ee7-104e-4f70-a928-2932964b017b"
}
Webhook Event Types
There are several events that are published as webhooks:
Event Type | ID Type | Explanation |
---|---|---|
recovery.updated | The id of the cycle for the recovery | Occurs when a recovery is created or updated. |
recovery.deleted | The id of the cycle for the recovery | Occurs when a recovery is deleted. Note: a recovery is deleted when its associated sleep is deleted. |
workout.updated | The id of the workout | Occurs when a workout is created or updated. |
workout.deleted | The id of the workout | Occurs when a workout is deleted. |
sleep.updated | The id of the sleep | Occurs when a sleep is created or updated. |
sleep.deleted | The id of the sleep | Occurs when a sleep is deleted. |
All webhook event types will be sent to the Webhook URL configured against your app. As such, if you do not want to process a certain type of event, you can simply respond with a 2XX
and skip any other processing you would normally do.
Webhooks Security
In order to validate that the webhooks you are receiving are originating from WHOOP, you will want to implement signature validation. This can be done by making use of two of the headers sent with each webhook request:
X-WHOOP-Signature
- the actual signatureX-WHOOP-Signature-Timestamp
- the milliseconds since epoch timestamp used to verify the 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. If they do not match then the request is invalid and should be dropped.
See below for example signature validation pseudocode:
calculated_signature_string = base64Encode(HMACSHA256(timestamp_header + raw_http_request_body, client_secret))
Example Request Flow
To illustrate a webhook use case, check out the below example request flow that utilizes webhooks.
- WHOOP user
456
goes through the OAuth consent flow with your app, allowing you to read their sleep data. - WHOOP user
456
records a sleep. WHOOP assigns id1234
to this sleep. - WHOOP makes a post request to your webhook endpoint with the body of:
{
"user_id": 456,
"id": 1234,
"type": "sleep.updated",
"trace_id": "e369c784-5100-49e8-8098-75d35c47b31b"
} - Based on seeing
sleep.updated
in thetype
field, your app makes a GET request to the/activity/sleep/1234
endpoint using the access token for WHOOP user456
in order to retrieve the sleep data.
As you begin implementing webhooks, you should have logic for interpreting each of these webhook event types. As shown in the example above, when you receive a sleep.updated
webhook for user 456
, you may want to make a request to the v1/activity/sleep/{sleepId}
endpoint using the sleepId of 1234
and the token for user of 456
if you want to get the data of the sleep the webhook was sent for.
Webhooks Testing
To generate a webhook, first authenticate with your app to ensure your data is retrievable via the API. Then, open the WHOOP app and do one of the following:
- Log an activity in the past (e.g. navigate to yesterday and create a 5 minute cycling activity) – once this activity is processed, your webhook endpoint will receive a
workout.updated
webhook. - Edit a previous sleep by changing the start or end time by 1 minute – once the sleep is processed, your webhook will receive both a
sleep.updated
webhook and arecovery.updated
webhook.
You can then edit or delete the workout you created, and you can revert the sleep duration change. Each of these actions will also result in webhooks being sent – workout.updated
, workout.deleted
, and sleep.updated
, respectively.
Delivery & Retries
WHOOP will retry webhook delivery for failed webhook requests five times over the course of about one hour. A webhook delivery is considered failed if it receives any response other than a successful 2XX
, or if it receives no response before timing out.
In order to reduce the amount of delivery failures you encounter, you should consider processing the requests asynchronously after returning a successful response, e.g. with a queue worker.
Best Practices
While working with webhooks, it is important to follow these best practices in order to ensure that you can get the most out of the webhook system.
Respond quickly
As laid out in the Delivery & Retries section, WHOOP will not indefinitely retry if your webhook endpoint is determined to be unavailable. We recommend designing your webhook API to return a successful 2XX
status code within a second and for high availability. If your webhook implementation has other dependencies or needs to do expensive work, one strategy is to have your API enqueue the event on a queue for asynchronous processing.
Validate signatures
Be sure to validate the message signature to ensure webhooks you receive were actually sent by WHOOP. Check out the Webhooks Security section to see more details on how to implement this.
Implement a reconciliation job
Since webhook delivery can fail webhooks should not be the sole source of truth for your application. Therefore, we recommend implementing a reconciliation job to occasionally fetch data from WHOOP.
Limitations
There are a few limitations to be aware of when implementing webhooks:
- It is possible you will receive multiple webhook invocations for the same triggering event. You can make use of the
trace_id
field in the webhook to detect duplicate webhooks. - These are event based webhooks, meaning they are notifications of changes and not actually the changes themselves. You will need to call the API in order to retrieve the most up-to-date data around this event. There is an example of what this flow would look like in the Example Request Flow section.
- It is possible that a webhook may be missed. You should implement a reconciliation job that can occasionally reach out to the WHOOP API for the data types you care about to fetch data that may have been missed.
Webhooks FAQs
Why are there only update and delete events? Do we get notified of create events?
Yes! When workouts, sleeps, or recoveries are created those events are published as an "update" event.
Are there webhooks for Day Strain, cycles, or body measurements?
Not at the moment; these data points should be retrieved by calling their respective APIs. All of the current webhook types are listed in the Event Types section.
How can I stop receiving webhooks for users who have disabled my integration?
It is best practice to revoke access tokens for users who have disabled your integration. Once you do revoke their access token, no webhooks will be sent for the user.