Introduction

Webhooks push data to your server automatically whenever new events occur—no polling required. Configure endpoints and manage delivery on the Webhooks page in the Dashboard, or browse payload schemas in the Event Reference.


Event Types

Event TypeDescriptionSchema
ScoreCreatedIntegrationEventHealth score generated (activity, sleep, wellbeing, readiness, etc.)View
BiomarkerCreatedIntegrationEventBiomarker value calculated (steps, heart rate, sleep duration, etc.)View
ArchetypeCreatedIntegrationEventArchetype assignment (activity level, sleep pattern, chronotype, etc.)View
TagReceivedIntegrationEventTag received (symptom, medication, shift, subscription, etc.)View
DataLogReceivedIntegrationEventRaw health data received from user's deviceView

View Event Reference


Setup

  1. Go to the Sahha Dashboard
  2. Navigate to Webhooks in the sidebar
  3. Add your endpoint URL (must be HTTPS)
  4. Select which event types you want to receive
  5. Copy your secret key for signature verification

Testing your endpoint

Use the Send Test Event button in the dashboard to verify your endpoint is receiving and processing webhooks correctly before going live.


Delivery

Interval

Scores and biomarkers update throughout the day as new data arrives. To control how frequently you receive these updates, configure the webhook interval in the dashboard.

The interval acts as a deduplication window. When a score or biomarker is delivered, subsequent updates for the same metric and profile are held until the interval expires. If multiple updates occur within the window, only the final value is sent when the interval ends.

IntervalBehavior
Real-timeEvery update delivered immediately
1 min – 720 minUpdates batched; final value sent at interval end

Data Logs and Tags are always real-time

The delivery interval only applies to scores and biomarkers. Data log events (DataLogReceivedIntegrationEvent) and tag events (TagReceivedIntegrationEvent) are always delivered immediately since each one carries new incoming data rather than an updated value.

Behavior

AspectDetails
TimeoutSahha waits up to 30 seconds for a response
Success codes2xx status codes indicate successful delivery
RetriesFailed deliveries are retried up to 5 times with exponential backoff
OrderingEvents may arrive out of order—use timestamps for sequencing

Handling Requests

Every webhook request includes these headers:

HeaderDescription
X-SignatureHMAC-SHA256 hash of the payload using your secret key
X-External-IdThe profile's external identifier—use this to associate the event with a user in your system
X-Event-TypeThe event type string—use this to determine how to parse the payload

Verifying Signatures

To ensure a webhook request is genuinely from Sahha (and not a malicious actor), verify the X-Signature header. Compute an HMAC-SHA256 hash of the raw request body using your secret key, then compare it to the signature in the header. If they match, the request is authentic.

Handler Example

Webhook handler with signature verification
const express = require('express');
const crypto = require('crypto');

const app = express();
app.use(express.text({ type: '*/*' }));

const SECRET_KEY = process.env.SAHHA_WEBHOOK_SECRET;

app.post('/webhook', (req, res) => {
	const signature = req.get('X-Signature');
	const externalId = req.get('X-External-Id');
	const eventType = req.get('X-Event-Type');

	// Validate headers
	if (!signature || !externalId || !eventType) {
		return res.status(400).json({ error: 'Missing required headers' });
	}

	// Verify signature
	const hmac = crypto.createHmac('sha256', SECRET_KEY);
	const computed = hmac.update(req.body).digest('hex');
	if (signature.toLowerCase() !== computed.toLowerCase()) {
		return res.status(401).json({ error: 'Invalid signature' });
	}

	// Process event (do heavy work async to respond quickly)
	const payload = JSON.parse(req.body);
	processEventAsync(eventType, externalId, payload);

	res.status(200).json({ received: true });
});

app.listen(3000);

Best Practices

  • Always verify signatures — Never process a webhook without validating X-Signature first
  • Respond quickly — Return 200 OK immediately, handle processing asynchronously
  • Handle duplicates — Use the event id field for idempotency in case of retries
  • Use HTTPS — Your endpoint must use TLS encryption
  • Secure your secret — Store in environment variables, never hardcode in your application

Support

Need help? Contact support@sahha.ai or join the Slack Community.

Docs