· 5 min · TextMeFlow Team

Sending WhatsApp Notifications from Zoho CRM Webhooks

Zoho CRM's workflow automation can fire an outbound webhook whenever a record changes — a lead moves to "Qualified," a deal hits "Closed Won," a task becomes overdue. Most teams wire that into an email alert that sits unread in an inbox. Routing the same event to WhatsApp instead gets it in front of the rep who needs to act, since a WhatsApp message gets opened in minutes rather than hours.

This is the same webhook-to-API pattern used for other CRMs: a Zoho workflow rule, a small receiver you host, and TextMeFlow's messages API. No Zoho Marketplace extension to install or maintain.

Step 1: Create an outgoing webhook in Zoho CRM

In Zoho CRM, go to Setup → Automation → Workflow Rules and create a rule scoped to the module you care about (Leads, Deals, Tasks). Set the trigger — "When a record is edited" or "When a field is updated" — then add an Instant Action → Webhook to the rule. Zoho lets you map which fields from the record get sent in the webhook body, so pick the ones you'll need downstream: record id, owner email or phone, the field that changed, and its new value.

Zoho webhooks don't support HTTP Basic Auth out of the box, so add a shared-secret query parameter or header value to the URL you configure (https://yourapp.com/zoho-hook?key=...) and check it in your receiver before doing anything else — otherwise the endpoint accepts unauthenticated POSTs from anyone who finds the URL.

Step 2: Build a receiver that maps the owner to a phone number

Zoho sends the fields you mapped as either form-encoded or JSON, depending on how you configured the webhook action. A Node receiver for a "Lead status changed" rule looks like this:

import express from 'express';
const app = express();
app.use(express.urlencoded({ extended: true }));

app.post('/zoho-hook', async (req, res) => {
  if (req.query.key !== process.env.ZOHO_WEBHOOK_KEY) {
    return res.sendStatus(401);
  }

  const { lead_status, owner_phone, lead_name } = req.body;
  if (lead_status !== 'Qualified' || !owner_phone) {
    return res.sendStatus(200);
  }

  await fetch('https://api.textmeflow.eu/v1/messages', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.TEXTMEFLOW_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      to: owner_phone,
      text: `Lead "${lead_name}" just moved to Qualified. Follow up today.`,
    }),
  });

  res.sendStatus(200);
});

Zoho's workflow rule fires on every save that matches the trigger, so the lead_status !== 'Qualified' guard matters — without it you'd send a message on any edit to a lead that's already qualified, not just the transition into that status.

Step 3: Normalize phone numbers before sending

Zoho stores phone fields as free text, entered however the sales rep typed it. Normalize to E.164 and skip the send rather than guessing when a number doesn't parse cleanly:

function toE164(raw, defaultCountry = '32') {
  const digits = raw.replace(/[^\d]/g, '');
  if (raw.startsWith('+')) return '+' + digits;
  if (digits.startsWith('00')) return '+' + digits.slice(2);
  if (digits.startsWith('0')) return `+${defaultCountry}${digits.slice(1)}`;
  return `+${digits}`;
}

The same logic applies whether you're building the receiver in PHP, Python or Node — TextMeFlow's REST endpoint doesn't care which language calls it, and integration examples for all three are in the docs.

Step 4: Use Blueprint transitions for approval-gated stages

If the stage change you care about goes through a Zoho Blueprint (a formal approval process rather than a plain field update), the workflow rule trigger is "When a Blueprint transition occurs" instead of a field edit. This matters for things like "Deal moved to Contract Sent after manager approval" — a plain field-update trigger would fire on the raw field change even before the approval step completes, and you'd notify the rep too early.

Keep the receiver idempotent

Zoho can redeliver a webhook if your endpoint doesn't respond quickly or returns a non-2xx status. Dedupe on record id plus the field value you're reacting to (lead id + "Qualified", deal id + "Closed Won") so a retry doesn't send the same WhatsApp message to the same rep twice. A unique constraint on that pair in whatever table logs sent notifications is enough — no custom dedup logic needed.

What this is good for

Good fits: alerting a rep the moment their lead qualifies or their deal closes, pinging a manager when a high-value deal has sat in one stage too long, notifying a team lead when a task is marked overdue. Poor fit: messaging the lead or contact themselves through this pattern without their prior opt-in — that's customer messaging and needs proper consent handling, not an internal sales alert. Keep this pattern to internal, one-to-one notifications to your own team, which also keeps volume low and away from anti-spam risk scoring.

Start free on TextMeFlow — 50 messages a month, no time limit, enough to wire up and test a Zoho CRM webhook before deciding on a paid plan.

Zelf WhatsApp-berichten versturen via API?

Gratis voor altijd tot 50 berichten/maand. QR scannen en binnen 5 minuten verstuur je je eerste bericht.

Gratis voor altijd