Skip to content

Overview

Webhooks for Real-Time Data Updates

What are Webhooks?

Webhooks are a way for the Oura API to notify your application when new data is available, instead of requiring your application to constantly check for updates (polling). Think of webhooks as “reverse APIs” - instead of your application requesting data, Oura’s servers send data to your application when something changes.

Why Use Webhooks (Important!)

  • RECOMMENDED APPROACH: Webhooks are the preferred way to consume Oura data
  • Avoid Rate Limits: We have not had customers hit rate limits with webhooks properly implemented
  • Near Real-Time Updates: Webhook notifications come approximately 30 seconds after data syncs from the mobile app
  • Efficient Resource Usage: Reduces unnecessary API calls and server load
  • Better User Experience: Your application stays updated without constant polling

How Webhooks Work with Oura

  1. You set up an endpoint: Create a URL on your server that can receive POST requests
  2. You subscribe to events: Tell Oura what data types and events you want to be notified about
  3. Oura verifies your endpoint: A one-time check to ensure your endpoint is valid
  4. Oura sends notifications: When data changes, Oura sends a POST request to your endpoint
  5. You process the event: Your endpoint receives basic event details
  6. You fetch complete data: Use the provided IDs to retrieve the full data via the API
  1. Initial Data Load: When a user first connects, make a single API request for historical data
  2. Subscribe to Webhooks: Set up webhook subscriptions for all data types you need
  3. Process Webhook Events: As users sync their rings, you’ll receive notifications about new data
  4. Fetch Updated Data: Use the object_id from webhook events to fetch the specific updated data

This pattern minimizes API calls while ensuring your application always has the latest data.

Setup Guide

Step 1: Create Your Webhook Endpoint

Set up an HTTP endpoint on your server that can:

  • Handle both GET requests (for verification) and POST requests (for events)
  • Respond to verification challenges during subscription setup
  • Process incoming webhook events quickly (under 10 seconds)

Example endpoint implementation (Node.js):

// Express.js route handlers for your webhook endpoint
app.get('/oura-webhook', (req, res) => {
  // Verification handler - required during subscription setup
  const { verification_token, challenge } = req.query;

  // Verify the token matches your expected token
  if (verification_token === YOUR_VERIFICATION_TOKEN) {
    // Return the challenge in the required format
    return res.json({ challenge });
  }

  // If verification fails
  return res.status(401).send('Invalid verification token');
});

app.post('/oura-webhook', (req, res) => {
  // Event handler - processes incoming webhook events

  // Always respond quickly (under 10 seconds)
  // Process the event asynchronously if needed
  res.status(200).send('OK');

  // Then process the event data
  const { event_type, data_type, object_id, user_id } = req.body;
  processEventAsync(event_type, data_type, object_id, user_id);
});

Step 2: Create a Webhook Subscription

Call the POST /v2/webhook/subscription endpoint to register your webhook:

POST /v2/webhook/subscription
Headers:
  x-client-id: YOUR_CLIENT_ID
  x-client-secret: YOUR_CLIENT_SECRET
  Content-Type: application/json

Body:
{
  "callback_url": "https://your-server.com/oura-webhook",
  "verification_token": "your-secret-verification-token",
  "event_type": "update",
  "data_type": "sleep"
}

You need to create separate subscriptions for each combination of:

  • event_type: The type of event (create, update, delete)
  • data_type: The type of data you’re interested in (sleep, activity, etc.)

Step 3: Verification Process

When you create a subscription, Oura verifies your endpoint:

  1. Oura sends a GET request to your callback URL with query parameters:

    GET https://your-server.com/oura-webhook?verification_token=your-token&challenge=random-string
  2. Your endpoint must verify the token and respond with the challenge:

    {
      "challenge": "random-string"
    }
  3. If verification succeeds, your subscription is activated

Verification Flow

Step 4: Receiving and Processing Events

When an event occurs (e.g., user syncs new sleep data):

  1. Oura sends a POST request to your callback URL:

    POST https://your-server.com/oura-webhook
    Headers:
      x-oura-signature: HMAC_SIGNATURE
      x-oura-timestamp: 1234567890
    
    Body:
    {
      "event_type": "update",
      "data_type": "sleep",
      "object_id": "12345abc",
      "event_time": "2023-01-01T08:00:00+00:00",
      "user_id": "user123"
    }
  2. Your endpoint should:

    • Verify the signature for security (see below)
    • Respond quickly (under 10 seconds) with a 2xx status
    • Process the event asynchronously if needed
    • Use the object_id to fetch the complete data via the API

Security Best Practices

Verify Webhook Signatures

Always verify that webhook requests are actually from Oura by checking the HMAC signature:

const crypto = require('crypto');

function verifySignature(headers, body, clientSecret) {
  const signature = headers['x-oura-signature'];
  const timestamp = headers['x-oura-timestamp'];

  // Create HMAC using your client secret
  const hmac = crypto.createHmac('sha256', clientSecret);
  hmac.update(timestamp + JSON.stringify(body));
  const calculatedSignature = hmac.digest('hex').toUpperCase();

  // Compare calculated signature with received signature
  return calculatedSignature === signature;
}

// In your webhook handler
app.post('/oura-webhook', (req, res) => {
  // Verify signature
  if (!verifySignature(req.headers, req.body, CLIENT_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  // Process valid webhook
  res.status(200).send('OK');
  // ...
});

Use HTTPS

Always use HTTPS for your webhook endpoint to ensure data is encrypted in transit.

Keep Your Verification Token Secret

Choose a strong, random verification token and don’t share it.

Handling Webhook Failures

Retry Mechanism

Oura will retry failed webhook deliveries:

  • For 4xx responses: 10 retries
  • For 5xx responses: 10 retries
  • For timeouts: 10 retries

Canceling Subscriptions

If you want to cancel a subscription, you can:

  • Use the DELETE endpoint: DELETE /v2/webhook/subscription/{id}
  • Or respond with a 410 status code to automatically cancel

Common Questions

How quickly will I receive webhooks?

Webhook notifications arrive approximately 30 seconds after data syncs from the mobile app. The timing depends on the data type:

  • Sleep, Readiness, and other user-initiated sync data: These only sync when the user opens the Oura app and actively syncs their ring
  • Daily Activity, Daily Stress, and other background data: These may update periodically in the background without user action

What if my server goes down?

Oura will retry webhook deliveries for about an hour if your server doesn’t respond properly. However, if your server is down for an extended period, you might miss some events. It’s a good practice to implement a reconciliation process that can fetch data for periods when your webhook might have been unavailable.

How can I test webhooks locally?

Use a tool like ngrok to expose your local development server to the internet with a public URL.

Can I use the same callback URL for different subscriptions?

Yes, you can use the same URL for multiple subscriptions. Your handler can differentiate between events using the event_type and data_type fields in the webhook payload.

Will I hit rate limits using webhooks?

We have not had customers hit rate limits with webhooks properly implemented. The recommended pattern is:

  1. Make a single request for historical data when a user first connects
  2. Use webhooks for all ongoing data updates
  3. Only fetch the specific data that has changed based on webhook notifications

This approach minimizes API calls while ensuring your application always has the latest data.