# HMAC Webhook Verification Guide
To protect your application against spoofing and replay attacks, Tumr signs every outgoing webhook notification with a cryptographic HMAC-SHA256 digest using your endpoint's signing_secret.
Tumr transmits the signature in the X-Tumr-Signature HTTP header:
``http
X-Tumr-Signature: sha256=d523674681f21132a87401c107297e...
Verification Algorithm
1. Extract the signature string from the X-Tumr-Signature header.
2. Read the raw, unparsed request body as bytes.
3. Compute the HMAC-SHA256 hash of the raw payload using your webhook secret.
4. Perform a constant-time string comparison between your calculated hash and the received signature.
---
Node.js / Express Implementation
import crypto from 'crypto';const app = express();
// Important: Read raw buffer to prevent JSON re-serialization differences app.post('/webhooks/tumr', express.raw({ type: 'application/json' }), (req, res) => { const signature = req.headers['x-tumr-signature']; const secret = process.env.TUMR_WEBHOOK_SECRET;
const expectedSignature = 'sha256=' + crypto .createHmac('sha256', secret) .update(req.body) .digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))) { console.warn('Invalid signature received!'); return res.status(401).send('Invalid signature'); }
const event = JSON.parse(req.body.toString()); console.log('Event received:', event.type, event.data);
// Return 200 OK immediately res.status(200).json({ received: true }); }); ```
---
Python / FastAPI Implementation
import hmac
import hashlibapp = FastAPI() WEBHOOK_SECRET = "whsec_your_signing_secret_here"
@app.post("/webhooks/tumr") async def handle_tumr_webhook(request: Request): signature = request.headers.get("X-Tumr-Signature", "") raw_body = await request.body()
expected_signature = "sha256=" + hmac.new( key=WEBHOOK_SECRET.encode("utf-8"), msg=raw_body, digestmod=hashlib.sha256 ).hexdigest()
if not hmac.compare_digest(signature, expected_signature): raise HTTPException(status_code=401, detail="Invalid signature")
payload = await request.json() print("Received event:", payload["type"]) return {"status": "success"} ```