Skip to content

Ledger entryNo. 119418

Webhook Verification Request

Also called: hub.challenge · Verify token handshake · Webhook verification · hub.verify_token

Definition
The GET Meta sends to a callback URL with hub.mode, hub.verify_token and hub.challenge; the endpoint must echo the challenge with a 200 to be accepted.

Webhooks & events

Mode · sequence

Work it out

Step through the hub.challenge handshake

7 / 7
  • YouApp Dashboard
  • MetaWebhooks
  • Your endpoint/webhooks/whatsapp

Step 7 of 7Response

Your endpointMeta

200 OK

Acknowledge each event quickly.

  • Request — solid arrow — a call that expects an answer
  • Response — dashed arrow — the answer coming back
  • Event — dotted arrow — something happened and is pushed out
From saving a callback URL to the first signed event: what Meta sends, what your endpoint must check, and the single value it has to echo back.

Step 7 of 7: Your endpoint to Meta — 200 OK. Acknowledge each event quickly.

What is the webhook verification request?

The webhook verification request is the handshake Meta performs before it will send events to a URL. Whenever you set or edit the callback URL or the verify token of your webhook subscription in the App Dashboard, Meta sends a GET request to that URL with three query parameters: hub.mode set to subscribe, hub.verify_token containing the string you entered, and hub.challenge containing a random value Meta generates.

Your webhook endpoint compares hub.verify_token with the value stored on your server. If it matches and the mode is subscribe, the endpoint responds HTTP 200 with the raw hub.challenge value as the entire body — no JSON wrapper, no quotes, no extra text. Anything else, typically a 403, makes Meta reject the URL, and the configuration is not saved.

The verify token is not a password or an API key: it is an arbitrary string you choose so the endpoint can recognise a handshake you started. Verification runs once per configuration change and proves the URL is reachable and under your control. It is different from signature validation with X-Hub-Signature-256, which authenticates every later POST with your app secret — a verified URL must still check the signature on each event. Nor is it webhook testing: passing the handshake says nothing about how the endpoint handles the events that follow.

Key takeaways

  • The webhook verification request is a GET Meta sends when you save a callback URL or verify token, with hub.mode, hub.verify_token and hub.challenge.
  • Answer HTTP 200 with the bare hub.challenge value only when hub.verify_token matches the token stored on your server.
  • The verify token is any string you choose; it is not the app secret and does not protect later events.
  • Every later event is a POST authenticated by its X-Hub-Signature-256 signature, not by the handshake.
  • Self-signed certificates are not supported: the callback URL needs a valid certificate from a public CA.

The figures

The webhook verification request: key facts and limits

Method
GET (events later arrive as POST)
Query parameters
hub.mode, hub.verify_token, hub.challenge
hub.mode
subscribe
Success
HTTP 200 with the bare hub.challenge as the body
Failure
403 or any other non-200 status
When sent
Each time the callback URL or verify token is set or edited
Certificate
Valid CA-signed TLS; self-signed not supported

How does the webhook verification request work?

  1. You save a URL and a token

    In WhatsApp > Configuration, or through the Application Subscriptions API, you enter the callback URL and a verify token of your choosing.

  2. Meta sends a GET

    The request carries hub.mode=subscribe, hub.challenge and hub.verify_token in the query string — and no body.

  3. Your endpoint checks

    Confirm hub.mode is subscribe and hub.verify_token equals your stored token. Read the parameter names with their dots intact.

  4. Echo the challenge

    Respond 200 with the hub.challenge value as plain text. On a mismatch, respond 403 or another non-200 status.

  5. Meta saves the configuration

    Only after a correct echo is the URL saved and field subscription available. From then on, events arrive as POSTs signed in X-Hub-Signature-256.

In code

The webhook verification request in code

import hmac
import os
from fastapi import FastAPI, Request, Response
app = FastAPI()
VERIFY_TOKEN = os.environ["VERIFY_TOKEN"]
@app.get("/webhooks/whatsapp")
async def verify(request: Request) -> Response:
q = request.query_params # keys keep their dots: "hub.mode"
token = q.get("hub.verify_token", "")
if q.get("hub.mode") == "subscribe" and hmac.compare_digest(token, VERIFY_TOKEN):
return Response(content=q.get("hub.challenge", ""), media_type="text/plain") # 200
return Response(status_code=403)

Example: The dots that PHP ate

A Ranchi developer deploys a PHP endpoint and the dashboard keeps rejecting it. The access log shows Meta's GET arriving, but the code reads $_GET['hub.challenge'], which is always empty: PHP converts dots in incoming parameter names to underscores, so the values sit in $_GET['hub_challenge'] and $_GET['hub_verify_token']. After switching keys — and removing a middleware that wrapped every response in JSON — the endpoint returns the bare challenge, the URL saves, and the first test payload arrives a minute later.

Costly mistakes

Common mistakes with the webhook verification request

  • Returning JSON instead of the bare value

    A body like {"challenge": "1158201444"}, or the value in quotes, fails. Send the challenge alone as text/plain.

  • Losing the dots in parameter names

    PHP converts hub.mode to hub_mode, and some routers rewrite query keys too. Log the raw query string once to see exactly what your framework hands you.

  • Auth or a firewall in front of the URL

    Basic auth, an API-gateway key or an IP block on the webhook path makes Meta's GET fail. Allow unauthenticated GETs there and protect POSTs with the signature check.

  • Treating the verify token as security

    It only matches a handshake you started. Anyone can send your endpoint a GET; the real protection for events is the X-Hub-Signature-256 check on every POST.

  • Certificate problems

    Self-signed certificates are not supported. Use a TLS certificate from a public certificate authority, serve the full chain, and test the URL from outside your own network.

Webhook Verification Request FAQs

Why does the dashboard say my callback URL could not be validated?
Usually the token, the response format, the certificate or a blocking proxy is at fault. Check that the token matches, the body is the bare challenge with a 200, the certificate is publicly trusted and nothing in front of the endpoint blocks the GET.
What should the verify token be?
Any string you choose; a long random value is best. Store it as a server secret, enter the same value in the App Dashboard, and use a different token for each environment.
Is the verify token the same as the app secret?
No, the verify token is any string you choose for the GET handshake. The app secret is issued by Meta and keys the HMAC in X-Hub-Signature-256 on every POST.
How often does Meta send the verification request?
Only when you set or edit the callback URL or the verify token, not before every event. Keep the GET handler deployed anyway, because every future configuration change runs it again.

Why does the webhook verification request matter?

It is the first thing every WhatsApp integration has to get right, and the most common place new integrations stall: the dashboard shows a terse error and nobody can tell whether the token, the certificate, the routing or the response format is at fault. Knowing exactly what Meta sends and expects turns a lost afternoon into a two-minute fix — and makes clear why passing verification does not mean your events are secure.

Best practices for the webhook verification request

  • Generate the verify token randomly and keep it in your secrets store, separate from the app secret.
  • Log verification attempts, without the token value, so a failed save is easy to diagnose.
  • Run the handshake yourself with curl before saving the URL in the dashboard.
  • Keep the GET handler deployed: every future edit of the URL or token runs it again.

Put Webhook Verification Request to work

InfiQ is an official Meta Business Partner. Start a 7-day free trial — no card needed — and see this running on your own number.