Send WhatsApp messages from Zapier or Make with one HTTP request
Send WhatsApp template messages from a Zap or a Make scenario using a plain webhook or HTTP step, with a test key, idempotent re-runs, refusal handling and a webhook for delivery status and replies.
November 12, 2026 · 8 min read
If your orders, leads or bookings already flow through Zapier or Make, WhatsApp is the step that's easy to postpone: Meta's developer setup, template approval, then the API calls. The call itself is one HTTP request. This guide sets it up in a Zap and in a Make scenario, tests it without sending anything, and shows what to do when WhatsApp refuses a message.
SendFromChat doesn't have a dedicated Zapier or Make app, so this uses each tool's generic web-request step against the REST API. SendFromChat is an independent product and is not affiliated with Meta, WhatsApp, Zapier or Make. It runs on Meta's official WhatsApp Cloud API; you keep your own WhatsApp Business account. Menu names in both tools change, so treat the labels below as a guide.
What you need first
- A SendFromChat account (30-day free trial, no card) with a WhatsApp number connected, using Continue with Facebook (getting started).
- One approved template for the message. Messages you start outside a 24-hour conversation window must use one (how the window works). An order update is a Utility template, for example:
Hi {{1}}, your order {{2}} has shipped. Track it here: {{3}}(approval rules). - Customers who agreed to WhatsApp updates (how to collect opt-in).
- For Zapier: a plan that includes Webhooks by Zapier. Zapier's help article lists it as not available on the Free plan and available on Professional, Team and Enterprise (read 12 Nov 2026; check Zapier's current page).
Step 1: a test key
On the dashboard's Developers page, create a test key (sfc_test_…). It runs every check a real send would (plan, opt-outs, template, 24-hour window), then stops: nothing is sent, saved or counted. Build the whole Zap or scenario with it, then swap in a live key (sfc_live_…).
One thing to know about keys in these tools: the key sits in a header inside the Zap or scenario, so anyone who can edit it can see it. Create a dedicated key for this automation. Keys can be revoked individually, so if someone leaves, revoke that one and nothing else breaks.
Step 2: the request
Both tools send the same request. This is the shape, as a curl command you can run first to see the answer:
curl -i https://sendfromchat.com/api/v1/messages \
-H "Authorization: Bearer sfc_test_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-4821-shipped" \
-d '{
"to": "+491512345678",
"template": {
"name": "order_shipped",
"language": "en_US",
"variables": ["Anna", "#4821", "https://example.com/track/4821"]
}
}'
to must be in international format with the country code. The API strips spaces and dashes and adds the +, but it can't guess a missing country code (phone number format).
With a test key you get 200 and "status": "test". With a live key you get 202 and "status": "accepted", which means the request was accepted, not that the message was delivered or read. Delivery comes through GET /messages/{id} or a webhook (step 5).
Step 3a: in Zapier
- Add an action step, app Webhooks by Zapier, event Custom Request. Zapier's help says Custom Request is the one for nested JSON, arrays and custom headers; the plain POST event is simpler but can't build the
variablesarray as cleanly. - Method:
POST. URL:https://sendfromchat.com/api/v1/messages. - Data: the JSON body from above, with the mapped fields from your trigger dropped into the values. Zapier sends Custom Request data as the raw text you enter, without parsing it, so if a value has a stray quote the request is sent broken. See the quote note below.
- Headers: one per line,
Authorization=Bearer sfc_test_…,Content-Type=application/json, andIdempotency-Key= a value built from the record, such asorder-plus the order ID plus-shipped. Zapier's article doesn't say whether Content-Type is added for you, so set it explicitly. - Test the step. You should see
200and"status": "test".
Step 3b: in Make
- Add an HTTP module, action Make a request.
- URL:
https://sendfromchat.com/api/v1/messages. Method:POST. - Headers:
Authorization=Bearer sfc_test_…(Make's HTTP module also has a credentials option for authentication; a header works the same here), andIdempotency-Keybuilt from the record's ID. - Body type: raw, content type: JSON (application/json), and paste the JSON body with your mapped values.
- If the module has an option to parse the response as JSON, turn it on so later modules can read
statusanderror.type; otherwise add a parse step. - Check the module's Return error if HTTP request fails setting. When it is on, a
4xxor5xxresponse stops the scenario and goes to your error handling. When it is off, the scenario carries on and you readerror.typeyourself. Either works; choose on purpose.
The quote gotcha
Both tools build the body as text. If a customer's name is Anna "Ana" Lopez and you paste it into a JSON string unchanged, the quote ends the string early and the API answers 400 with The request body must be JSON. Escape the values (\"), or in Make build the body with a JSON-creating module and map it in, and in Zapier clean the text in a Formatter or Code step first. We ran these cases against a local mock of the API (mock only, not run against the real API or Meta): the unescaped quote returned 400, the escaped one returned 200.
Why the idempotency key matters here
Zaps and scenarios get re-run: you replay a failed run, a trigger fires twice, or a retry kicks in. If the Idempotency-Key comes from the event (order-4821-shipped) and not from the run, a repeat returns the first response with Idempotent-Replayed: true instead of sending a second message. Reusing a key with a different body returns idempotency_mismatch. A key from the run ID or a random value would defeat the point.
Step 4: handle the refusals
Some errors are WhatsApp's rules, not bugs, and retrying won't fix them. Branch on error.type (a Filter or Paths step in Zapier, a Router or error route in Make):
error.type |
What it means | What to do |
|---|---|---|
opted_out |
The customer replied STOP | Stop; don't retry |
window_closed |
Free-form text was sent outside the 24-hour window | Send a template instead |
template_not_approved |
The template isn't approved yet | Wait, or fix the template |
idempotency_mismatch |
The same key was reused with a different body | Fix the key |
warmup_limit |
A new number reached today's cap (50, then 100, then 200 a day) | Retry later |
sending_paused |
Trial ended or plan limit reached | Check the plan |
A missing or revoked key returns 401. The full list is in the REST API reference.
Step 5: delivery status and replies
For what happens after the send, start a second Zap or scenario with a webhook trigger (a step that gives you a URL to receive requests). Copy that URL, add it as a webhook endpoint on the Developers page and subscribe to:
message.status: sent, delivered, read, or failed with a reasonmessage.received: the customer replied (with their text and phone number)contact.opted_out: someone replied STOP, so you can remove them from your own lists too
Two things to get right. Our webhook needs a 2xx within 5 seconds, so keep this workflow's first step light and do the slow work after. Deliveries can repeat, so keep the event id (evt_…) of what you've handled, for example in a sheet or table, and ignore duplicates.
Each request carries a SendFromChat-Signature header, an HMAC-SHA256 of the timestamp and raw body, signed with the endpoint secret. We haven't tested checking it inside Zapier or Make, which hand you a parsed payload rather than the raw body. The REST API reference has the verification function; if you can't verify in your setup, don't let unverified events trigger anything sensitive, such as a refund or a data change. Posting a Slack note is fine.
A reply path that fits
With message.received flowing in, you can create a task, post to your team chat, or look up the order and answer. When a customer writes in, a 24-hour window opens and you can answer with free-form text via POST /messages with { "to": "…", "text": "…" }, no template needed. Replies, auto-replies and chatbot messages are unlimited on every plan, and only messages you start count toward the monthly allowance (plans and limits). For common questions, a chatbot flow can answer and hand over to a person without any Zap or scenario. For the delivery side in depth, see tracking sent, delivered and read and receiving replies with webhooks.
Costs, in plain terms
Three separate things. SendFromChat's flat monthly plan (from $19, with 5,000 messages a month that you start on Starter). Meta's per-message charge, which Meta bills to your own WhatsApp Business account at its published rates, with no markup from us (WhatsApp API pricing). And your Zapier or Make plan: each send uses a task or operation there, so check how your plan counts them.
A note for EU and UK customers
SendFromChat stores data in Mumbai, India. Transfers from the EU and UK are covered by the Standard Contractual Clauses and the UK Addendum in our Data Processing Agreement, and the sub-processors are listed publicly. Read it before you decide. Zapier and Make are processors on your side too, so they belong in your own records. Our GDPR guide covers consent, opt-out and records.
Prefer another tool?
The same request works in n8n, and the same account works from Claude, ChatGPT and Claude Code (connect your assistant).
Try it
Start the 30-day free trial (no card), connect a number, create a test key and run the step above. If you're weighing other routes, see SendFromChat vs Twilio.
Do this from your AI assistant
SendFromChat connects Claude, ChatGPT, Cursor or any MCP client to the WhatsApp Business API — templates, bulk sends, inbox, chatbots and sequences, just by asking. 30 days free, no card.
Start freeMore guides
WhatsApp display name rejected? The rules, the fix, and the 14-day re-register
Why Meta rejects a WhatsApp Business display name, what each rejection reason in the webhook means, how to edit and resubmit it (10 edits in 30 days), and the 14-day window to re-register the number after approval. Includes a mock-tested Node script.
WhatsApp two-step verification PIN: what to do when register asks for it
What the 6-digit two-step verification PIN does on the WhatsApp Cloud API, when the register call needs your existing one, how to change a forgotten PIN, why you should not loop the call (10 tries in 72 hours), and Meta's PIN deprecation for eligible numbers.
Send a WhatsApp message when a Stripe payment fails (and when it succeeds)
Tell a customer on WhatsApp when their Stripe invoice payment fails, with a link to fix it, and send a receipt when it is paid. A signed-webhook Node server, two templates, no double sends on Stripe retries.
Send yourself WhatsApp alerts from a script (cron, CI, uptime check)
Get a WhatsApp message on your own phone when a cron job, CI run or health check fails. One approved template, one REST call, a Node script, a bash cron check and a GitHub Actions step, with no double alerts on retries.