WhatsApp automations
Send approved WhatsApp templates automatically from the automations engine — triggers, the send_whatsapp action, guard-chain failure codes and opt-out handling.
WhatsApp automations
Once WhatsApp is connected and you have at least one approved message template, you can send it automatically. The automations engine supports a send_whatsapp action that attaches a template to any trigger — a new lead, a stage change, an appointment reminder, and more.
Who this is for
- Admin or Manager building follow-up flows.
- Super Admin diagnosing why an automation's WhatsApp step failed.
Difficulty: Medium · Time: about 15 minutes · Prerequisites:
- WhatsApp connected (Settings → Connections → WhatsApp) and healthy.
- An approved template (see Message templates).
- The
automationspermissions for your role.
Step 1 — Create the automation
- Open Automations (
/admin/automations) and click New automation. - Give it a name and choose a trigger:
| Trigger | Fires when |
|---|---|
| New Lead Received | A lead is created (any source) |
| Lead Stage Changed | A lead moves stage in the sales process |
| Lead Assigned to User | Routing assigns a lead to a salesperson |
| Tag Added to Lead | A tag is attached |
| Lead Score Crosses Threshold | Score-based hand-raise |
| No Activity (Time-based) | A lead goes quiet for the configured period |
| Date Reached (birthday / anniversary) | A lead date field is due (checked daily at 07:00) |
| Form Submitted | A Meta lead form submission is ingested |
| Manual Trigger | Only when fired by hand — great for testing |
| Appointment booked | A booking is created |
| Appointment approaching | Swept every 5 minutes; choose the lead time — 1, 3, 6, 12 or 24 hours before (default 24) |
| Follow-up due | Swept every 5 minutes; fires when a follow-up task reaches its due time |
- Optional conditions (stage, tag, source…) narrow when the automation runs.
Step 2 — Add the send_whatsapp action
- Add an action and choose Send WhatsApp.
- Pick a template from the template selector. The dropdown lists only templates that are approved (Meta-approved, or a legacy local template that is still usable). If the list is empty, approve a template first.
- The CRM shows a variable preview under the selector — the tokens each placeholder will be filled with, resolved from the template's mapping. You do not retype variables per automation.
Legacy automations keep working Automations created before the managed-template flow used mode: text (a free-text message with {{first_name}}-style tokens) or mode: template with a name, language and manual parameter rows. Those fields still render when no managed template is selected, so older automations stay editable and saveable.
Step 3 — Test it
- Set the trigger to Manual Trigger (or use the trigger's natural event with a test lead from Test your setup).
- Run the automation — with the local
syncqueue it executes immediately; in production it is picked up by the worker within a minute. - Open Automations → Run history on the automation row to see the run and each step's outcome.
Step 4 — Understand failures before they confuse you
The send_whatsapp action verifies conditions at send time in a fixed guard order. The first guard that fails becomes the step's failure_code:
| Order | Failure code | Meaning |
|---|---|---|
| 1 | TEMPLATE_UNAVAILABLE |
The chosen template no longer exists (deleted locally) |
| 2 | TEMPLATE_NOT_APPROVED |
The template is not approved anymore (rejected/disabled/paused) |
| 3 | OPTED_OUT |
The lead sent a STOP keyword — deliberately skipped |
| 4 | WHATSAPP_NOT_CONNECTED |
The workspace's WhatsApp connection is missing or disconnected |
| 5 | NO_VALID_PHONE |
The lead has no usable phone number |
| 6 | MISSING_TEMPLATE_VARIABLE |
A mapped token has no value at send time |
| 7 | MEDIA_NOT_FOUND |
The template needs media the CRM cannot locate |
| — | WHATSAPP_ERROR |
The queue call to Meta failed unexpectedly |
Opt-out skips are silent by design When the lead has opted out, the run skips the step with OPTED_OUT and writes no CRM message row or timeline entry — that is policy, not a bug. Every other failure writes a failed LeadMessage with its failure_code, visible in the lead's message history and the run history. The action never throws — a failure can't break the rest of the automation.
Opt-outs (STOP keywords)
If a lead replies with an opt-out keyword, the CRM automatically flags them:
- Default keywords: stop, unsubscribe, do not message.
- Matching is exact after normalisation (lowercased, punctuation/spacing removed) — "Please STOP." matches, "stopping by later" does not.
- On match, the lead is marked opted out (
whatsapp_opted_out_at, sourceinbound_keyword) with a reason, and a timeline entry on the lead.
No dedicated keyword screen (yet) The keyword list has no settings screen in the UI today. It lives in the tenant setting automation.whatsapp_optout_keywords; a Super Admin can change it per workspace. Treat this as an operator change, not self-service.
Expected result
The automation fires on its trigger and the lead receives the approved template from your business number. The run history shows a successful step; failures show one of the codes above so you know exactly which guard failed.
Common mistakes
- Choosing a template, then editing it on Meta. If Meta rejects/disables it later, sends fail with
TEMPLATE_NOT_APPROVED— pick another approved template. - Sending free-form expectations. Automations always send a template; free-form chat happens manually from the inbox inside the 24-hour window.
- Testing with a lead whose phone is empty →
NO_VALID_PHONE. - Forgetting the worker. With
QUEUE_CONNECTION=sync(local) sends run inline; in production a queue worker must run or automations sit in the queue — see Cron & queue workers.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Step shows TEMPLATE_UNAVAILABLE |
Template deleted from the CRM | Re-create and resubmit; reselect in the automation |
Step shows TEMPLATE_NOT_APPROVED |
Meta disabled/rejected it | Check Message templates statuses |
Step shows MISSING_TEMPLATE_VARIABLE |
A mapped token (e.g. appointment.date) has no value — e.g. no upcoming appointment |
Check the lead's data; appointment tokens read the next upcoming confirmed/scheduled/pending booking |
| Nothing happens at all | No worker in production | Run the scheduler + worker per Cron & queue workers |
| Reminders fire at the wrong time | appointment_approaching lead time is wrong |
Edit the trigger config (hours before: 1/3/6/12/24) |
Technical details
- Action class:
SendWhatsAppAction(registered as thesend_whatsappautomation action); failure propagation viapullLastFailure(). - Sends are queued as
LeadMessagerows and dispatched by theSendLeadMessagejob. - Sweeps that drive time-based triggers:
CheckAppointmentApproachingandCheckFollowUpDueevery 5 minutes;date_fielddaily at 07:00 — all registered inroutes/console.php, so the scheduler must run every minute. - Opt-out handling:
WhatsAppMessageService::maybeOptOut(); flags persist on the lead (whatsapp_opted_out_at,whatsapp_opt_out_source, reason + timeline entry). - Run visibility:
AutomationRunHistorypage per automation; failed steps carry the guards' codes.
Related guides
Was this page helpful?
Thanks — noted. ✦