The SignifAI Community Hub

Welcome to the SignifAI Community Hub.
This is the place for you to find something new, express your thoughts, share and collaborate with other people. You'll find comprehensive guides and documentation to help you start working with SignifAI as quickly as possible, as well as support if you get stuck. Let's jump right in!

Webhook

A Guide to consuming incident webhooks

Using Webhook as an actuator

SignifAI Webhook let you receive HTTP callbacks when events happen based on your decisions. SignifAI will send the event's details via HTTPS POST to a URL that you specify.
We currently support issues-based webhook. After adding a webhook URL to a notification method, the triggering of new issues on that decision will cause outgoing webhook messages to be sent to that URL.

Format

All webhooks are issued with the HTTP POST method, the body is always JSON (the header is Content-Type: application/json). We expect that your endpoint will return as success HTTP code (2xx). In the case of a faluire HTTP code, the system will retry to resend the notification every 10 minutes (not accurate) for up to 7 days. If after 7 days the endpoint is still down, the Issue notification will be discarded.

Here is the json schema of the request body, if you're unfamiliar with JSON schema, see this great guide.
Notice that all time fields, timestamp_created and timestamp_updated are in unix time format (seconds since epoch).

{
    "name": {
        "type": "string",
        "minLength": 1,
        "maxLength": 255
    },
    "description": {
        "type": "string",
        "minLength": 1,
        "maxLength": 255
    },
    "state": {
        "type": "string",
        "enum": [
            "created",
            "active",
            "acknowledged",
            "closed"
        ]
    },
    "previous_state": {
        "oneOf": [{
                "type": "string",
                "enum": [
                    "created",
                    "active",
                    "acknowledged",
                    "closed"
                ]
            },
            {
                "type": "null"
            }
        ]
    },
    "priority": {
        "type": "string",
        "enum": [
            "critical",
            "high",
            "medium",
            "low"
        ]
    },
    "categories": {
        "oneOf": [{
                "type": "array",
                "items": {
                    "type": "string",
                    "enum": [
                        "infrastructure",
                        "application",
                        "storage",
                        "networking"
                    ]
                }
            },
            {
                "type": "null"
            }
        ]
    },
    "closing_reason": {
        "oneOf": [{
                "type": "string",
                "enum": [
                    "nacked",
                    "resolved",
                    "system"
                ]
            },
            {
                "type": "null"
            }
        ]
    },
    "is_snoozed": {
        "oneOf": [{
                "type": "boolean"
            },
            {
                "type": "null"
            }
        ]
    },
    "is_starred": {
        "oneOf": [{
                "type": "boolean"
            },
            {
                "type": "null"
            }
        ]
    },
    "is_archived": {
        "oneOf": [{
                "type": "boolean"
            },
            {
                "type": "null"
            }
        ]
    },
    "modified_on": {
        "type": "integer"
    },
    "created_on": {
        "type": "integer"
    },
    "closed_on": {
        "oneOf": [{
                "type": "integer"
            },
            {
                "type": "null"
            }
        ]
    },
    "acknowledged_on": {
        "oneOf": [{
                "type": "integer"
            },
            {
                "type": "null"
            }
        ]
    },
    "active_since": {
        "oneOf": [{
                "type": "integer"
            },
            {
                "type": "null"
            }
        ]
    },
    "snoozed_on": {
        "oneOf": [{
                "type": "integer"
            },
            {
                "type": "null"
            }
        ]
    },
    "assigned_to": {
        "oneOf": [{
                "type": "array",
                "items": {
                    "type": "string",
                    "minLength": 1
                }
            },
            {
                "type": "null"
            }
        ]
    },
    "created_by": {
        "oneOf": [{
                "type": "array",
                "items": {
                    "type": "string",
                    "minLength": 1
                }
            },
            {
                "type": "null"
            }
        ]
    },
    "timestamp": {
        "type": "integer"
    },
    "origin_type": {
        "type": "string",
        "enum": [
            "user",
            "system"
        ]
    },
    "issue_id": {
        "type": "string",
        "minLength": 1,
        "format": "uuid"
    }
}

Authentication

Every webhook trigger will contain the X-Signifai-Signature. The signature is the HMAC on the message body and the secret. The secret can be added in the integration window [TODO: add link]. The secret must be entered as base64 format and with minimum length of 16 characters.

The signing algorithm is as follows (in pseudo-code):

TO_BASE64(HMACH_SHA256(FROM_BASE64(secret), request.body))

Verifying the signature (examples)

import hashlib
import hmac
import base64

secret = "<You secret here as base64>"
signature = hmac.new(base64.b64decode(secret), request.body, hashlib.sha256).digest()

# Verify the signature
assert signature == base64.b64decode(request.headers['X-Signifai-Signature']), "Signature does not match"
package com.company;

import java.util.Arrays;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;


public class Example {
    public static boolean verifySignature(String body, String secret, String signatureHeader) throws NoSuchAlgorithmException, InvalidKeyException {
        Mac hmac = Mac.getInstance("HmacSHA256");

        byte[] secretBytes = Base64.getDecoder().decode(secret);

        SecretKeySpec secretKey = new SecretKeySpec(secretBytes, "HmacSHA256");
        hmac.init(secretKey);

        byte[] computedSignature = hmac.doFinal(body.getBytes());

        return Arrays.equals(computedSignature, signatureBytes);
    }
}
namespace SignifAI
{
    using System;
    using System.Text;
    using System.Linq;
    using System.Security.Cryptography;

    class Example
    {
        /// <summary>Verify the webhook signature</summary>
        /// <param name="body">The body of the request, as string</param>
        /// <param name="secret">The secret used by the customer</param>
        /// <param name="signatureHeader">The content of the X-Signifai-Header</param>
        /// <returns>Returns true if the signature is correct, false otherwise.</returns>
        public static bool VerifySignature(string body, string secret, string signatureHeader)
        {
            // First make sure to remove pesky whitespaces
            secret = secret.Trim();
            signatureHeader = signatureHeader.Trim();

            byte[] signature = Convert.FromBase64String(signatureHeader);

            // If the signature is not in the correct size, it's definatly wrong since SHA256 is always 256 bit long
            if (signature.Length != 32)
                return false;

            byte[] computedHash;
            
            byte[] secretBytes = Convert.FromBase64String(secret);
            using (var hmac = new HMACSHA256(secretBytes))
            {
                computedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));
            }

            for (int i = 0; i < signature.Length; i++)
            {
                if (computedHash[i] != signature[i])
                {
                    return false;
                }
            }

            return true;
        }
    }
}
var CryptoJS = require("crypto-js");

function verifySignature(body, secret, signatureHeader) {
    var hash = CryptoJS.HmacSHA256(body, secret);
    return CryptoJS.enc.Base64.stringify(hash) == signatureHeader
}

The JavaScript implementation is using CryptoJS, to install use the shell command npm install crypto-js

Need help with the integration?

Contact us at: support@signifai.io and we will be happy to help.

Webhook

A Guide to consuming incident webhooks