WhatsApp Cloud API Webhooks on Localhost: The Verify-Token Handshake, Retries and App Secret Setup
You start your WhatsApp bot on localhost:3000 , open a tunnel, paste the public URL into the Meta App Dashboard, click Verify and save , and get a red error. Or it works once, you restart the tunnel the next morning, and the dashboard wants verifying all over again. Both problems share a cause: Meta’s webhook onboarding is a handshake , not a simple “send me events” switch. Meta first sends a GET carrying a hub.challenge value. Only after you answer it correctly does it start delivering signed POST requests. If you fail to acknowledge those with a 200 , Meta keeps retrying for up to 7 days on the WhatsApp Business Platform. This guide covers the handshake, the retry behavior and the app-secret setup, then shows why a stable subdomain turns a recurring chore into a one-time registration. TL;DR Meta verifies your callback URL with a GET containing hub.mode , hub.verify_token and hub.challenge . Return HTTP 200 with the challenge value, but only if the token matches. The ver...