Prerequisites
- An active Dualhook connection with Webhook Override enabled
- A Make scenario using Webhooks > Custom webhook
- Your Meta webhook verify token (for the GET handshake — set during connection setup in Dualhook)
- A WhatsApp access token and
PHONE_NUMBER_IDfor outbound Graph API sends, such as sending messages - Your
WABA_IDfor payload-side validation
Keep these values available in Make keychains, connection fields, or secure variables:
WEBHOOK_VERIFY_TOKEN
WHATSAPP_ACCESS_TOKEN
PHONE_NUMBER_ID
WABA_ID
META_GRAPH_VERSION=v25.0
No
META_APP_SECRET? In the Embedded Signup flowX-Hub-Signature-256is signed by Dualhook's Meta app and isn't customer-verifiable — validate inbound webhook payload shape instead. See Messaging Webhook → Inbound Webhook POST Validation.
Architecture Overview
In this pattern, Make is the actual Meta webhook receiver. Dualhook still handles onboarding and operations, but message-path traffic goes straight from Meta to your Custom webhook URL.
Meta (messages, statuses, errors)
-> Make Custom webhook
-> verification
-> inbound payload validation
-> scenario logic
-> optional Graph API send
Dualhook
-> management events, template operations, health monitoring
Make can also retain payloads in webhook queues, webhook logs, and scenario histories. That means Make becomes part of your privacy and retention boundary even though Dualhook itself stays metadata-only.
Create the Inbound Scenario
Set up one scenario around Webhooks > Custom webhook.
Recommended webhook options:
- Get request headers: on
- Get request HTTP method: on
- JSON pass through: on
These options matter because:
- you need the request method to branch cleanly between
GETandPOST - you need the original JSON for payload-shape validation before downstream modules act on it
Do not rely on Make's API key protection for the Meta callback. That feature expects X-Make-Apikey, which Meta will not send. Use Meta's verify-token handshake for GET and payload-shape validation for POST.
Use JSON > Parse JSON to expose mapped fields you can branch on.
Handle the Verification Handshake
Meta verifies the callback URL with a GET request containing:
hub.mode=subscribehub.verify_token=<YOUR_VERIFY_TOKEN>hub.challenge=<CHALLENGE_VALUE>
In the GET path:
- Check that
hub.modeissubscribe. - Check that
hub.verify_tokenmatches your expected token. - Return the raw
hub.challengevalue using Webhooks > Webhook response. - Return
403if verification fails.
Recommended response settings:
- Success:
200, plain text body = challenge - Failure:
403, plain text body =Forbidden
Validate the Webhook Payload
Validate every POST before downstream modules act on the payload. Confirm the WhatsApp envelope shape and the WABA ID / phone_number_id against the values you stored for this connection.
Check the envelope, WABA, and phone number
After JSON > Parse JSON, add a Router or Filter that only accepts the request when:
objectequalswhatsapp_business_accountentry[].idequals your storedWABA_IDentry[].changes[].fieldismessagesentry[].changes[].value.metadata.phone_number_idis present and equals your storedPHONE_NUMBER_ID
If any check fails, respond 401 with Webhook response and stop the scenario.
Keep the URL unguessable
Make's Custom webhook URL contains a random token — treat it as a secret. Do not paste it into shared chat, do not commit it to repos, and regenerate the webhook (creating a new URL) if it leaks.
Respond quickly
If validation passes:
- Send
200early with Webhook response. - Continue the business logic afterward.
If validation fails:
- Return
401 - Do not process the payload
Send Template Messages via Graph API
Make sends outbound template messages directly to Meta. Dualhook is not in the outbound request path.
Use HTTP > Make a request:
- Method:
POST - URL:
https://graph.facebook.com/v25.0/<PHONE_NUMBER_ID>/messages - Body content type:
application/json
Prefer Make's dedicated credentials fields over manually placing auth inside query strings or arbitrary headers. If you use header-based auth, the request still needs:
Authorization: Bearer <WHATSAPP_ACCESS_TOKEN>Content-Type: application/json
Example template send:
{
"messaging_product": "whatsapp",
"to": "12015550123",
"type": "template",
"template": {
"name": "order_update_v1",
"language": { "code": "en_US" }
}
}
For templates with variables, media headers, and buttons, see Sending Template Messages.
Test with Dualhook
- Copy the Make Custom webhook URL into Dualhook.
- Use the same verify token in Dualhook and in the Make scenario.
- Run Dualhook Test Ping to confirm the endpoint is reachable.
- Send a real WhatsApp message to your number.
- Confirm that:
- the
GETverification path works - the
POSTenvelope/WABA/phone-number check accepts real Meta payloads and rejects unrelated test pings - the scenario returns
200quickly - both inbound messages and status callbacks are processed
- the
Production Caveats
- Make Custom webhooks have a fixed payload size limit of 5 MB. Large payloads will fail before your scenario logic runs.
- Make documents webhook throughput limits, queue behavior, and webhook log retention. If traffic spikes, you can see 429s, queue buildup, or delayed processing.
- Use Webhook response intentionally. If you respond too early and a later module fails, Make can keep consuming credits while the sender already saw success.
- Make queues and logs can contain payload data. Review retention and operational access carefully if you want to keep message exposure low outside Dualhook.
- Use idempotent downstream handling for retried events.
- Treat webhook fields as evolving. The BSUID rollout makes some identifiers conditional, so parse defensively instead of assuming
fromorwa_idare always present. See BSUID Transition Guide.