This guide provides step-by-step instructions for configuring webhooks in each fitness tracker provider's developer portal to enable real-time data synchronization with HealthDecoder.
- Prerequisites
- Fitbit Webhook Setup
- Whoop Webhook Setup
- Oura Webhook Setup
- Environment Variables Summary
- Troubleshooting
Before configuring webhooks, ensure you have:
- A Supabase project with Edge Functions deployed
- Your Supabase project ID (found in project settings)
- Developer accounts with each fitness tracker provider
- The webhook Edge Functions deployed:
fitbit-webhookwhoop-webhookoura-webhook
All webhook URLs follow this pattern:
https://[YOUR-PROJECT-ID].supabase.co/functions/v1/[provider]-webhook
Replace [YOUR-PROJECT-ID] with your actual Supabase project reference ID.
Fitbit uses a Subscription API that sends push notifications when user data changes. The webhook endpoint must handle both verification requests (GET) and notification delivery (POST).
https://[YOUR-PROJECT-ID].supabase.co/functions/v1/fitbit-webhook
- Go to dev.fitbit.com
- Sign in with your Google account (required for Fitbit developer access)
- Navigate to Manage > Register An App
-
Fill in the application details:
- Application Name: Your app name
- Description: Brief description
- Application Website URL: Your website
- Organization: Your organization name
- Organization Website URL: Your organization website
- Terms of Service URL: Your ToS URL
- Privacy Policy URL: Your privacy policy URL
- OAuth 2.0 Application Type: Select "Server"
- Callback URL: Your OAuth callback URL
- Default Access Type: Select "Read-Only" or "Read & Write" as needed
-
Click Register to create the application
- In your application settings, locate the Subscription section
- Enter your webhook URL in the Subscriber Endpoint field:
https://[YOUR-PROJECT-ID].supabase.co/functions/v1/fitbit-webhook - Note the Subscriber Verification Code displayed (you'll need this for the environment variable)
Set the following secret in your Supabase project:
supabase secrets set FITBIT_SUBSCRIBER_VERIFICATION_CODE="your-verification-code-here"This code is used for:
- Verification requests: Fitbit sends GET requests with
?verify=CODEto confirm your endpoint - Signature validation: Used to verify webhook POST request signatures via HMAC-SHA256
- Click the Verify button in the Fitbit developer dashboard
- Fitbit will send two GET requests to your endpoint:
- One with the correct verification code (expect HTTP 204 response)
- One with an incorrect code (expect HTTP 404 response)
- Both requests must respond correctly within 5 seconds
- If successful, your endpoint will be marked as "Verified"
After verification, you can create subscriptions programmatically using the Fitbit API. Subscribe to the following collection types:
| Collection Type | Description | Maps To |
|---|---|---|
activities |
Daily activity summaries | summary sync |
sleep |
Sleep data | sleep sync |
body |
Body measurements | summary sync |
weight |
Weight measurements | summary sync |
Fitbit signs webhook payloads using HMAC-SHA256:
- Header:
X-Fitbit-Signature - Algorithm: HMAC-SHA256
- Key:
SUBSCRIBER_VERIFICATION_CODE + "&" - Data:
request_body + "&" - Format: Base64-encoded signature
The webhook receives an array of notifications:
[
{
"collectionType": "activities",
"date": "2025-01-15",
"ownerId": "ABC123",
"ownerType": "user",
"subscriptionId": "1"
}
]Whoop webhooks notify your application when user data is created, updated, or deleted. The webhook endpoint only handles POST requests (no verification handshake required).
https://[YOUR-PROJECT-ID].supabase.co/functions/v1/whoop-webhook
- Go to developer.whoop.com
- Sign in with your Whoop account credentials
- Navigate to the Dashboard
- Click Create App in the dashboard
- Fill in the application details:
- App Name: Your application name
- Description: Brief description
- Redirect URI: Your OAuth callback URL
- Save the application
- In your application settings, scroll to the Webhooks section
- Add your webhook URL:
https://[YOUR-PROJECT-ID].supabase.co/functions/v1/whoop-webhook - Select the Model Version (choose
v2for new integrations) - Save your changes
Set the following secret in your Supabase project:
supabase secrets set WHOOP_CLIENT_SECRET="your-client-secret-here"Find your Client Secret in the app settings of the Whoop Developer Dashboard. This is used for webhook signature verification.
Whoop signs webhook payloads using HMAC-SHA256:
- Signature Header:
X-WHOOP-Signature - Timestamp Header:
X-WHOOP-Signature-Timestamp(milliseconds since epoch) - Algorithm: HMAC-SHA256
- Key: Your
CLIENT_SECRET - Data:
timestamp + request_body(concatenated) - Format: Base64-encoded signature
All configured webhook URLs receive all event types:
| Event Type | Description | Maps To |
|---|---|---|
recovery.updated |
Recovery data updated | recovery sync |
recovery.deleted |
Recovery data deleted | - |
sleep.updated |
Sleep data updated | sleep sync |
sleep.deleted |
Sleep data deleted | - |
workout.updated |
Workout data updated | workout sync |
workout.deleted |
Workout data deleted | - |
{
"user_id": 12345,
"type": "recovery.updated",
"id": 67890,
"trace_id": "abc-123-def"
}- Return HTTP
2XXstatus code within 1 second - For async processing, acknowledge immediately and queue the work
- Failed deliveries are retried up to 5 times over approximately 1 hour
Oura webhooks notify your application when user data is available. The webhook endpoint must handle both challenge verification (GET) and event delivery (POST).
https://[YOUR-PROJECT-ID].supabase.co/functions/v1/oura-webhook
- Go to cloud.ouraring.com
- Sign in with your Oura account
- Navigate to Applications or API Applications
- Click Create New Application
- Fill in the application details:
- Application Name: Your app name
- Description: Brief description
- Redirect URI: Your OAuth callback URL
- Webhook URL: Your webhook endpoint (can be added later)
- Note your Client ID and Client Secret
- In your application settings, locate the webhook configuration section
- Enter your webhook URL:
https://[YOUR-PROJECT-ID].supabase.co/functions/v1/oura-webhook - Note the Webhook Secret provided (for signature verification)
- Save your configuration
Set the following secrets in your Supabase project:
supabase secrets set OURA_CLIENT_ID="your-client-id-here"
supabase secrets set OURA_CLIENT_SECRET="your-client-secret-here"
supabase secrets set OURA_WEBHOOK_SECRET="your-webhook-secret-here"When you configure or update the webhook URL, Oura sends a verification challenge:
- Method: GET
- Query Parameter:
?challenge=RANDOM_STRING - Expected Response: Echo back the challenge string with HTTP 200
- Content-Type:
text/plain
The webhook function handles this automatically by returning the challenge parameter value.
Oura signs webhook payloads using HMAC-SHA256:
- Header:
X-Oura-Signature - Algorithm: HMAC-SHA256
- Key: Your
WEBHOOK_SECRET - Data: Raw request body
- Format: Base64-encoded signature
| Data Type | Description | Maps To |
|---|---|---|
sleep |
Sleep sessions | sleep sync |
daily_sleep |
Daily sleep summaries | sleep sync |
daily_activity |
Daily activity summaries | activity sync |
daily_readiness |
Daily readiness scores | readiness sync |
workout |
Workout sessions | workout sync |
session |
Activity sessions | workout sync |
daily_spo2 |
Blood oxygen data | readiness sync |
Events are delivered as an array:
[
{
"event_type": "create",
"data_type": "daily_sleep",
"user_id": "OURA_USER_ID",
"event_timestamp": "2025-01-15T08:30:00Z"
}
]- Return HTTP
200status code to acknowledge receipt - Process events asynchronously if needed
- Missing signature results in logged warning but accepted payload
| Variable | Provider | Purpose |
|---|---|---|
FITBIT_SUBSCRIBER_VERIFICATION_CODE |
Fitbit | Endpoint verification and signature validation |
WHOOP_CLIENT_SECRET |
Whoop | Webhook signature verification |
OURA_CLIENT_ID |
Oura | OAuth authentication |
OURA_CLIENT_SECRET |
Oura | OAuth authentication |
OURA_WEBHOOK_SECRET |
Oura | Webhook signature verification |
These are automatically available in Edge Functions:
| Variable | Purpose |
|---|---|
SUPABASE_URL |
Supabase project URL |
SUPABASE_SERVICE_ROLE_KEY |
Service role key for internal API calls |
Use the Supabase CLI to set secrets:
# Set individual secrets
supabase secrets set VARIABLE_NAME="value"
# Set multiple secrets from a file
supabase secrets set --env-file .env.secretsVerification Failed
- Ensure your endpoint responds within 5 seconds
- Check that correct verification code returns HTTP 204
- Check that incorrect verification code returns HTTP 404
- Verify the endpoint is publicly accessible (no auth required for verification)
Signature Validation Failed
- Confirm
FITBIT_SUBSCRIBER_VERIFICATION_CODEmatches the dashboard value - Check that you're appending
&to both the key and body before signing
Subscriber Disabled
- Fitbit automatically disables subscribers that fail to respond with HTTP 204 within 5 seconds
- Check your Edge Function logs for errors
- Re-verify the endpoint after fixing issues
Not Receiving Webhooks
- Confirm the webhook URL is saved in the dashboard
- Ensure at least one user has authenticated with your app
- Check that your endpoint returns HTTP 2XX within 1 second
Signature Mismatch
- Verify
WHOOP_CLIENT_SECRETis set correctly - Ensure you're concatenating timestamp + body (not body + timestamp)
- Check timestamp header is being read correctly
Challenge Verification Failed
- Ensure your endpoint returns the challenge string exactly as received
- Response must be plain text, not JSON
- HTTP status must be 200
Signature Validation Failed
- Confirm
OURA_WEBHOOK_SECRETis set correctly - Check that you're using the raw request body for signing
Check Edge Function Logs
supabase functions logs fitbit-webhook --tail
supabase functions logs whoop-webhook --tail
supabase functions logs oura-webhook --tailTest Endpoint Accessibility
# Test Fitbit verification (should return 404 for wrong code)
curl "https://[PROJECT-ID].supabase.co/functions/v1/fitbit-webhook?verify=wrong-code"
# Test Oura challenge (should echo back the challenge)
curl "https://[PROJECT-ID].supabase.co/functions/v1/oura-webhook?challenge=test123"Verify Secrets Are Set
supabase secrets list- Always validate signatures - Never skip signature verification in production
- Use HTTPS - All webhook URLs must use HTTPS (enforced by all providers)
- Acknowledge quickly - Return success status immediately, process asynchronously
- Log webhook events - The Edge Functions log to
ingestion_logtable for debugging - Monitor failures - Set up alerts for repeated webhook failures
- Rotate secrets - Periodically rotate webhook secrets and update all configurations
| Provider | URL Pattern |
|---|---|
| Fitbit | https://[PROJECT-ID].supabase.co/functions/v1/fitbit-webhook |
| Whoop | https://[PROJECT-ID].supabase.co/functions/v1/whoop-webhook |
| Oura | https://[PROJECT-ID].supabase.co/functions/v1/oura-webhook |
| Provider | Success | Verification Success | Verification Failure |
|---|---|---|---|
| Fitbit | 204 | 204 | 404 |
| Whoop | 200 | N/A | N/A |
| Oura | 200 | 200 (echo challenge) | 404 |
| Provider | Signature Header | Timestamp Header |
|---|---|---|
| Fitbit | X-Fitbit-Signature |
N/A |
| Whoop | X-WHOOP-Signature |
X-WHOOP-Signature-Timestamp |
| Oura | X-Oura-Signature |
N/A |