Use Tab, then Enter to open a result.
Defining the Signature Mismatch Error
WhatsApp Flow signature mismatch errors occur when your backend server fails to validate the cryptographic signature sent by Meta. Every request from a WhatsApp Flow to an external endpoint includes a security header. This header is the X-Hub-Signature-256. It contains an HMAC-SHA256 hash of the request body. Your server calculates its own hash using your App Secret and compares it to the header value. If the values do not match, the request is rejected. This mechanism prevents attackers from spoofing requests to your database.
Production environments often trigger this error when the raw request body undergoes modification before verification. High-integrity systems require the exact byte-for-byte representation of the payload. Any change to whitespace, character encoding, or key ordering results in a failed match. This failure halts the flow and prevents users from completing their interactions.
Why Verification Fails in Production
Verification failures stem from three primary causes. First, developers often use the parsed JSON body instead of the raw request buffer. Modern web frameworks automatically parse incoming JSON into objects. Stringifying these objects back to JSON for hashing produces different strings than the original payload. Even one extra space character changes the resulting SHA256 hash.
Second, incorrect App Secret usage leads to failures. The App Secret resides in the Meta App Dashboard under the Basic Settings section. Using an Access Token or a Client Secret instead of the App Secret will result in an invalid hash. Third, encoding issues affect the hash. Meta sends the payload in UTF-8 encoding. If your server interprets the payload using a different encoding before hashing, the signature will fail.
Prerequisites for Secure Endpoint Verification
You need specific assets from your Meta Developer Portal to begin. Ensure you have the following ready:
- The App Secret: Locate this in your App Dashboard. Do not share this value.
- An HTTPS Endpoint: Meta requires a secure connection. A self-signed certificate is insufficient for production.
- Raw Body Access: Your server must have the ability to read the incoming request as a raw buffer or stream.
If you use a tool like WASenderApi for session management, remember that Flows typically interact directly with Meta servers. Your backend serves as the logic engine for these Flows. This guide focuses on the direct communication between Meta and your logic server.
Implementing HMAC-SHA256 Verification
The verification process follows a strict sequence. You must extract the signature header, capture the raw body, generate a local hash, and perform a timing-safe comparison. Using a timing-safe comparison prevents side-channel attacks that attempt to guess the signature by measuring response times.
Step 1: Extract the Header
The header arrives as X-Hub-Signature-256. The value begins with the prefix sha256=. You must remove this prefix to isolate the hexadecimal signature. If the header is missing, the request did not originate from a verified Meta source.
Step 2: Capture the Raw Buffer
Configure your middleware to skip JSON parsing for the verification route. In Node.js, this involves using the verify option in the body-parser middleware. In Python, you access request.data instead of request.json. This ensures you hash the exact bytes received over the network.
Step 3: Generate the Local Hash
Use your App Secret as the HMAC key. Apply the SHA256 algorithm to the raw body buffer. The output must be a hexadecimal string. This string represents your local version of the expected signature.
Practical Verification Examples
The following examples demonstrate verification in Node.js and Python. These implementations avoid common pitfalls like JSON stringification errors.
Node.js and Express Implementation
This implementation uses the built-in crypto module. It captures the raw body before Express parses the JSON.
const crypto = require('crypto');
const express = require('express');
const app = express();
// Capture the raw body for signature verification
app.use(express.json({
verify: (req, res, buf) => {
req.rawBody = buf;
}
}));
app.post('/whatsapp-flow', (req, res) => {
const signatureHeader = req.get('X-Hub-Signature-256');
if (!signatureHeader) {
return res.status(401).send('Signature missing');
}
const signature = signatureHeader.replace('sha256=', '');
const appSecret = process.env.APP_SECRET;
const expectedSignature = crypto
.createHmac('sha256', appSecret)
.update(req.rawBody)
.digest('hex');
// Use timing-safe comparison
const isValid = crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expectedSignature, 'hex')
);
if (!isValid) {
return res.status(401).send('Signature mismatch');
}
// Process the verified request
console.log('Verified Flow Data:', req.body);
res.status(200).json({ status: 'success' });
});
Python and Flask Implementation
Flask provides access to the raw data through request.data. Use the hmac and hashlib modules for the calculation.
import hmac
import hashlib
import os
from flask import Flask, request, jsonify
app = Flask(__name__)
APP_SECRET = os.environ.get('APP_SECRET')
@app.route('/whatsapp-flow', methods=['POST'])
def verify_flow_request():
signature_header = request.headers.get('X-Hub-Signature-256')
if not signature_header:
return "Missing signature", 401
# Remove the 'sha256=' prefix
signature = signature_header.split('=')[1]
# Get raw bytes from request
raw_body = request.data
# Calculate HMAC-SHA256
expected_signature = hmac.new(
APP_SECRET.encode('utf-8'),
raw_body,
hashlib.sha256
).hexdigest()
# Securely compare signatures
if not hmac.compare_digest(signature, expected_signature):
return "Signature mismatch", 401
# Request is valid
data = request.get_json()
return jsonify({"status": "verified"}), 200
Expected JSON Structure
When a WhatsApp Flow sends a request to your endpoint, the payload follows a specific format. The mismatch error often occurs because the server expects one format while Meta sends another. Here is a typical Flow payload for an external data exchange.
{
"version": "3.0",
"action": "data_exchange",
"screen": "CONTACT_INFO",
"data": {
"user_id": "12345",
"selected_option": "premium_support"
},
"flow_token": "abc-123-token"
}
Edge Cases and Hidden Pitfalls
Several environmental factors cause signature mismatches despite correct code. Proxies and load balancers often interfere with the request. If your server sits behind a reverse proxy, the proxy might strip headers or modify the request body. Verify that your proxy preserves the X-Hub-Signature-256 header and does not alter the payload encoding.
Cloudflare and similar WAFs might block the request before it reaches your verification logic. Check your security logs for blocked requests originating from Meta IP addresses. Another issue arises from environment variables. If your App Secret contains special characters, ensure your deployment platform does not escape them. An escaped character in the secret results in a different hash than what Meta uses.
Multi-region deployments require attention to time synchronization. While HMAC verification does not rely on timestamps in the same way as TOTP, heavy latency in body delivery results in timeouts. Timeouts appear as connection errors rather than signature mismatches, but they often occur simultaneously during high-traffic periods.
Troubleshooting Steps
Follow these steps to isolate the cause of a signature mismatch:
- Log the Raw Body: Print the raw buffer to your console before hashing. Compare the length of this buffer with the
Content-Lengthheader. If they differ, the body was truncated or modified. - Verify the Secret: Hardcode the App Secret temporarily in a local test environment to rule out environment variable errors. Do not commit this hardcoded version to source control.
- Check the Algorithm: Ensure you use SHA256 and not SHA1. Older Meta documentation referenced SHA1, but modern WhatsApp Flows require SHA256.
- Inspect the Header Prefix: Confirm your code correctly handles the
sha256=prefix. Forgetting to remove this string is a frequent source of errors. - Use a Test Tool: Create a local script that generates an HMAC-SHA256 signature using your secret and a sample payload. Compare this output to the one generated by your server logic.
FAQ
Do I need to verify signatures for internal flow transitions? No. Verification is required only when the Flow calls an external endpoint that you host. Interactions within the Meta-hosted Flow environment do not require this step.
Is X-Hub-Signature-256 the same as X-Hub-Signature?
No. The version without the -256 suffix uses SHA1. Meta has moved toward SHA256 for better security. Always use the 256-bit version for WhatsApp Flows.
Does the signature verification work with encrypted flows? If your flow uses end-to-end encryption for the payload, you must decrypt the payload first. However, the signature verification typically happens on the encrypted blob to ensure it was not tampered with during transit. Check your specific flow version requirements.
Why does my local signature match sometimes but not always? This behavior indicates a body parsing issue. Small payloads might not trigger modification by your framework, while large or complex JSON payloads might. Always use the raw buffer to ensure consistency across all payload sizes.
Will a signature mismatch error ban my WhatsApp account? No. A signature mismatch is a technical error between your server and Meta. It does not violate WhatsApp Business policies. Still, repeated failures provide a poor user experience and might cause Meta to pause your flow if the success rate drops too low.
Final Checklist
- App Secret is verified and correctly loaded into the environment.
- Request body is captured as a raw buffer before any JSON parsing occurs.
- The
sha256=prefix is removed from theX-Hub-Signature-256header. - The hashing algorithm is set specifically to HMAC-SHA256.
- The comparison between signatures uses a timing-safe function.
- The server returns a 200 OK response only after successful verification.