Data Flow
Introduction to Webhooks
Sahha processes millions of rows of log data with the primary objective of producing insightful inference results. If needed, we have the capacity to forward the data events we obtain to a selected endpoint of your choice.
Webhooks are the preferred approach for accessing Sahha data
Webhooks offer a real-time, efficient, and customizable method for accessing Sahha data, allowing you to only receive the specific event data you need. Enhancing data processing efficiency, security, and cost-effectiveness.
How can I use webhooks?
Webhooks can be setup within the Sahha Dashboard under the webhooks tab on the side-nav
Security
To know for sure that a webhook was, in fact, sent by Sahha instead of a malicious actor, you can verify the request signature. Each webhook request contains a header named X-Signature, and you can verify this signature by using your secret webhook key. The signature is an HMAC hash of the request payload hashed using your secret key.
If your generated signature matches the X-Signature header, you can be sure that the request was coming from Sahha.
Identifying profiles
Each payload has a header X-External-Id which contains the external identifier provided by you when registering your profile, this will allow you to identify the profile the events are related to.
Identifying events
In order to determine the appropriate event type to deserialize the payload into, you should examine the value present in the X-Event-Type header.
Events
Below are the events that will be fired to your elected URL, please review the code sample above for how to consume these correctly.
Real-Time Health Logs
X-Event-Type - DataLogReceivedIntegrationEvent
profileId StringThe identifier for the user's profile associated with this data log.
accountId StringThe identifier for the account associated with this data log.
externalId StringAn external identifier for the data log, if applicable.
receivedAtUtc StringThe timestamp when the data log was received in UTC. In ISO 8601 format.
logType StringThe type of log (e.g., Activity, Blood, Body, Device, Energy, Heart, Oxygen, Sleep, Temperature).
dataType StringThe specific type of data within the log.
dataLogs ArrayAn array of data logs.
dataLogs Properties
id StringA unique identifier for each data log.
parentId StringThe identifier for the parent log, if applicable.
value NumberThe recorded value for the data type.
unit StringThe unit of measurement for the value.
source StringIdentifies where or what device the data came from.
recordingMethod StringDescribes how the data was collected (e.g., RECORDING_METHOD_ACTIVELY_RECORDED, RECORDING_METHOD_AUTOMATICALLY_RECORDED, RECORDING_METHOD_MANUAL_ENTRY, RECORDING_METHOD_UNKNOWN).
deviceType StringThe specific type or model of the device that recorded the data.
startDateTime StringWhen the data recording started, in an ISO 8601 format.
endDateTime StringWhen the data recording ended, also in an ISO 8601 format.
additionalProperties ObjectAdditional properties associated with the data log.
[ { "logType": "activity", "dataType": "step_count", "profileId": "123e4567-e89b-12d3-a456-426614174001", "accountId": "123e4567-e89b-12d3-a456-426614174002", "externalId": "ext-789", "receivedAtUtc": "2023-06-26T12:34:56+00:00", "dataLogs": [ { "id": "123e4567-e89b-12d3-a456-426614174003", "parentId": null, "value": 10000, "unit": "count", "source": "iPhone X", "recordingMethod": "RECORDING_METHOD_AUTOMATICALLY_RECORDED", "deviceType": "iPhone13,2", "startDateTime": "2023-06-25T00:00:00+00:00", "endDateTime": "2023-06-25T23:59:59+00:00", "additionalProperties": { "context": "morning_walk" } } ] }] Digital Biomarker Logs
X-Event-Type - BiomarkerCreatedIntegrationEvent
id StringA unique identifier for each biomarker reading.
profileId StringThe identifier for the user's profile associated with this biomarker.
accountId StringThe identifier for the account associated with this biomarker.
externalId StringExternal identifier for the profile.
category StringThe category of the biomarker, such as activity, sleep, or body.
type StringThe specific type of biomarker.
periodicity StringThe frequency at which the biomarker data is generated.
aggregation StringThe method used to aggregate the biomarker data.
value StringThe numerical value of the biomarker.
unit StringThe unit of measurement for the biomarker value.
valueType StringThe type of value, indicating whether it's an integer, float, etc.
startDateTime DateTimeOffsetThe starting timestamp for the period over which the biomarker was calculated. In ISO 8601 format with a timezone offset.
endDateTime DateTimeOffsetThe ending timestamp for the period over which the biomarker was calculated. In ISO 8601 format with a timezone offset.
createdAtUtc DateTimeOffsetThe timestamp when the event was created in UTC. In ISO 8601 format.
version DoubleThe version of the event schema.
{ "id": "123e4567-e89b-12d3-a456-426614174000", "profileId": "123e4567-e89b-12d3-a456-426614174001", "accountId": "123e4567-e89b-12d3-a456-426614174002", "externalId": "ext-789", "category": "activity", "type": "steps", "periodicity": "daily", "aggregation": "total", "value": "10000", "unit": "count", "valueType": "integer", "startDateTime": "2023-06-25T00:00:00+00:00", "endDateTime": "2023-06-25T23:59:59+00:00", "createdAtUtc": "2023-06-26T12:34:56+00:00", "version": 1} Health Scores
X-Event-Type - ScoreCreatedIntegrationEvent
id stringA unique identifier for each health score.
profileId stringThe identifier for the user's profile associated with this score.
accountId stringThe identifier for the account associated with this score.
externalId stringExternal identifier for the profile.
type stringThe type of health score (e.g., Wellbeing, Activity, Sleep, Depression, Stress, Anxiety).
state stringA qualitative assessment of the score (e.g., low, medium, high).
score numberA numerical value representing the health score.
factors arrayA list of factors that influenced the score, each with a name and value.
dataSources arrayThe types of data used to calculate the score.
scoreDateTime stringThe timestamp the score was calculated for. In ISO 8601 format in the profiles timezone offset.
createdAtUtc stringThe timestamp when the score was calculated. In ISO 8601 format with a UTC timezone offset.
version numberThe version of the event schema.
{ "id": "123e4567-e89b-12d3-a456-426614174000", "profileId": "123e4567-e89b-12d3-a456-426614174001", "accountId": "123e4567-e89b-12d3-a456-426614174002", "externalId": "ext-789", "type": "activity", "state": "medium", "score": 73.5, "factors": [ { "name": "steps", "value": 10000, "goal": 12000, "score": 0.8, "state": "medium", "unit": "count" }, { "name": "active_calories", "value": 74, "goal": 500, "score": 0.19, "state": "low", "unit": "kcal" }, { "name": "active_hours", "value": 8, "goal": 10, "score": 0.85, "state": "medium", "unit": "hour" }, { "name": "extended_inactivity", "value": 925, "goal": 480, "score": 0.95, "state": "high", "unit": "minute" }, { "name": "intense_activity_duration", "value": 1, "goal": 30, "score": 0.37, "state": "minimal", "unit": "minute" }, { "name": "floors_climbed", "value": 27, "goal": 10, "score": 1, "state": "high", "unit": "count" } ], "dataSources": [ "activity" ], "createdAtUtc": "2023-06-26T12:34:56+00:00", "version": 1} Handler code examples
You can find your webhook secret key in the Sahha dashboard.
You can use the following example code to handle a webhook in C# or Python.
[HttpPost("receiver")]public async Task<IActionResult> ProcessWebhook(){ var signatureHeader = Request.Headers["X-Signature"].ToString(); if (string.IsNullOrEmpty(signatureHeader)) { return BadRequest("X-Signature header is missing."); }
var externalIdHeader = Request.Headers["X-External-Id"].ToString(); if (string.IsNullOrEmpty(externalIdHeader)) { return BadRequest("X-External-Id header is missing."); }
var eventTypeHeader = Request.Headers["X-Event-Type"].ToString(); if (string.IsNullOrEmpty(eventTypeHeader)) { return BadRequest("X-Event-Type header is missing."); }
// Read the request payload var payload = await new StreamReader(Request.Body).ReadToEndAsync();
// Compute the signature based on the provided secret // Replace with the webhook secret key found in your Sahha Dashboard var secretKey = "INPUT_YOUR_WEBHOOK_SECRET_KEY"; using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey)); var computedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload)); var signatureString = BitConverter.ToString(computedHash).Replace("-", "").ToLower();
// Compare the received signature with the expected signature if (!string.Equals(signatureHeader, signatureString, StringComparison.OrdinalIgnoreCase)) { return BadRequest("Invalid signature."); }
// Deserialize the payload based on the event type switch (eventTypeHeader) { case "DataLogReceivedIntegrationEvent": // Handle Data Log Event break; case "BiomarkerCreatedIntegrationEvent": // Handle Biomarker Event break; case "ScoreCreatedIntegrationEvent": // Handle Score Event break; default: return BadRequest($"Unsupported event type: {eventTypeHeader}"); }
// Return Ok so we know to not retry. return Ok();}