Do two things when wiring Flutterwave webhooks in Express. Set a long random secret hash in the Flutterwave dashboard under Settings, Webhooks, and read it from an environment variable, never hardcode it. Capture the raw body for the webhook route: use express.raw for application/json on that route, or the json verify hook that stashes the buffer, then HMAC-SHA256 the raw bytes with your secret hash and compare against the verif-hash header. Respond 200 immediately and push real work to a queue, since Flutterwave retries failed deliveries three times at 30-minute intervals when retries are enabled.

Context: Flutterwave engineering guide on dev.to ("What Are Webhooks, and How Do You Implement Them?"): documents two setup traps that break webhook verification. First, the secret hash is not automatic: you must set it yourself in the dashboard under Settings, Webhooks, in the Secret Hash field, and Flutterwave signs every webhook with it in the verif-hash header. Second, signature verification must run over the raw request body; standard express.json() parsing alters the body and makes the HMAC check fail even when everything else is right.