WhatsApp in Dify on the official API, with ZapClaw
In short: ZapClaw posts each WhatsApp message to a Webhook Trigger in a Dify workflow. The workflow reads it, asks your model in an LLM node and replies with one HTTP Request. Messages go through Meta's official Cloud API, and the number keeps working in the WhatsApp Business app on your phone. No relay server and no Meta app of your own.
Connect your number in minutes and give your Dify app a WhatsApp number, on Dify Cloud or on your own install.
Business plan: US$ 29 per month, with unlimited numbers
Start for free See the stepsWhat you need to connect
- The WhatsApp Business app on the number, version 2.24.17 or newer, in regular use.
- Two-factor authentication on the personal Facebook account that authorizes the connection.
- The number not connected to another provider. If it is, disconnect it there first.
- A computer to run the connection, with your phone at hand to confirm it in the app.
You do not need a company tax ID or a verified business to connect. The full checklist.
Why the official API for a Dify app
- No unofficial library behind your AI. Integrations that imitate WhatsApp Web go against WhatsApp's terms and put the number at risk. ZapClaw uses Meta's Cloud API, and Meta's policies apply as they do to any business on it.
- The app stays on the phone. Through Coexistence your team keeps answering in the WhatsApp Business app while the Dify workflow works the same number.
- One webhook in, one HTTP call out. The Webhook Trigger takes ZapClaw's JSON as it is, and the HTTP Request node sends your key in an
X-API-Keyheader. No plugin to install in Dify. - Deliveries you can check. Dify answers ZapClaw as soon as the workflow is triggered, each event is signed with HMAC-SHA256, and an event Dify does not accept gets up to 3 delivery attempts and can be replayed from the ZapClaw dashboard.
Why a Webhook Trigger, and not Dify's API
Dify can also run a workflow through its app API (POST /v1/workflows/run), but that call needs an Authorization: Bearer header with the app's API key. A ZapClaw webhook sends only its own headers, so it cannot call that API directly. The Webhook Trigger accepts a plain HTTP request, which is exactly what ZapClaw sends. If you already run a server between ZapClaw and Dify, that server can call the app API instead.
Triggers run only in Workflow apps, not in Chatflow or Agent apps. A self-hosted Dify has had them since version 1.10.0, released in November 2025; on an older install, upgrade first.
Set it up in six steps
- In Dify, create a Workflow app. On the canvas, right-click and choose Add Node > Start > Webhook Trigger. Set the method to POST and the content type to
application/json. - Add three request body parameters and leave them optional:
eventas String,dataas Object andaccountas Object. The names must match ZapClaw's keys. A required parameter that is missing keeps the workflow from starting, and the dashboard's test event has noaccount. - After the trigger, add in this order: a Code node that reads the message, an If-Else that goes on only when
textis not empty, an LLM node that writes the answer, a second Code node that builds the reply and an HTTP Request that sends it. The settings of each are below. - In ZapClaw, connect your number, create an API key and paste the trigger's test URL under Webhook. Under Events to receive, leave only Inbound messages checked: every delivery status would otherwise start a run, and on Dify Cloud each run counts against the trigger quota. In Dify, click Run this step on the trigger, then send a WhatsApp message to your number from another phone. The trigger's Last Run tab shows what arrived.
- Publish the workflow, and in ZapClaw replace the test URL with the trigger's webhook URL.
- Send another WhatsApp message from the other phone: the workflow answers it.
The two Code nodes
Both run Python. In the first, map the input variables event, data and account to the trigger's outputs, and declare three String outputs: sender, customer and text. Anything that is not an incoming text leaves text empty, so status updates and the test event end at the If-Else and your own replies never loop.
def main(event: str, data: dict, account: dict) -> dict:
data = data or {}
account = account or {}
text = ""
if event == "message.received" and data.get("type") == "text":
text = (data.get("text") or {}).get("body") or ""
return {
"sender": str(account.get("display_phone_number") or account.get("phone_number_id") or ""),
"customer": str(data.get("from") or data.get("user_id") or ""),
"text": text,
}
In the LLM node, pick your model, write your instructions in the system prompt and put the first Code node's text in the user prompt. In the second Code node, map sender and customer from the first one and reply from the LLM node's text output, and declare one Object output, body. Built in code, the body stays valid JSON whatever quotes or line breaks the model writes.
def main(sender: str, customer: str, reply: str) -> dict:
return {
"body": {"from": sender, "to": customer, "text": reply},
}
To prove the round trip before you set up a model, map reply to the first Code node's text instead: the workflow answers with the text it received.
The HTTP Request node
- Method and URL: POST
https://api.zapclaw.app/api/v1/send/text. - Authentication: API Key of the Custom type, with
X-API-Keyas the header and your ZapClaw key as the value. Keep the key in a Secret environment variable, which Dify masks in the run logs. - Body: JSON, holding only the
bodyvariable of the second Code node.
Free text works within 24 hours of the customer's last message. Outside that window Meta accepts only an approved template: call POST /api/v1/send/template instead.
To try your key before you build the workflow, send the same call from a terminal. A sent message answers 201.
curl -X POST https://api.zapclaw.app/api/v1/send/text \
-H "X-API-Key: zc_live_..." \
-H "Content-Type: application/json" \
-d '{ "from": "15551617409", "to": "15555550100", "text": "Hello from Dify" }'
What reaches the Webhook Trigger
Each message comes as a message.received event, and the trigger hands the workflow its three top-level keys: event, data with the message (the text in data.text.body, the sender in data.from, the contact's name in data.contact_name) and account with your number. A media message carries a media_url you download with your API key.
{
"event": "message.received",
"sequence": 1042,
"timestamp": "2026-05-21T14:00:00.000Z",
"data": {
"from": "15555550100",
"user_id": "US.1A2B3C4D5E6F7G8H9I0J",
"contact_name": "Maria Silva",
"contact_username": "maria_lima",
"message_id": "wamid.HBg...",
"type": "text",
"timestamp": "1747836000",
"text": { "body": "Hello" }
},
"account": {
"phone_number_id": "964283400112599",
"display_name": "My Business",
"display_phone_number": "+1 555 161-7409"
}
}
Every request carries an X-ZapClaw-Signature header, an HMAC-SHA256 of the raw body keyed with your signing secret: how to verify it. Dify's Webhook Trigger extracts fields and headers but does not document a way to read the raw body the signature covers, so keep the trigger's URL to yourself, like a password.
Good to know
- Each message is answered on its own. Triggers run Workflow apps, and the LLM node's memory is a Chatflow feature, so the model sees the message in front of it, not the conversation before it. Put the context it needs in the system prompt.
- Dify answers right away. The trigger replies
200 OKwhen the workflow is triggered, well within ZapClaw's 10 second timeout, while the model takes the time it needs. - Self-hosted Dify. Point the
TRIGGER_URLenvironment variable at a public HTTPS domain. Dify builds the trigger URLs from it (the default ishttp://localhost), and ZapClaw delivers only to public HTTPS addresses. - Dify Cloud. Runs started by a trigger count against a monthly quota that depends on your Dify plan.
What it costs
- Free: 500 messages a month and 1 number, with no card and no end date. At the limit, sending pauses and new messages are held until you subscribe.
- Business: US$ 29 per month, with 20,000 messages included and unlimited numbers.
- Agency: US$ 49 per month for agencies, with 3 clients and 60,000 messages included and unlimited numbers.
Meta bills its WhatsApp message fees to you directly, with no ZapClaw markup. Every plan and price.
Who it is for
Teams that build their AI in Dify and want it on WhatsApp: answers from a knowledge base, lead qualification, first-line support. Agencies connect their clients' numbers on the Agency plan, each client isolated from the others. It does not fit bulk broadcasting.
Go further
- The official WhatsApp API with ZapClaw: what you get, the price and the requirements.
- A webhook for your own AI, when people keep replying from the app and the AI only reads.
- WhatsApp in n8n and WhatsApp in Make, for automations around the AI.
- The webhook reference, with every event and field.
- Flowise on WhatsApp, if you build your agents in Flowise.
Sources
Checked on 15 October 2026. Dify changes quickly, so if a setting looks different, check the source.
- Dify docs, Webhook Trigger: method, content type, body parameters, required parameters, the response and the test URL.
- Dify docs, Trigger: Workflow apps only, publishing and the trigger quota.
- Code, If-Else, LLM and HTTP Request in the Dify docs.
- Dify docs, Get started with the Dify API: the Bearer API key.
- Dify docs, self-hosted environment variables:
TRIGGER_URL. - Dify 1.10.0 release notes on GitHub: triggers, 13 November 2025.
- ZapClaw: webhooks, signature verification and sending a text in the docs.
Put your Dify app on WhatsApp today
Start on the free plan: one number, no card. Move to the Business plan when you need more numbers or more messages.
Start for free Read the webhook docs