Webhook Signatures
Every webhook request AlertifyPro sends includes an X-AlertifyPro-Signature header. Always verify this signature to ensure the request genuinely came from AlertifyPro and hasn't been tampered with.
How it works
AlertifyPro signs each payload using HMAC-SHA256 with your webhook's signing secret:
signature = HMAC-SHA256(signing_secret, raw_request_body)
header = "sha256=" + hex(signature)
Verification examples
Node.js / TypeScript
import crypto from 'crypto';
import express from 'express';
const app = express();
// ⚠️ Must use raw body — JSON parsing removes whitespace
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-alertifypro-signature'] as string;
const secret = process.env.WK_WEBHOOK_SECRET!;
const body = req.body.toString('utf-8');
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(body, 'utf-8')
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(body);
// Handle event...
res.status(200).send('ok');
});
Python
import hmac
import hashlib
import os
from flask import Flask, request, abort
app = Flask(__name__)
@app.route('/webhook', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-AlertifyPro-Signature', '')
secret = os.environ['WK_WEBHOOK_SECRET'].encode('utf-8')
body = request.get_data() # Raw bytes
expected = 'sha256=' + hmac.new(secret, body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(signature, expected):
abort(401)
event = request.get_json(force=True)
# Handle event...
return 'ok', 200
Go
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"net/http"
"os"
)
func verifyWebhook(r *http.Request, secret string) bool {
sig := r.Header.Get("X-AlertifyPro-Signature")
body, _ := io.ReadAll(r.Body)
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(body)
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(sig), []byte(expected))
}
Important notes
- Use the raw request body — Do not parse JSON before computing the signature, as parsing can alter whitespace
- Use constant-time comparison (
timingSafeEqual/hmac.compare_digest) — Prevents timing attacks - Store the secret securely — In an environment variable or secrets manager
Rotate your signing secret
If your secret is compromised:
- Go to Settings → Webhooks → [Your Webhook] → Rotate Secret
- Copy the new secret
- Update your server's environment variable
- AlertifyPro will use the new secret for all future deliveries