Webhooks

Webhooks keep your system in sync with the status of the bronID verifications. You will receive a webhook every time there is a change in the verification status of your KYC entries. This includes an initial webhook when you submit the KYC. The initial webhook will have the same value as the HTTP response you get from the API. For security reasons, always verify the signature of the webhook to confirm it came from bronID.

Webhooks get triggered when:

  • There is a new KYC submission (through the API or the bronID Portal)
  • There is a change in the status of the verification

The verification status of the webhooks can be:

  • pending - the verification is pending processing
  • verified - the verification was successful
  • rejected - the verification was not successful
  • info - (legal entities only) additional information required for verification
  • locked - too many unsuccessful verification attempts

Look up the understanding results documentation for more details.

Webhook examples

We will look at some webhooks results to show you their structure and how to handle them.

Pending webhook

The pending webhook means that the KYC verification is pending processing. The status of this verification will change into verified, rejected or info.

Here is the example of the pending webhook:

{
	"name": "My Company",
	"verificationStatus": "pending",
	"verificationUuid": "63152524-c722-4a44-8931-1c6caad4dbcf",
	"timestamp": 1585549407,
	"userId": "yourUniqueUserId",
	"submissionStatus": "submitted",
	"stakeholders": [],
	"signature": {
		"version": "1",
		"sha256": "bronid_sec_sha256_340cb830d92ecebbc88709d4828c39a33884004bd92fc55523c7e36ba3d07728",
		"keccak256": "bronid_sec_keccak256_080b5c54352977d8bbdbffe7f9cc90fa08fcaf53e6814b50728fc79e3d9f75d2"
	}
}

Verified webhook

The verified webhook means that the KYC verification has satisfied the bronID verification requirements.

Here is the example of the verified webhook:

{
	"name": "My Company",
	"verificationStatus": "verified",
	"verificationUuid": "63152524-c722-4a44-8931-1c6caad4dbcf",
	"timestamp": 1585548662,
	"userId": "yourUniqueUserId",
	"submissionStatus": "submitted",
	"stakeholders": [],
	"signature": {
		"version": "1",
		"sha256": "bronid_sec_sha256_d1a08cf016dcb86c4a4d551220eba5da6ba51e12bbb1c78d7bab01e9cea87885",
		"keccak256": "bronid_sec_keccak256_20d89ffa5870fbb2cc577585e431f626a586857b164d34a902bfd17bc61be75e"
	}
}

Rejected webhook

The rejected webhook means that the KYC verification has not has satisfied the bronID verification requirements.

Here is the example of the rejected webhook:

{
	"name": "My Company",
	"verificationStatus": "rejected",
	"verificationUuid": "63152524-c722-4a44-8931-1c6caad4dbcf",
	"timestamp": 1585548940,
	"userId": "yourUniqueUserId",
	"submissionStatus": "submitted",
	"stakeholders": [],
	"signature": {
		"version": "1",
		"sha256": "bronid_sec_sha256_d4dab7958f6205adc6e52f3d3a3947c6a7677b7b4aca84780c8770013bec0786",
		"keccak256": "bronid_sec_keccak256_7dd112ee1255367ff5cc17a3199b162ac9307d781dbbc407264cc95202838242"
	}
}

Info webhook

The info webhook means that you need to provide more details to complete the verification. In this example we will look at a verification that is missing a stakeholder (individual) and a document.

Here is the example of the info webhook:

{
	"verificationStatus": "info",
	/* the verificationUuid which has missing information */
	"verificationUuid": "63152524-c722-4a44-8931-1c6caad4dbcf",
	"timestamp": 1583455152245,
	"userId": "theSubmittedUserId",
	/* these are the requestedActions */
	"requestedActions": [
		{
			"type": "individual",
			"role": "trustee",
			/* the missing stakeholder id */
			"id": "5ad6b33c-44f1-4b12-9ec9-31fadd41f6c3",
			"fields": {
				/* information about the missing stakeholder */
				"country": "AUS",
				"firstName": "Jane",
				"lastName": "Citizen",
				"middleName": ""
			}
		},
		{
			"type": "document",
			/* the missing document id */
			"id": "a2820913-8be8-4cd9-bacc-29b6cfd2d38c",
			"extractType": "trustDeed"
		}
	]
}

You can submit the information for the requestedActions with a follow-up request:

{
	"metadata_version": "4",
	"metadata_serviceUid": "yourServiceUid",
	"metadata_secretKey": "yourSecretKey",
	/* this matches the verificationUuid of the info verification */
	"verificationUuid": "cd0c518f-7084-49ff-802a-9c517245daf2",
	/* these are the requestedActions */
	"requestedActions": [
		{
			"type": "individual",
			"role": "trustee",
			/* this matches the id of the requested stakeholder */
			"id": "5ad6b33c-44f1-4b12-9ec9-31fadd41f6c3",
			"fields": {
				"country": "AUS",
				"firstName": "Jane",
				"lastName": "Citizen",
				"middleName": "",
				"gender": "female",
				"dateOfBirth": "20/10/1982",
				"unitNumber": "1",
				"streetNumber": "94",
				"streetName": "lennox",
				"streetType": "Street",
				"suburb": "CASINO",
				"postcode": "2470",
				"state": "NSW",
				"email": "email@domain.com"
			}
		},
		{
			"type": "document",
			/* this matches the id of the requested document */
			"id": "a2820913-8be8-4cd9-bacc-29b6cfd2d38c",
			/* the base64 of the document */
			"documentBase64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",
		}
	]
}

Locked webhook

The locked webhook means that the KYC verification has not has satisfied the bronID verification requirements.

Here is the example of the locked webhook:

{
	"name": "My Company",
	"verificationStatus": "locked",
	"verificationUuid": "63152524-c722-4a44-8931-1c6caad4dbcf",
	"timestamp": 1585548940,
	"userId": "yourUniqueUserId",
	"submissionStatus": "submitted",
	"stakeholders": [],
	"signature": {
		"version": "1",
		"sha256": "bronid_sec_sha256_d4dab7958f6205adc6e52f3d3a3947c6a7677b7b4aca84780c8770013bec0786",
		"keccak256": "bronid_sec_keccak256_7dd112ee1255367ff5cc17a3199b162ac9307d781dbbc407264cc95202838242"
	}
}

Verify webhook source

You can verify the webhook came from bronID by re-creating the signature on your side. To create the signature, append the pre-shared signing key to the end of the payload and then generate a hash. Your hash should match the webhook signature.

The response contains two signatures - KECCAK256 and SHA3-256.

Note: SHA3-256 gives different results than SHA-256 (part of the SHA2 family).

Note: Omit the signatures from the response when signing the payload in your code.

Here is an example of a signed response:

{
	/* payload start */
	"name": "My Company",
	"verificationStatus": "verified",
	"verificationUuid": "63152524-c722-4a44-8931-1c6caad4dbcf",
	"timestamp": 1585548940,
	"userId": "yourUniqueUserId",
	"submissionStatus": "submitted",
	"stakeholders": [],
	/* payload end */
	/* omit the signature to verify source */
	"signature": {
		"version": "1",
		"sha256": "bronid_sec_sha256_d4dab7958f6205adc6e52f3d3a3947c6a7677b7b4aca84780c8770013bec0786",
		"keccak256": "bronid_sec_keccak256_7dd112ee1255367ff5cc17a3199b162ac9307d781dbbc407264cc95202838242"
	}
}

Runnable example

const sha3_256 = require('js-sha3').sha3_256;
const keccak256 = require('js-sha3').keccak256;

// secret signing key
const signingKey = "the_secret_signing_key@!";

// sample incoming webhook
const incomingWebhook = {
	/* payload start */
    name: 'My Company',
    verificationStatus: 'pending',
    verificationUuid: '5e397c13-3413-477f-b980-58e45cf97d0c',
    timestamp: 1586151903,
    userId: 'yourUniqueUserId',
    submissionStatus: 'submitted',
    stakeholders: [
        {
            name: 'Dave ok Citizen',
            role: 'directors',
            id: 'c184f980-60a2-4a32-801d-4decb96f7c3a',
            verificationStatus: 'verified',
            verificationScore: 250,
            verificationUuid: 'b4920dd6-1c81-4a26-b7a5-b15d8ccd07f0',
            submissionStatus: 'submitted',
            timestamp: 1586151899,
            inWatchLists: [
            	{
                    method: 'background',
                    name: 'International Criminal Police Organization (INTERPOL) Wanted Persons',
                    greenIdCode: 'interpolmostwanted',
                    officialUrl: 'https://www.interpol.int/How-we-work/Notices/View-Red-Notices',
                }
            ],
            outOfWatchLists: [
                {
                    method: 'background',
                    name: 'Canada Office of the Superintendent of Financial Institutions (OSFI)',
                    greenIdCode: 'osfi',
                    officialUrl: 'http://www.osfi-bsif.gc.ca/Eng/fi-if/amlc-clrpc/atf-fat/Pages/default.aspx',
                }
            ],
        },
        {
            name: 'Jane ok Citizen',
            role: 'shareholders',
            id: '42de9703-e563-4504-9c26-b5667c604576',
            verificationStatus: 'verified',
            verificationScore: 250,
            verificationUuid: 'f069cc43-7114-4eb9-9228-c13d1172d28d',
            submissionStatus: 'submitted',
            timestamp: 1586151899,
            inWatchLists: [],
            outOfWatchLists: [
                {
                    method: 'background',
                    name: 'International Criminal Police Organization (INTERPOL) Wanted Persons',
                    greenIdCode: 'interpolmostwanted',
                    officialUrl: 'https://www.interpol.int/How-we-work/Notices/View-Red-Notices',
                },
                {
                    method: 'background',
                    name: 'Canada Office of the Superintendent of Financial Institutions (OSFI)',
                    greenIdCode: 'osfi',
                    officialUrl: 'http://www.osfi-bsif.gc.ca/Eng/fi-if/amlc-clrpc/atf-fat/Pages/default.aspx',
                }
            ],
        },
    ],
    /* payload end */
    
    /* omit the signature when verifying the source */
    signature: {
        version: '1',
        sha256: 'bronid_sec_sha256_e29dea58183041072e68f13966419450932f226527d21e81f174e013fa5e023c',
        keccak256: 'bronid_sec_keccak256_6fbc541cc20e3b32f8ee9e9d01b9f444b46c2761dfaba268dce6901e0f7c06ea',
    },
};

// remove signatures from webhook
const payload = _.omit(incomingWebhook, 'signature');

// stringify the payload
const stringifiedPayload = JSON.stringify(payload);

// append the signing key
const unsignedString = `${stringifiedPayload}${signingKey}`;

// generate signature
const generatedSha3_256 = sha3_256(unsignedString);
const generatedKeccak256 = keccak256(unsignedString);

// expected sha3_256 signature: e29dea58183041072e68f13966419450932f226527d21e81f174e013fa5e023c
// prefix with bronid_sec_sha256_ before comparison
const comparableSignature = `bronid_sec_sha256_${generatedSha3_256}`;
console.log(comparableSignature);

// expected keccak256 signature: 6fbc541cc20e3b32f8ee9e9d01b9f444b46c2761dfaba268dce6901e0f7c06ea
// prefix with bronid_sec_keccak256_ before comparison
console.log(`bronid_sec_keccak256_${generatedKeccak256}`);

const isVerifiedSource = incomingWebhook.signature.sha256 === comparableSignature;
console.log(isVerifiedSource ? 'source is bronID!' : 'unauthorised source!')

// Click “▶ run” to try this code live and generate a webhook signature.