Skip to main content

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:

  1. Go to Settings → Webhooks → [Your Webhook] → Rotate Secret
  2. Copy the new secret
  3. Update your server's environment variable
  4. AlertifyPro will use the new secret for all future deliveries