Skip to content
TUMR DOCS
DocsguidesHMAC Webhook Verification

HMAC Webhook Verification Guide

Securely verify incoming webhook payloads using HMAC-SHA256 signatures.

To protect your application against spoofing, Tumr signs every outgoing webhook notification with an HMAC-SHA256 digest over the raw request body.

Tumr transmits the signature as a raw hex digest in the X-Tumr-Signature HTTP header (no sha256= prefix): ``http X-Tumr-Signature: 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 hex-encoded HMAC-SHA256 of the raw payload using the shared webhook signing secret configured for the platform (issued during integration — ask your Tumr contact or support). 4. Perform a constant-time string comparison between your calculated digest 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 = 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 hashlib

app = 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 = 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"} ```