Webhooks Integration
General
We provide the possibility within our Tributech Node for external services to receive a wide variety of notifications about internal events via Webhooks. A webhook is an HTTP-based callback function that enables lightweight, event-driven communication between a Tributech Node and a client. The only requirement for a client is to provide a HTTP POST endpoint that returns Status code 200 if an event was successfully received (see Error Handling for more details). In the following section, we will demonstrate how to setup a Tributech Node to send events to an client and how a user can verify that the received event data has not been tampered with.
Webhook Subscription Management
In order to receive information about a Tributech Node Event we need to create a webhook subscription.
A webhook subscription defines the clients HTTP POST endpoint, a selection of Events and a secret to sign the event data.
In this section we will show how to create and manage a webhook subscription for a specific event in the Tributech Node Webhook
Section.
Create
We can create a Subscription by simple clicking the ADD SUBSCRIPTION
button on the right side of the Tributech Node Webhook
Section.
In the following Dialog Box we add our clients HTTP POST endpoint (e.g. Webhook.site), choose the desired Event Types and add a secret for the event information verification. Its important to note that the secret can later only be updated and not retrieved.
In this example we subscribe to the Events SourceCommandRequestEvent
, SourceCommandResponseEvent
and SourceCommandStateUpdateEvent
that occurs when interacting with Source commands that we can trigger within our Quickstart Example (more information about events can be found Events).
Edit
If we want to update the settings for a Webhook subscription, we can do that via the three dots in the actions column.
This way the events, webhook url or secret can be adjusted for the current Subscription.
Deactivate
We can disable the previously created Webhook by clicking the three dots in the action colum and choosing Toggle Active
button to pause the notifications.
Deletion
If we want to delete a Webhook subscription we can do that via the three dots in the actions column.
Webhook Events
A Webhook Event is a json
object that is generated by a Tributech Node containing Event specific information when a certain condition is met.
This json object
is send within a HTTP object to a Webhook Subscription previously defined by a User. In this section we will go into detail which events are supported and what information can be retrieved from it.
Headers
Webhooks send by the Tributech Node are HTTP objects that contain default HTTP information and some Tributech specific attributes, which we will discuss in the following section. The following Headers are included in the HTTP response and contain Tributech specific information:
Attribute | Description | Sample Value |
---|---|---|
x-tributech-correlationid | A trace id to track the data through our node | 00000000-0000-0000-0000-000000000000 |
x-tributech-eventid | unique identifier through the node (guid) to identify the event | 49acaa7b-fa72-4863-ab4b-7933fedeb59a |
x-tributech-event | event name to identify the type of event | ValueReceivedEvent |
x-tributech-signaturetimestamp | creation timestamp utc of the signature (timestamp format: ISO 8601) | 06/16/2023 05:08:46 +00:00 |
x-tributech-signature | signature of the webhook event | sha256=0316443119ED851EE18AA6176E8281E6A28 D02A52EAF5DC85D45A6149173F412 |
x-tributech-timestamp | event timestamp utc (timestamp format: ISO 8601) | 06/16/2023 05:08:43 +00:00 |
Event Types
When creating a Webhook subscription, we can choose what type of events we want to receive. We will describe in the following section the Events based on type of information they are providing, e.g. Device, Proof, Stream,.. .
Its important to note that each of the Event Types contain a predefined property EventQoS
which will be contained in each
webhook event indicating the frequency of the events. This property is used by the redelivery mechanism to know how often a event should be handled in case
the HTTP POST client response with an error code (more details in Error Handling).
Device Events
Device Events are triggered by interactions with devices and device status changes, this includes enrollments, configuration and source commands.
Name | Description |
---|---|
DeviceConnectionEvent | Occurs when a Device has changed its connection status |
DeviceStateEvent | Occurs when a Device has changed its own state |
GetConfigurationRequestEvent | Occurs when the Get Configuration Command has been requested |
GetConfigurationResponseEvent | Occurs when the Get Configuration Command has finished and the response was received |
IncomingEnrollmentRequestEvent | Occurs when an Enrollment request was received |
SetConfigurationRequestEvent | Occurs when the Set Configuration Command has been requested |
SetConfigurationResponseEvent | Occurs when the Set Configuration Command has finished and the response was received |
SourceCommandRequestEvent | Occurs when a Source Command is invoked and sent to the Agent |
SourceCommandResponseEvent | Occurs when the Source Command has finished and the response was received |
SourceCommandStateUpdateEvent | Occurs when a Agent or Agent Source has sent a execution state of the current Command |
Proof Events
Proof Events describe events related to proof handling and status, e.g. received, valid, stored.
Name | Description |
---|---|
ProofConfirmedEvent | Occurs when the Proof is successfully placed on the BlockChain |
ProofReceivedEvent | Occurs when a Proof is received from a Device |
ProofStoredEvent | Occurs when the Proof is stored into the Persistence Layer and passed into the Block Chain |
ProofValidatedEvent | Occurs when the Proof is validated |
Stream Events
Stream Events provide information when stream management operations are executed.
Name | Description |
---|---|
StreamDeletedEvent | Occurs when a Agent or Stream is deleted and will be removed from the Node |
Twin Events
Twin Events provide information when Digital Twin management operations are executed.
Name | Description |
---|---|
TwinInstanceDeletedEvent | Occurs when a Digital Twin Instance was removed from the Node |
TwinModelDeletedEvent | Occurs when a Digital Twin Model was removed from the Node |
Value Events
Value Events are triggered when the Tributech Node interacts with Stream Values.
Name | Description |
---|---|
ValueReceivedEvent | Occurs when a Value is received from an Device |
ValueStoredEvent | Occurs when a Value is stored into the Persistence Layer |
Error Handling
The error handling of webhook events is based on the status code of the HTTP POST responses to the clients HTTP POST endpoint. Commonly errors occur in the following situations:
- Network is not available
- Endpoint described within the subscription is not reachable
- Endpoint returns something else then a HTTP 200 (OK)
In the case of an error during Webhook Event delivery we implemented an back-off redelivery mechanism that calculates retries based on the formula (Attempt Count + 0,7) ^ 4 + Minimum Retry Interval
. The Attempt Count
in this formula is dependent on the EventQoS
property value (contained in ever webhook event) which is predefined and cannot be changed.
A value of 1
indicates that the event is a high frequency event (like data received) and a 2
marks standard frequency events.
- High frequency events will not be retried
- Standard Frequency events have 10 retries before they are discarded.
Verification
In order to verify that the received Webhook Event has not been tempered with can verify the event using either OpenSSL or Microsoft C#. We will show in this section how to use both approaches with examples based on the previously created webhook. We will use an arbitrary secret foobar
and choose as event ProofStoredEvent
(see Event Types) and assume that a Tributech Source is already successfully sending data to a Tributech Node (see QuickStarter Guide).
In our examples we use the free Webhook client Webhook.site to provide the HTTP POST endpoint for the subscription. We will use the website to inspect the received events json
payload and HTTP Header attributes. The URL https://webhook.site/#!/view/c445d2cb-bbcb-4db6-b71c-04ca220eea6d
in our samples needs to be adjusted to your HTTP POST endpoint. Note that Webhook.site limits the amount of events you can receive and high frequency events will reach those limits quickly.
Make sure that the Webhook Subscription is configured to listen to ProofStoredEvent
events. Shortly after starting the Source and activating the Webhook Subscription we should receive our first events from an active Stream:
On Webhook.site we see the Tributech Headers with the following headers (excerpt) on the right side:
Headers | |
---|---|
x-tributech-webhook-version | 2.0.0 |
x-tributech-event | ProofStoredEvent |
x-tributech-signaturetimestamp | 2024-05-28T06:31:14.851318+00:00 |
x-tributech-signature | sha256=065CF4E993CF1DF7399B2DF64A147567552EB4BB7DD91ACC73840D5B8411B940 |
The Headers contains the Tributech specific Attribute x-tributech-signature
and x-tributech-signaturetimestamp
which we will use in the next section to verify that the Payload was not tempered with. The payload itself contains the EventQoS
flag for Standard Frequency (see Error Handling) and some information about the Agent, Stream and the Proof. Important to note is that in this example the Root hash of the MerkleTree and the Signature of the RootHash are BASE64
encoded strings in byte array form.
{
"TransactionId":null,
"StreamId":"15caf997-2f2f-43c2-b5e9-6a3ac00b1edb",
"MerkleTreeDepth":5,
"LastTimestamp":"2024-05-28T06:31:14.851318+00:00",
"RootHash":[199,51,64,165,193,234,157,113,156,124,186,212,182,137,146,243,6,114,177,215,234,61,38,199,138,227,179,195,27,237,88,198],
"Signature":[61,212,2,199,55,23,184,147,251,18,36,229,59,39,152,32,196,18,189,170,34,162,147,255,69,59,56,31,63,221,41,226,20,128,238,106,148,148,192,217,48,16,67,197,217,118,56,232,167,63,229,27,247,105,208,79,185,193,254,26,17,200,186,154,192,117,153,60,81,232,196,251,28,128,151,44,32,2,41,188,252,224,241,46,172,77,247,81,61,247,32,220,166,141,107,150,149,230,206,5,114,182,111,202,245,217,172,90,178,75,143,113,43,225,34,4,127,85,167,116,150,114,35,47,124,66,196,194,58,114,12,73,253,13,68,181,11,151,152,212,171,189,146,5,187,71,111,212,88,228,246,246,14,33,88,114,151,17,71,23,231,152,56,219,108,220,60,226,241,53,77,104,153,98,200,127,77,153,160,138,245,76,204,171,68,52,236,169,8,238,46,18,58,231,196,104,77,34,190,14,85,41,220,39,180,117,22,77,18,20,229,55,25,170,22,151,157,254,9,199,52,205,71,51,67,135,229,143,248,27,45,19,141,72,72,43,215,87,85,243,93,186,53,202,186,96,120,202,188,215,125,80,115,120,143,97],
"AgentId":"a324b439-c03b-4e0f-a63e-8067c4423b7b",
"EventQoS":1
}
Openssl
With the following OpenSSL command we verify the previous example values of the x-tributech-signature
HMAC SHA256 signature on a unix system:
echo -n '<event-json><signature-timestamp>' | openssl dgst -sha256 -hmac "<webhook-secret>"
In order to work we need to replace the placeholders with concrete values
event-json
- The received raw json content from the webhook eventsignature-timestamp
- The Header attributex-tributech-signaturetimestamp
valuewebhook-secret
- the secret used to create the webhook in the Tributech Node UI
Explicit Example:
echo -n '{"TransactionId":null,"StreamId":"15caf997-2f2f-43c2-b5e9-6a3ac00b1edb","MerkleTreeDepth":5,"LastTimestamp":"2024-05-28T06:31:14.851318+00:00","RootHash":[199,51,64,165,193,234,157,113,156,124,186,212,182,137,146,243,6,114,177,215,234,61,38,199,138,227,179,195,27,237,88,198],"Signature":[61,212,2,199,55,23,184,147,251,18,36,229,59,39,152,32,196,18,189,170,34,162,147,255,69,59,56,31,63,221,41,226,20,128,238,106,148,148,192,217,48,16,67,197,217,118,56,232,167,63,229,27,247,105,208,79,185,193,254,26,17,200,186,154,192,117,153,60,81,232,196,251,28,128,151,44,32,2,41,188,252,224,241,46,172,77,247,81,61,247,32,220,166,141,107,150,149,230,206,5,114,182,111,202,245,217,172,90,178,75,143,113,43,225,34,4,127,85,167,116,150,114,35,47,124,66,196,194,58,114,12,73,253,13,68,181,11,151,152,212,171,189,146,5,187,71,111,212,88,228,246,246,14,33,88,114,151,17,71,23,231,152,56,219,108,220,60,226,241,53,77,104,153,98,200,127,77,153,160,138,245,76,204,171,68,52,236,169,8,238,46,18,58,231,196,104,77,34,190,14,85,41,220,39,180,117,22,77,18,20,229,55,25,170,22,151,157,254,9,199,52,205,71,51,67,135,229,143,248,27,45,19,141,72,72,43,215,87,85,243,93,186,53,202,186,96,120,202,188,215,125,80,115,120,143,97],"AgentId":"a324b439-c03b-4e0f-a63e-8067c4423b7b","EventQoS":1}2024-05-28T06:31:37.3121930+00:00' | openssl dgst -sha256 -hmac "foobar"
We can now compare the output SHA2-256(stdin)= 065cf4e993cf1df7399b2df64a147567552eb4bb7dd91acc73840d5b8411b940
of the command with the value of the Webhook Header attribute x-tributech-signature
which does have matching hexadecimal values.
C# Code
In the following section we use the previous example values to verify the HMAC SHA256 signatures by using MS C# which can be pasted into Fiddle or Locale Development Environment:
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Globalization;
public class Program
{
public static void Main()
{
Console.WriteLine(HashHMAC("foobar",@"{""TransactionId"":null,""StreamId"":""15caf997-2f2f-43c2-b5e9-6a3ac00b1edb"",""MerkleTreeDepth"":5,""LastTimestamp"":""2024-05-28T06:31:14.851318+00:00"",""RootHash"":[199,51,64,165,193,234,157,113,156,124,186,212,182,137,146,243,6,114,177,215,234,61,38,199,138,227,179,195,27,237,88,198],""Signature"":[61,212,2,199,55,23,184,147,251,18,36,229,59,39,152,32,196,18,189,170,34,162,147,255,69,59,56,31,63,221,41,226,20,128,238,106,148,148,192,217,48,16,67,197,217,118,56,232,167,63,229,27,247,105,208,79,185,193,254,26,17,200,186,154,192,117,153,60,81,232,196,251,28,128,151,44,32,2,41,188,252,224,241,46,172,77,247,81,61,247,32,220,166,141,107,150,149,230,206,5,114,182,111,202,245,217,172,90,178,75,143,113,43,225,34,4,127,85,167,116,150,114,35,47,124,66,196,194,58,114,12,73,253,13,68,181,11,151,152,212,171,189,146,5,187,71,111,212,88,228,246,246,14,33,88,114,151,17,71,23,231,152,56,219,108,220,60,226,241,53,77,104,153,98,200,127,77,153,160,138,245,76,204,171,68,52,236,169,8,238,46,18,58,231,196,104,77,34,190,14,85,41,220,39,180,117,22,77,18,20,229,55,25,170,22,151,157,254,9,199,52,205,71,51,67,135,229,143,248,27,45,19,141,72,72,43,215,87,85,243,93,186,53,202,186,96,120,202,188,215,125,80,115,120,143,97],""AgentId"":""a324b439-c03b-4e0f-a63e-8067c4423b7b"",""EventQoS"":1}", DateTimeOffset.Parse("2024-05-28T06:31:37.3121930+00:00") ));
}
public static string HashHMAC(string secret, string webHookPayloadJson, DateTimeOffset signatureTimestamp) {
var encoding = new System.Text.ASCIIEncoding();
var messageHash = new HMACSHA256(encoding.GetBytes(secret));
var payload = encoding.GetBytes(webHookPayloadJson)
.Concat(encoding.GetBytes(signatureTimestamp.ToString(CultureInfo.InvariantCulture)))
.ToArray();
var signature = messageHash.ComputeHash(payload);
return BitConverter.ToString(signature).Replace("-", "");
}
}
secret
- The webhook secret defined during the creationwebHookPayloadJson
- The whole received Json objectsignature-timestamp
- The Header attributex-tributech-signaturetimestamp
value
This code example can be used to output the exact same signature which is present in the header (without the sha256=
prefix, i.e. D633514A1CE9688E816F33B2A6A48E08ED6FE621246483B0F219BB3B873C1B5E
).