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

  1. 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 variables array as cleanly.
  2. Method: POST. URL: https://sendfromchat.com/api/v1/messages.
  3. 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.
  4. Headers: one per line, Authorization = Bearer sfc_test_…, Content-Type = application/json, and Idempotency-Key = a value built from the record, such as order- plus the order ID plus -shipped. Zapier's article doesn't say whether Content-Type is added for you, so set it explicitly.
  5. Test the step. You should see 200 and "status": "test".

Step 3b: in Make

  1. Add an HTTP module, action Make a request.
  2. URL: https://sendfromchat.com/api/v1/messages. Method: POST.
  3. Headers: Authorization = Bearer sfc_test_… (Make's HTTP module also has a credentials option for authentication; a header works the same here), and Idempotency-Key built from the record's ID.
  4. Body type: raw, content type: JSON (application/json), and paste the JSON body with your mapped values.
  5. If the module has an option to parse the response as JSON, turn it on so later modules can read status and error.type; otherwise add a parse step.
  6. Check the module's Return error if HTTP request fails setting. When it is on, a 4xx or 5xx response stops the scenario and goes to your error handling. When it is off, the scenario carries on and you read error.type yourself. 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 reason
  • message.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 free