Webhook notifications and verifying HMAC signatures
A webhook notification channel lets CronJobPro POST a JSON payload to your own endpoint whenever an alert fires, so you can route it into your systems. If you set a signing secret, each request is signed with HMAC-SHA256 and you should verify that signature before trusting the payload.
Add a webhook channel
- 1
Open notification settings
Go to Settings then Notifications and add a new channel of type Webhook.
- 2
Enter your endpoint URL
Provide the HTTPS URL on your server that will receive the POST requests.
- 3
Set an optional signing secret
Enter a shared secret to enable HMAC-SHA256 signing. Treat it like a password and store it securely on your side.
- 4
Choose which alerts to send
The channel can deliver failure, recovery, and circuit-breaker or disabled alerts. Save the channel to activate it.
The JSON payload
On each event CronJobPro sends a POST request with a JSON body describing the alert. The exact fields depend on the event type, but the shape looks like this:
{
"event": "failure",
"job": {
"id": "job_123",
"name": "Nightly sync"
},
"status": "failed",
"consecutiveFailures": 3,
"timestamp": "2026-05-31T09:00:00Z"
}Verify the HMAC signature
When a signing secret is set, CronJobPro signs the request with HMAC-SHA256 and sends the signature in a request header. To verify it, recompute the HMAC over the exact raw request body using your shared secret, then compare it to the header value in constant time.
Always compute the HMAC over the raw, unparsed request body. Re-serializing the JSON can reorder keys or change whitespace and will break verification. Use a constant-time comparison to avoid timing attacks.
const crypto = require('crypto');
const express = require('express');
const SECRET = process.env.CRONJOBPRO_WEBHOOK_SECRET;
const app = express();
// Capture the raw body so we can verify the signature over the exact bytes.
app.use(express.raw({ type: 'application/json' }));
app.post('/webhooks/cronjobpro', (req, res) => {
const signature = req.get('X-CronJobPro-Signature') || '';
const expected = crypto
.createHmac('sha256', SECRET)
.update(req.body) // req.body is a Buffer of the raw body
.digest('hex');
const sigBuf = Buffer.from(signature);
const expBuf = Buffer.from(expected);
if (
sigBuf.length !== expBuf.length ||
!crypto.timingSafeEqual(sigBuf, expBuf)
) {
return res.status(401).send('invalid signature');
}
const payload = JSON.parse(req.body.toString('utf8'));
console.log('Verified event:', payload.event);
res.status(200).send('ok');
});
app.listen(3000);Respond with a 2xx status once you have accepted the request. Confirm the exact header name and payload fields shown in your dashboard, since they are the source of truth for your account.
Use the Webhook Tester to send a sample signed payload to your endpoint and confirm your verification logic works before relying on it in production.