Integrations
Zapier, Make and form plugins
Triggers and actions for automation tools, and sending from Contact Form 7, WPForms, Gravity Forms and others.
The WordPress plugin exposes an authenticated REST surface on your own site with both directions covered: actions an automation can call to send a notification, and triggers that fire when something happens in your store. It works with Zapier, Make, n8n, Pabbly, or anything that can send an HTTP request.
The same class also bridges seven form plugins, so a contact-form submission can send a WhatsApp message without an automation tool in the middle at all.
Connecting an automation tool
Get the integration key
WhatsApp → Tools → Zapier key. It is shown masked. Press Regenerate to issue one, any Zap holding the old key stops working until you update it there too.Send it as a header
Every request carriesX-WAC-Key: <your key>. A logged-in user withmanage_woocommerce(ormanage_optionson a site without WooCommerce) is also accepted, which is what lets you test a route from a browser tab.Check the connection
GET /wp-json/wac/v1/zapier/authexists for exactly this. A tool that cannot prove the key works before a Zap is built gives the user a failure at the wrong moment.
curl https://your-site.com/wp-json/wac/v1/zapier/auth \
-H "X-WAC-Key: wac_zap_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
The REST surface
Everything is under /wp-json/wac/v1/zapier/ on your own domain.
- GET /zapier/auth
- Connection test. Proves the key is valid before anything is built on it.
- GET /zapier/events
- Every catalog event with its label, group, category, whether it is enabled, and a field list an automation tool can render a form from. Also returns the trigger list.
- POST /zapier/send
- The action. Sends one notification.
- GET /zapier/sample/:trigger
- A representative payload for a trigger, as a one-item list. Zapier builds the user's field mapping from this at setup time, before any real data exists. The samples are built by the same code that builds live payloads, because a sample whose keys drift breaks every Zap built against it and only in the customer's account.
- POST /zapier/subscribe
- Register a REST hook. Body takes
target_url(HTTPS) andtrigger. Returns a subscription id. - DELETE /zapier/subscribe/:id
- Remove a subscription.
- GET /zapier/poll/:trigger
- The 25 most recent items for a trigger. Zapier requires a polling endpoint even when the REST hook is what actually fires, and it is also the fallback for tools that cannot receive a webhook.
Triggers
- order_created
- A WooCommerce order was placed.
- order_status_changed
- An order moved status. Carries
previous_statusandnew_status. - message_received
- A customer replied to your WhatsApp number and the reply reached this site.
- form_submitted
- Any submission captured from a bridged form plugin, whether or not a notification rule matched it.
- cart_abandoned
- A cart was marked abandoned.
- optout
- A customer replied STOP, or was opted out by an admin.
- notification_sent
- A notification reached the platform.
- notification_failed
- A notification could not be delivered.
The action
| Field | Required | Type | Description |
|---|---|---|---|
| event | Yes | string | A catalog event key. The list is on Event catalog, or from /zapier/events.Example: order_shipped |
| to | Yes | string | Recipient phone number. E.164 with the country code. A national number is completed using the plugin's default country when one is set, and rejected when it is not. Example: +919876543210 |
| variables | No | object | The event's template variables, keyed by name. Send every one the event declares, an omitted variable puts a literal - in the customer's message rather than failing the send.Default: {} |
| language | No | string | Language override, when the template exists in more than one. Example: en_US |
| sync | No | boolean | Deliver inline and return the platform's message id instead of queueing. Slower, and the request now waits on WhatsApp, worth it only when the automation needs the id back. Default: false |
curl -X POST https://your-site.com/wp-json/wac/v1/zapier/send \
-H "X-WAC-Key: wac_zap_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"event": "order_shipped",
"to": "+919876543210",
"variables": {
"orderId": "#ORD-9001",
"awbCode": "AWB123456789",
"trackingLink": "https://yourstore.com/track/AWB123456789"
}
}'{
"success": true,
"queued": true,
"message_id": "",
"log_id": 4127
}queued: true with an empty message_id is the normal answer: the notification was accepted and the background pass will deliver it. Pass sync: true to get the id. A failure returns the plugin's own error code prefixed with wac_, as 400 when the request cannot succeed as written and 503 when it is worth retrying.
Form plugins
Seven form plugins are bridged, each with an admin-defined rule that maps its fields onto an event's variables. Configure them under WhatsApp → Forms.
- Contact Form 7
- WPForms
- Gravity Forms
- Elementor Pro Forms
- Fluent Forms
- Ninja Forms
- Forminator
Only the plugins actually installed are hooked, and only after init, several of them bootstrap late enough that asking earlier gets the wrong answer. Every adapter is wrapped so that a notification failure can never fail the submission: losing the visitor's enquiry is worse than losing the WhatsApp alert.

How a rule maps a form to an event
- Source and form ID
- Which plugin, and optionally which form. Leaving the form ID empty matches every form from that source, which is what a site with one contact form wants and saves looking an id up.
- Event
- Which catalog event to send. A rule carries its own enabled flag, so a lead alert built on
welcomedoes not require you to also switch on the customer-facing welcome message. - Phone field
- The field holding the number, matched case- and punctuation-insensitively so
Your Email,your-emailandyour_emailare the same field. Left empty, the plugin triesphone,your-phone,mobile,mobile_number,phone_number,tel,telephone,billing_phoneandwhatsappin that order. It also accepts a merge-tag expression when the number has to be built rather than read. - Variables
- One merge-tag expression per variable the event declares. Use
{field:your-name}for a submitted value; every ordinary tag such as{store_name}works too. An expression left blank falls through to the event's own mapping and then to the catalog default, so a rule only has to specify what differs. - Notify admin
- Sends the store its own copy of the lead to a number you type into the rule. That send skips the consent register on purpose. The recipient is your own staff number, which has never been through a checkout and so has no consent record to consult.
Source Contact Form 7
Form ID (empty, every CF7 form)
Event welcome
Phone field your-phone
customerName {field:your-name}
storeName {store_name}Values are truncated at capture, not at send, so the delivery log shows what the customer actually received. Nonces, referrers and captcha tokens are dropped before the fields reach the merge context, otherwise the first mistyped field name would put a captcha token in a WhatsApp message.
Each rule deduplicates on the submission reference, so a form plugin that fires its completion hook twice still sends one message.
Why form sends are rate limited
A public form is the one path where an anonymous stranger chooses both the trigger and the recipient. Everywhere else the platform messages a customer who reached the store through checkout. Here a visitor types a number into a box and the store messages it.
Without a ceiling that is a spam relay running on your credits: submit the form in a loop with a different number each time and every one receives a WhatsApp message. The consent register does not stop it, because a form submission has no order or account behind it and so there is no recorded choice to consult.
- 3 per phone per hour
- Stops one victim being messaged repeatedly through one form.
- 5 per IP per hour
- Stops one submitter working through a list of numbers. Counted from
REMOTE_ADDRand neverX-Forwarded-For, which is attacker-supplied. Behind a reverse proxy every visitor shares one address and this check stops being useful, which is why the third one exists. - 100 per hour overall
- The backstop that still holds when the other two are evaded by a botnet. This is the limit that bounds the credit loss.
A refusal is written to the delivery log as a skip with its reason, rather than disappearing , and only successful sends count against the ceilings, so a submission with no usable phone number does not use up a real visitor's allowance. All three are filterable in code if your traffic genuinely needs different numbers.
Calling the platform from Zapier instead
If you are not on WordPress, a Zapier or Make webhook action can post to the platform's own API directly with your sk_live_ key. That is the same request the plugin makes, without the site in the middle.
See Custom and headless stores for a worked example including error handling, and Notifications API for the endpoint reference.
