Before using this guide, we recommend reviewing the following sections to understand Blockmark’s webhook system:
This guide provides a step-by-step process for setting up and managing webhooks in BlockMark Registry, enabling your application to receive real-time notifications for events such as certificate issuance and updates. It covers creating a webhook group, verifying an endpoint, handling webhook events, and verifying request authenticity using the webhook secret. The examples use Node.js with Express to demonstrate a simple implementation.
Organisation Settings
page..env
file). You can retrieve this from the Webhooks
section of the Organisation Settings
page.Webhooks
section within the Organisation Settings
page in the Registry dashboard.Create a New Webhook
button to open the webhook creation form.issued_certificate.created
and issued_certificate.updated
).After creating the webhook group, BlockMark Registry requires endpoint verification to ensure the URL is accessible and correctly configured.
event
key set to endpoint.verify
and a secret
string, as shown below:{
"event": "endpoint.verify",
"secret": "abc123"
}
secret
string in the response body and an HTTP 200
status code.Verify Now
when ready.Below is an example that handles the endpoint.verify
event in a Node.js app using Express:
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook', (req, res) => {
const { event, secret } = req.body;
if (event === 'endpoint.verify') {
return res.status(200).send(secret);
}
return res.status(200).send({ message: 'Webhook received' });
});
app.listen(3000, () => console.log('Server running on port 3000'));
This code checks for the endpoint.verify
event and responds with the provided secret
. Once verified, your endpoint is ready to receive webhook events.
To process webhook events securely, your application must verify the X-Token
header in each request using the webhook secret. The webhook payload always includes an event
key (e.g., issued_certificate.created
) and a data
key containing event-specific details. Below is an enhanced example that handles issued_certificate.created
and issued_certificate.updated
events, including X-Token verification.
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
app.post('/webhook', (req, res) => {
const { headers, body } = req;
const { event, data } = body;
// Verify X-Token
const xToken = headers['x-token'];
const webhookSecret = process.env.WEBHOOK_SECRET;
const generatedHex = crypto.createHmac('sha256', webhookSecret).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(generatedHex, 'hex'), Buffer.from(xToken, 'hex'))) {
return res.status(400).send({ message: 'Invalid X-Token' });
}
// Handle endpoint verification
if (event === 'endpoint.verify') {
return res.status(200).send(body.secret);
}
// Handle issued_certificate.created
if (event === 'issued_certificate.created') {
console.log('Certificate issued:', data);
return res.status(200).send({ message: 'Certificate creation processed' });
}
// Handle issued_certificate.updated
if (event === 'issued_certificate.updated') {
console.log('Certificate updated:', data);
return res.status(200).send({ message: 'Certificate update processed' });
}
return res.status(200).send({ message: 'Webhook received' });
});
app.listen(3000, () => console.log('Server running on port 3000'));
X-Token
header is verified by generating an HMAC using the webhook secret then converting it to a hexadecimal string, and then comparing it with the provided x-token
using crypto.timingSafeEqual
to prevent timing attacks.endpoint.verify
, issued_certificate.created
, and issued_certificate.updated
events, logging data and responding appropriately.process.env.WEBHOOK_SECRET
(e.g., in a .env
file).To ensure your endpoint handles webhooks correctly, use the Send Test Webhook
feature in the webhook group’s action menu. This sends mock data to your endpoint without incurring costs (unlike non-test webhooks, which cost 10 credits per successful attempt). Test each event type to validate your implementation.
issued_certificate.created
Send Test Webhook
and choose issued_certificate.created
.{
"event": "issued_certificate.created",
"data": {
"slug": "2db008cf-1ce0-48d8-9b66-300423891fe5",
"name_on_certificate": "William Mcfarland",
"has_name_mismatch": true,
"is_issued_to_thing": false,
"start_time": "1985-10-14T18:17:46.183395+01:00",
"finish_time": "2006-10-05T04:53:52.001364+01:00",
"timestamp": "1972-11-10T03:28:14.717931Z",
"status": "Expired",
"hidden": false,
"is_test": true,
"is_archived": false,
"is_adopted": null,
"bespoke_id": null,
"template": {
"slug": "344f6969-4eed-4632-84aa-d890ef19064f",
"name": "embrace 24/365 partnerships"
},
"is_to_guardian": true,
"is_self_certified": false,
"custom_fields": {},
"custom_notes": {}
}
}
200
status, as shown in the example above.issued_certificate.updated
issued_certificate.updated
in the Send Test Webhook
menu.{
"event": "issued_certificate.created",
"data": {
"slug": "2db008cf-1ce0-48d8-9b66-300423891fe5",
"name_on_certificate": "William Mcfarland",
"has_name_mismatch": true,
"is_issued_to_thing": false,
"start_time": "1985-10-14T18:17:46.183395+01:00",
"finish_time": "2006-10-05T04:53:52.001364+01:00",
"timestamp": "1972-11-10T03:28:14.717931Z",
"status": "Expired",
"hidden": false,
"is_test": true,
"is_archived": false,
"is_adopted": null,
"bespoke_id": null,
"template": {
"slug": "344f6969-4eed-4632-84aa-d890ef19064f",
"name": "embrace 24/365 partnerships"
},
"is_to_guardian": true,
"is_self_certified": false,
"custom_fields": {},
"custom_notes": {}
}
}
Congratulations, you are now up and running and can start receiving webhooks from BlockMark Registry on your external app.
For additional support, as always, contact us via the help link in the footer.