Guides

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.

ActivityLog_Received

dataType String

value Double

The amount of times the data type occurred within the time period.

unit String

The unit of measure for the value.

source String

Source of this data log. e.g. 'Iphone X', 'Walking Buddy'

recordingMethod String

The method used to record the log: RECORDING_METHOD_ACTIVELY_RECORDED (For actively recorded data by the user), RECORDING_METHOD_AUTOMATICALLY_RECORDED (For passively recorded data by the app), RECORDING_METHOD_MANUAL_ENTRY (For manually entered data by the user), RECORDING_METHOD_UNKNOWN (Unknown recording method).

deviceType String

The device which recorded the log.

startDateTime DateTimeOffset

Denotes the start date and time for the log period. In ISO 8601 format with a timezone offset.

endDateTime DateTimeOffset

Denotes the end date and time for the log period. In ISO 8601 format with a timezone offset.

[
{
"DataType": "",
"Value": 55,
"Unit": 55,
"Source": "Iphone X",
"RecordingMethod": "RECORDING_METHOD_UNKNOWN",
"DeviceType": "iPhone13,2",
"StartDateTime": "2022-01-01T00:00:00+12:00",
"EndDateTime": "2022-01-01T00:00:00+12:00"
}
]

BloodLog_Received

dataType String

The type of data being logged. Can be 'BloodGlucose', 'BloodPressureDiastolic', 'BloodPressureSystolic'

count Double

The value corresponding to the data type.

unit String

Unit the data is in, eg mg/dL (milligrams per deciliter) or mmol/L (millimoles per liter)

relationToMeal String

Relationship to when the last or next meal is.

bodyPosition String

The position the body was in at time of measurement. E.g. sitting_down

measurementLocation String

Where the blood pressure was measured from. E.g. left_upper_arm

specimenSource String

Where the specimen was collected.

mealType String

The type of meal had prior to sampling.

source String

Source of this data log. e.g. 'Iphone X'

recordingMethod String

The method used to record the log: RECORDING_METHOD_ACTIVELY_RECORDED (For actively recorded data by the user), RECORDING_METHOD_AUTOMATICALLY_RECORDED (For passively recorded data by the app), RECORDING_METHOD_MANUAL_ENTRY (For manually entered data by the user), RECORDING_METHOD_UNKNOWN (Unknown recording method).

deviceType String

The device which recorded the log.

startDateTime DateTimeOffset

Denotes the start date and time for the blood log period. In ISO 8601 format with a timezone offset.

endDateTime DateTimeOffset

Denotes the end date and time for the blood log period. In ISO 8601 format with a timezone offset.

[
{
"DataType": "BloodGlucose",
"Count": 55,
"Unit": "mg/dL",
"RelationToMeal": "Before Meal",
"BodyPosition": "Body Position",
"MeasurementLocation": "Measurement Location",
"SpecimenSource": "capillary_blood",
"MealType": "breakfast",
"Source": "Iphone X",
"RecordingMethod": "RECORDING_METHOD_UNKNOWN",
"DeviceType": "iPhone13,2",
"StartDateTime": "2022-01-01T00:00:00+12:00",
"EndDateTime": "2022-01-01T00:00:00+12:00"
}
]

BodyLog_Received

dataType String

value Double

The amount of times the data type occurred within the time period.

unit String

The unit of measure for the value.

source String

Source of this data log. e.g. 'Iphone X', 'Walking Buddy'

recordingMethod String

The method used to record the log: RECORDING_METHOD_ACTIVELY_RECORDED (For actively recorded data by the user), RECORDING_METHOD_AUTOMATICALLY_RECORDED (For passively recorded data by the app), RECORDING_METHOD_MANUAL_ENTRY (For manually entered data by the user), RECORDING_METHOD_UNKNOWN (Unknown recording method).

deviceType String

The device which recorded the log.

startDateTime DateTimeOffset

Denotes the start date and time for the log period. In ISO 8601 format with a timezone offset.

endDateTime DateTimeOffset

Denotes the end date and time for the log period. In ISO 8601 format with a timezone offset.

[
{
"DataType": "",
"Value": 55,
"Unit": 55,
"Source": "Iphone X",
"RecordingMethod": "RECORDING_METHOD_UNKNOWN",
"DeviceType": "iPhone13,2",
"StartDateTime": "2022-01-01T00:00:00+12:00",
"EndDateTime": "2022-01-01T00:00:00+12:00"
}
]

Insight_Received

name String

The name of the insight.

value Double (nullable)

The value of the insight, this is the quantity of the unit.

unit String

The unit the value is measured in.

startDateTime DateTimeOffset (nullable)

The starting point for the insight window (If applicable).

endDateTime DateTimeOffset (nullable)

The ending point for the insight window (If applicable).

[
{
"Name": "TotalDailySteps",
"Value": 1000,
"Unit": "Count",
"StartDateTime": "2022-01-01T00:00:00+12:00",
"EndDateTime": "2022-01-02T00:00:00+12:00"
}
]

DeviceLog_Received

isLocked Boolean

Denotes whether the users device is locked or unlocked at the time specified.

isScreenOn Boolean

Denotes whether the users device screen is on at the time specified. This distinguishes between a user waking a phone or fully unlocking it.

source String

Source of this data log. e.g. 'Iphone X'

recordingMethod String

The method used to record the log: RECORDING_METHOD_ACTIVELY_RECORDED (For actively recorded data by the user), RECORDING_METHOD_AUTOMATICALLY_RECORDED (For passively recorded data by the app), RECORDING_METHOD_MANUAL_ENTRY (For manually entered data by the user), RECORDING_METHOD_UNKNOWN (Unknown recording method).

deviceType String

The device which recorded the log.

eventTimeStamp DateTimeOffset

Date and time the event occurred. In ISO 8601 format with a timezone offset.

[
{
"IsLocked": true,
"IsScreenOn": false,
"Source": "Iphone X",
"RecordingMethod": "RECORDING_METHOD_UNKNOWN",
"DeviceType": "iPhone13,2",
"EventTimestamp": "2022-01-01T00:00:00+12:00"
}
]

EnergyLog_Received

dataType String

value Double

The amount of times the data type occurred within the time period.

unit String

The unit of measure for the value.

source String

Source of this data log. e.g. 'Iphone X', 'Walking Buddy'

recordingMethod String

The method used to record the log: RECORDING_METHOD_ACTIVELY_RECORDED (For actively recorded data by the user), RECORDING_METHOD_AUTOMATICALLY_RECORDED (For passively recorded data by the app), RECORDING_METHOD_MANUAL_ENTRY (For manually entered data by the user), RECORDING_METHOD_UNKNOWN (Unknown recording method).

deviceType String

The device which recorded the log.

startDateTime DateTimeOffset

Denotes the start date and time for the log period. In ISO 8601 format with a timezone offset.

endDateTime DateTimeOffset

Denotes the end date and time for the log period. In ISO 8601 format with a timezone offset.

[
{
"DataType": "",
"Value": 55,
"Unit": 55,
"Source": "Iphone X",
"RecordingMethod": "RECORDING_METHOD_UNKNOWN",
"DeviceType": "iPhone13,2",
"StartDateTime": "2022-01-01T00:00:00+12:00",
"EndDateTime": "2022-01-01T00:00:00+12:00"
}
]

HeartLog_Received

dataType String

The type of data being logged. Can be 'HeartRate', 'RestingHeartRate', 'HeartRateVariablity'

count Double

The value corresponding to the data type.

source String

Source of this data log. e.g. 'Iphone X'

recordingMethod String

The method used to record the log: RECORDING_METHOD_ACTIVELY_RECORDED (For actively recorded data by the user), RECORDING_METHOD_AUTOMATICALLY_RECORDED (For passively recorded data by the app), RECORDING_METHOD_MANUAL_ENTRY (For manually entered data by the user), RECORDING_METHOD_UNKNOWN (Unknown recording method).

deviceType String

The device which recorded the log.

startDateTime DateTimeOffset

Denotes the start date and time for the heart log period. In ISO 8601 format with a timezone offset.

endDateTime DateTimeOffset

Denotes the end date and time for the heart log period. In ISO 8601 format with a timezone offset.

[
{
"DataType": "HeartRate",
"Count": 55,
"Source": "Iphone X",
"RecordingMethod": "RECORDING_METHOD_UNKNOWN",
"DeviceType": "iPhone13,2",
"StartDateTime": "2022-01-01T00:00:00+12:00",
"EndDateTime": "2022-01-01T00:00:00+12:00"
}
]

Inference_Created

id Guid

Unique identifier for the inference.

type String

Specifies the type of score being reported, such as stress_resilience or depression_resilience. The type informs the client about which aspect of health the inference relates to.

state String

Indicates the category of the score in a descriptive manner. Categories include none, low, medium, or high. This field provides a qualitative summary of the corresponding score.

score Double

The numerical value of the health score, ranging between 0 and 1. Higher scores typically indicate better mental and physical well-being based on the features analyzed.

factors List`1

A list of objects that break down the score contributions of different features such as sleep_routine, daily_activity, etc. Each object in the list specifies how much a particular feature has contributed to the final score.

inputData String[]

Lists the features that were used to generate the inference. This can include features like age, gender, sleep, and activity.

createdAt DateTimeOffset

Date the inference was generated.

{
"Id": "2F9EA4F8-8A96-4AC2-88B5-001EE6FC2CCD",
"Type": "stress_resilience",
"State": "low",
"Score": "0.88",
"Factors": [
{
"name": "sleep_routine",
"value": "0.12"
}
],
"InputData": [
"age",
"sleep",
"activity"
],
"CreatedAt": "2023-01-01 00:00:00.0000000 +00:00"
}

MovementLog_Received

dataType String

The type of movement being logged. Can be 'StepCount', 'FloorsDescended', 'FloorsAscended', 'Pace'

count Double

The amount of times the data type occurred within the time period.

source String

Source of this data log. e.g. 'Iphone X', 'Walking Buddy'

recordingMethod String

The method used to record the log: RECORDING_METHOD_ACTIVELY_RECORDED (For actively recorded data by the user), RECORDING_METHOD_AUTOMATICALLY_RECORDED (For passively recorded data by the app), RECORDING_METHOD_MANUAL_ENTRY (For manually entered data by the user), RECORDING_METHOD_UNKNOWN (Unknown recording method).

deviceType String

The device which recorded the log.

startDateTime DateTimeOffset

Denotes the start date and time for the movement log period. In ISO 8601 format with a timezone offset.

endDateTime DateTimeOffset

Denotes the end date and time for the movement log period. In ISO 8601 format with a timezone offset.

[
{
"DataType": "StepCount",
"Count": 55,
"Source": "Iphone X",
"RecordingMethod": "RECORDING_METHOD_UNKNOWN",
"DeviceType": "iPhone13,2",
"StartDateTime": "2022-01-01T00:00:00+12:00",
"EndDateTime": "2022-01-01T00:00:00+12:00"
}
]

OxygenLog_Received

dataType String

value Double

The amount of times the data type occurred within the time period.

unit String

The unit of measure for the value.

source String

Source of this data log. e.g. 'Iphone X', 'Walking Buddy'

recordingMethod String

The method used to record the log: RECORDING_METHOD_ACTIVELY_RECORDED (For actively recorded data by the user), RECORDING_METHOD_AUTOMATICALLY_RECORDED (For passively recorded data by the app), RECORDING_METHOD_MANUAL_ENTRY (For manually entered data by the user), RECORDING_METHOD_UNKNOWN (Unknown recording method).

deviceType String

The device which recorded the log.

startDateTime DateTimeOffset

Denotes the start date and time for the log period. In ISO 8601 format with a timezone offset.

endDateTime DateTimeOffset

Denotes the end date and time for the log period. In ISO 8601 format with a timezone offset.

[
{
"DataType": "",
"Value": 55,
"Unit": 55,
"Source": "Iphone X",
"RecordingMethod": "RECORDING_METHOD_UNKNOWN",
"DeviceType": "iPhone13,2",
"StartDateTime": "2022-01-01T00:00:00+12:00",
"EndDateTime": "2022-01-01T00:00:00+12:00"
}
]

SleepLog_Received

durationInMinutes Double

The Length of time in minutes the user was asleep for the given period.

sleepStage String

The stage of sleep the user was in. Can be one of 'in bed', 'asleep', 'awake'.

source String

Source of this data log. e.g. 'Iphone X', 'Walking Buddy'

recordingMethod String

The method used to record the log: RECORDING_METHOD_ACTIVELY_RECORDED (For actively recorded data by the user), RECORDING_METHOD_AUTOMATICALLY_RECORDED (For passively recorded data by the app), RECORDING_METHOD_MANUAL_ENTRY (For manually entered data by the user), RECORDING_METHOD_UNKNOWN (Unknown recording method).

deviceType String

The device which recorded the log.

startDateTime DateTimeOffset

Denotes the start date and time for the sleep period. In ISO 8601 format with a timezone offset.

endDateTime DateTimeOffset

Denotes the end date and time for the sleep period. In ISO 8601 format with a timezone offset.

[
{
"DurationInMinutes": 60,
"SleepStage": "asleep",
"Source": "Iphone X",
"RecordingMethod": "RECORDING_METHOD_UNKNOWN",
"DeviceType": "iPhone13,2",
"StartDateTime": "2022-07-01T10:00:00+09:00",
"EndDateTime": "2022-07-01T11:00:00+09:00"
}
]

Handler examples

[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
var secretKey = "p+UnYW0MpNSonDdnr3sDH/ZRCOxs1etrjV57wC8yQWA=";
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 "BloodLog_Received":
var bloodLog = JsonConvert.DeserializeObject<List<BloodLogDto>>(payload);
break;
case "HeartLog_Received":
var heartLog = JsonConvert.DeserializeObject<List<HeartLogDto>>(payload);
break;
case "DeviceLog_Received":
var deviceLog = JsonConvert.DeserializeObject<List<DeviceLogDto>>(payload);
break;
case "MovementLog_Received":
var movementLog = JsonConvert.DeserializeObject<List<MovementLogDto>>(payload);
break;
case "SleepLog_Received":
var sleepLog = JsonConvert.DeserializeObject<List<SleepLogDto>>(payload);
break;
case "Inference_Created":
var inference = JsonConvert.DeserializeObject<InferenceDto>(payload);
break;
// Process other Events
default:
return BadRequest($"Unsupported event type: {eventTypeHeader}");
}
// Return Ok so we know to not retry.
return Ok();
}
Previous
UI widgets