In this section, learn about the webhook notification request’s headers and body. It’s up to your application to parse the request, verify its signature, and send response.
Request
When an alert is triggered, the Secure Edge Portal sends an HTTP POST request to the webhook's configured URL. The headers and the JSON body of the POST request are described below.
The webhook notification request looks like this:
curl -X POST \
-H '<your_additional_headers>' \
-H "Content-type:application/json"
-H "X-Iotium-Signature:<IOTIUM_SIGNATURE>" \
-H "X-Iotium-Timestamp:<TIMESTAMP>" \
-d '{<WEBHOOK_NOTIFICATION_POST_BODY>}' \
'<your_webhook_url>'
Headers
The webhook notification request has the following ioTium-specific headers:
Header | Description |
---|---|
Your additional headers | Additional HTTP request headers you wanted to include in the notification request. When adding the webhook, you specified up to three headers with total header length of up to 2048 characters. |
X-Iotium-Signature | Signature created from this notification request's timestamp and body. For more details, see Verifying webhook signature. |
X-Iotium-Timestamp | POSIX timestamp in milliseconds, specifying when we sent this notification request. |
Body
The webhook notification body (payload) is a JSON object that describes the alert that triggered the notification. It also describes the target resource (iNode, iNode network, service, etc.) to which the alert applies.
The sections that follow describe the payload for each alert type.
Node Status Alert
The node status (iNode Status) alert notification payload has the following fields:
Field | Description |
---|---|
type | Metric type of your triggered alert. For this alert notification, it is NODE_STATE_CHANGE. |
display_type | Displayable text of the type field. For this alert notification, it is iNode Status. |
id | Unique identifier for your notification. When we retry sending the notification, this ID remains the same. |
alert | Details of your triggered alert: |
event_occurred_at | Date and time when your alert triggered in ISO 8601 format. |
created_at | Date and time when we detected your alert triggering in ISO 8601 format. |
organization | Details of your organization: |
node | Details of yournode that triggered the alert: |
profile | Your node profile details: |
hardware_serial_number | Serial number of your node that triggered the alert. |
status | Status of your node that triggered the alert: |
The node status alert notification payload looks like this:
{
"type":"NODE_STATE_CHANGE",
"node":{
"id":"<your_inode_id>",
"name":"<your_inode_name>"
},
"profile":{
"id":"<your_inode_profile_id>",
"name":"<your_inode_profile_name>"
},
"id":"<your_notification_id>",
"status":"<your_inode_status>",
"display_type":"iNode Status",
"hardware_serial_number":"<your_inode_serial_number>",
"organization":{
"id":"<your_org_id>",
"name":"<your_org_name>"
},
"alert":{
"id":"<your_alert_id>",
"name":"<your_alert_name>"
},
"created_at":"2020-01-01T00:00:00.000000000Z",
"event_occured_at":"2020-01-01T00:00:00.000000000Z"
}
Remote Network Connection Status
The Remote Network Connection Status alert notification payload has the following fields:
Field | Description |
---|---|
type | Metric type of your triggered alert. For this alert notification, it is TUNNEL_STATE_CHANGE. |
display_type | Displayable text of the type field. For this alert notification, it is Remote Network Connection Status. |
id | Unique identifier for your notification. When we retry sending the notification, this ID remains the same. |
alert | Details of your triggered alert: |
event_occurred_at | Date and time when your alert triggered in ISO 8601 format. |
created_at | Date and time when we detected your alert triggering in ISO 8601 format. |
organization | Details of your organization: |
node | Details of your node that triggered the alert: |
profile | Your node profile details: |
hardware_serial_number | Serial number of your node that triggered the alert. |
network | Details of your local network: |
peer_node | Details of your remote node: |
peer_node_profile | Details of your remote node profile: |
peer_network | Details of the remote network on your remote node: |
tunnel | Details of your local network connection: |
peer_tunnel | Details of your remote network connection: |
status | Status of your connection to the remote network that triggered the alert: |
The Remote Network Connection Status alert notification payload looks like this:
{
"type":"TUNNEL_STATE_CHANGE",
"node":{
"id":"<your_inode_id>",
"name":"<your_inode_name>"
},
"profile":{
"id":"<your_inode_profile_id>",
"name":"<your_inode_profile_name>"
},
"network":{
"id":"<your_network_id>",
"name":"<your_network_name>"
},
"peer_node":{
"id":"<your_remote_inode_id>",
"name":"<your_remote_inode_name>"
},
"peer_node_profile":{
"id":"<your_remote_inode_profile_id>",
"name":"<your_remote_inode_profile_name>"
},
"peer_network":{
"id":"<your_remote_network_id>",
"name":"<your_remote_network_name>"
},
"tunnel":{
"id":"<your_network_connection_id>",
"name":"<your_network_connection_name>"
},
"peer_tunnel":{
"id":"<your_remote_network_connection_id>",
"name":"<your_remote_network_connection_name>"
},
"id":"<your_notification_id>",
"status":"<your_remote_network_connection_status>",
"display_type":"Remote Network Connection Status",
"hardware_serial_number":"<your_inode_serial_number>",
"organization":{
"id":"<your_org_id>",
"name":"<your_org_name>"
},
"alert":{
"id":"<your_alert_id>",
"name":"<your_alert_name>"
},
"created_at":"2020-01-01T00:00:00.000000000Z",
"event_occured_at":"2020-01-01T00:00:00.000000000Z"
}
Service Status
The Service Status alert notification payload has the following fields:
Field | Description |
---|---|
type | Metric type of your triggered alert. For this alert notification, it is SERVICE_STATE_CHANGE. |
display_type | Displayable text of the type field. For this alert notification, it is Service Status. |
id | Unique identifier for your notification. When we retry sending the notification, this ID remains the same. |
alert | Details of your triggered alert: |
event_occurred_at | Date and time when your alert triggered in ISO 8601 format. |
created_at | Date and time when we detected your alert triggering in ISO 8601 format. |
organization | Details of your organization: |
node | Details of your node that triggered the alert: |
profile | Your node profile details: |
hardware_serial_number | Serial number of your node. |
service | Details of your service that triggered the event: |
container_status | Status of all the containers in your service, expressed as key-value pairs of the container name and container status. Status of a container can be: |
running_containers | Number of containers running in your service and the total number of containers, separated by the slash ( / ) character. |
status | Status of your service that triggered the alert: |
The Service Status alert notification payload looks like this:
{
"type":"SERVICE_STATE_CHANGE",
"node":{
"id":"<your_inode_id>",
"name":"<your_inode_name>"
},
"profile":{
"id":"<your_inode_profile_id>",
"name":"<your_inode_profile_name>"
},
"service":{
"id":"<your_service_id>",
"name":"<your_service_name>"
},
"id":"<your_notification_id>",
"status":"<your_service_status>",
"display_type":"Service Status",
"hardware_serial_number":"<your_inode_serial_number>",
"container_status":{"<your_container_name>":"<your_container_status>"},
"running_containers":"<your_running_containers_count>/<your_total_containers_count>",
"organization":{
"id":"<your_org_id>",
"name":"<your_org_name>"
},
"alert":{
"id":"<your_alert_id>",
"name":"<your_alert_name>"
},
"created_at":"2020-01-01T00:00:00.000000000Z",
"event_occured_at":"2020-01-01T00:00:00.000000000Z"
}
Standalone Mode Expiry
The Standalone Mode Expiry alert notification payload has the following fields:
Field | Description |
---|---|
type | Metric type of your triggered alert. For this alert notification, it is HEADLESS_EXPIRY. |
display_type | Displayable text of the type field. For this alert notification, it is Standalone Mode Expiry. |
id | Unique identifier for your notification. When we retry sending the notification, this ID remains the same. |
alert | Details of your triggered alert: |
event_occurred_at | Date and time when your alert triggered in ISO 8601 format. |
created_at | Date and time when we detected your alert triggering in ISO 8601 format. |
organization | Details of your organization: |
node | Details of your node that triggered the alert: |
profile | Your node profile details: |
hardware_serial_number | Serial number of your node that triggered the event. |
max_headless_time | Your node's standalone mode expiry period in minutes. |
headless_expire_at | Date and time when your node's standalone mode expires in ISO 8601 format. |
headless_expire_percent | Percentage of your node's standalone mode expiry period that is used up. If 80% or more is used up, the alert is triggered. |
is_headless_expired | Has your node's standalone mode expired? (true or false.) If true, the alert is triggered. |
The Standalone Mode Expiry alert notification payload looks like this:
{
"type":"HEADLESS_EXPIRY",
"node":{
"id":"<your_inode_id>",
"name":"<your_inode_name>"
},
"profile":{
"id":"<your_inode_profile_id>",
"name":"<your_inode_profile_name>"
},
"id":"<your_notification_id>",
"max_headless_time":10,
"headless_expire_at":"2020-01-01T00:00:00.000000000Z",
"headless_expire_percent":80,
"is_headless_expired":false,
"last_contact_at":"2020-01-01T00:00:00.000000000Z",
"hardware_serial_number":"<your_inode_serial_number>",
"display_type":"Standalone Mode Expiry"
"organization":{
"id":"<your_org_id>",
"name":"<your_org_name>"
},
"alert":{
"id":"<your_alert_id>",
"name":"<your_alert_name>"
},
"created_at":"2020-01-01T00:00:00.000000000Z",
"event_occured_at":"2020-01-01T00:00:00.000000000Z"
}
iNode Certificate Expiry
The node certificate expiry (iNode Certificate Expiry) alert notification payload has the following fields:
Field | Description |
---|---|
type | Metric type of your triggered alert. For this alert notification, it is CERT_EXPIRY. |
display_type | Displayable text of the type field. For this alert notification, it is iNode Certificate Expiry. |
id | Unique identifier for your notification. When we retry sending the notification, this ID remains the same. |
alert | Details of your triggered alert: |
event_occurred_at | Date and time when your alert triggered in ISO 8601 format. |
created_at | Date and time when we detected your alert triggering in ISO 8601 format. |
organization | Details of your organization: |
node | Details of your node that triggered the alert: |
profile | Your node profile details: |
hardware_serial_number | Serial number of your node that triggered the event. |
cert_expire_at | Date and time when your node certificate expires in ISO 8601 format. |
cert_expire_percent | Percentage of your node's certificate validity period that is used up. If 80% or more is used up, the alert is triggered. |
is_cert_expired | Has your node's certificate expired? (true or false.) If true, the alert is triggered. |
The iNode Certificate Expiry alert notification payload looks like this:
{
"type":"CERT_EXPIRY",
"id":"<your_notification_id>",
"certs":[{
"node":{
"id":"<your_inode_id>",
"name":"<your_inode_name>"
},
"profile":{
"id":"<your_inode_profile_id>",
"name":"<your_inode_profile_name>"
},
"organization":{
"id":"<your_org_id>",
"name":"<your_org_name>"
},
"cert_expire_at":"2020-01-01T00:00:00.000000000Z",
"hardware_serial_number":"<your_inode_serial_number>",
"cert_expire_percent":80,
"is_cert_expired":false
}],
"alert":{
"id":"<your_alert_id>",
"name":"<your_alert_name>"
},
"display_type":"iNode Certificate Expiry",
"created_at":"2020-01-01T00:00:00.000000000Z",
"event_occured_at":"2020-01-01T00:00:00.000000000Z"
}
iNode IP Address Change
The iNode IP Address Change alert notification payload has the following fields:
Field | Description |
---|---|
type | Metric type of your triggered alert. For this alert notification, it is NODE_IP_CHANGE. |
display_type | Displayable text of the type field. For this alert notification, it is iNode IP Address Change. |
id | Unique identifier for your notification. When we retry sending the notification, this ID remains the same. |
alert | Details of your triggered alert: |
event_occurred_at | Date and time when your alert triggered in ISO 8601 format. |
created_at | Date and time when we detected your alert triggering in ISO 8601 format. |
organization | Details of your organization: |
node | Details of your node that triggered the alert: |
profile | Your node profile details: |
hardware_serial_number | Serial number of your node that triggered the event. |
from | IP address of your node before the change. |
to | IP address of your node after the change. |
status | Your node's IP address change that triggered the alert: |
The iNode IP Address Change alert notification payload looks like this:
{
"type":"NODE_IP_CHANGE",
"node":{
"id":"<your_inode_id>",
"name":"<your_inode_name>"
},
"profile":{
"id":"<your_inode_profile_id>",
"name":"<your_inode_profile_name>"
},
"id":"<your_notification_id>",
"status":"<your_inode_which_ip_changed>",
"display_type":"iNode IP Address Change",
"hardware_serial_number":"<your_inode_serial_number>",
"from":"<your_from_ip_address>",
"to":"<your_to_ip_address>",
"organization":{
"id":"<your_org_id>",
"name":"<your_org_name>"
},
"alert":{
"id":"<your_alert_id>",
"name":"<your_alert_name>"
},
"created_at":"2020-01-01T00:00:00.000000000Z",
"event_occured_at":"2020-01-01T00:00:00.000000000Z"
}
iNode Upgrade Status
The node upgrade status (iNode Upgrade Status) alert notification payload has the following fields:
Field | Description |
---|---|
type | Metric type of your triggered alert. For this alert notification, it is NODE_UPGRADE. |
display_type | Displayable text of the type field. For this alert notification, it is iNode Upgrade Status. |
id | Unique identifier for your notification. When we retry sending the notification, this ID remains the same. |
alert | Details of your triggered alert: |
event_occurred_at | Date and time when your alert triggered in ISO 8601 format. |
created_at | Date and time when we detected your alert triggering in ISO 8601 format. |
organization | Details of your organization: |
node | Details of your node that triggered the alert: |
profile | Your node profile details: |
hardware_serial_number | Serial number of your node that triggered the event. |
from | Version of your node before the upgrade (if upgrade status is SUCCESSFUL). |
to | Version of your node after the upgrade (if upgrade status is SUCCESSFUL). |
status | Upgrade status of your node that triggered the alert: |
The iNode Upgrade Status alert notification payload looks like this:
{
"type":"NODE_UPGRADE",
"node":{
"id":"<your_inode_id>",
"name":"<your_inode_name>"
},
"profile":{
"id":"<your_inode_profile_id>",
"name":"<your_inode_profile_name>"
},
"id":"<your_notification_id>",
"status":"<your_inode_upgrade_status>",
"display_type":"iNode Upgrade Status",
"hardware_serial_number":"<your_inode_serial_number>",
"from":"<your_from_version>",
"to":"<your_to_version>",
"organization":{
"id":"<your_org_id>",
"name":"<your_org_name>"
},
"alert":{
"id":"<your_alert_id>",
"name":"<your_alert_name>"
},
"created_at":"2020-01-01T00:00:00.000000000Z",
"event_occured_at":"2020-01-01T00:00:00.000000000Z"
}
Responding to a webhook notification request
For all webhook notification requests, the response 2XX is success. The response 3XX and redirect of the request may occur at most five times. All other responses are considered failures.
Timeout and Retry
A webhook notification request may take up to five seconds to complete. If the connection fails, times out, or if the request fails with a 5XX response, a retry occurs after one minute. If the request still fails after the retry, there is no additional retry.
If the webhook notification request continuously fails 10 times, a WEBHOOK_FAILED event occurs and the webhook is disabled.
Verifying webhook signature
The webhook notification HTTP POST request contains a header with a signature generated from a cryptographic digest of the notification request body. When your application receives the request, we advise you to verify the signature by calculating the same digest and comparing it to the one in the header. Verifying the signature helps you to validate the integrity and origin of the webhook notification.
Following are the steps to verify the signature:
- Extract the timestamp from the X-Iotium-Timestamp header field and the signature from the X-Iotium-Signature header field of the webhook notification request.
- Compute the difference between the current timestamp and the extracted timestamp. Don't trust the payload if the difference exceeds your tolerance.
- Compute the expected signature this way:Shell
The secret is the value of the secret field you saved from the response to the POST request to add webhook. Donât use a pretty formatted request body for this computation.signature = Hex-encoded(HMACSHA256(<>,>,>
Following is a code snippet for computing the expected signature using Python (Version 3.8.2):Python
Following is a code snippet for computing the expected signature using Java (Version 1.8.0_191):def genSignature(secret:str, timestamp:str, body:str) -> str: import hmac import hashlib message:str = "%s.%s" % (timestamp, body) return hmac.new(secret.encode(), message.encode(), hashlib.sha256).hexdigest()
Java
4. Compare the expected signature with the signature extracted from the X-Iotium-Signature header field. If both are identical, you can trust the payload. Otherwise, don't trust the payload.import java.nio.charset.Charset; import java.security.GeneralSecurityException; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; public String getSignature(String secret, String timestamp, String body) { String HMAC_SHA256_ALGORITHM = "HmacSHA256"; String hmac_hex = null; try { SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(Charset.defaultCharset()), HMAC_SHA256_ALGORITHM); Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM); mac.init(signingKey); String message = timestamp.concat(".").concat(body); byte[] rawHmac = mac.doFinal(message.getBytes(Charset.defaultCharset())); hmac_hex = bytesToHex(rawHmac); } catch (GeneralSecurityException e) { throw new IllegalArgumentException(); } return hmac_hex; } public String bytesToHex(byte[] bytes) { char[] hexArray = "0123456789abcdef".toCharArray(); char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { int v = bytes[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); }