· 5 min · TextMeFlow Team

Sending WhatsApp Notifications from Salesforce Flow

Salesforce doesn't fire outbound webhooks the way Zoho or HubSpot do. Instead, record changes are wired to actions through Flow Builder, and the way to reach an external API is an HTTP Callout action inside a Record-Triggered Flow. It's a different mechanism, but the end result is the same: a rep or manager gets a WhatsApp message the moment something changes, instead of an email alert that sits unread.

This walks through wiring a Record-Triggered Flow on Opportunity or Case to TextMeFlow's messages API, with no Salesforce AppExchange package required.

Step 1: Define the external service (or a simple Apex callout)

Flow's HTTP Callout action needs either a registered External Service (from an OpenAPI schema) or a small invocable Apex class. For a single endpoint like POST /v1/messages, an Apex class is less setup than importing an external service definition:

public class TextMeFlowSender {
    @InvocableMethod(label='Send WhatsApp via TextMeFlow')
    public static void send(List<Request> requests) {
        for (Request req : requests) {
            HttpRequest httpReq = new HttpRequest();
            httpReq.setEndpoint('https://api.textmeflow.eu/v1/messages');
            httpReq.setMethod('POST');
            httpReq.setHeader('Authorization', 'Bearer ' + TextMeFlowSecret.getToken());
            httpReq.setHeader('Content-Type', 'application/json');
            httpReq.setBody(JSON.serialize(new Map<String, String>{
                'to' => req.phone,
                'text' => req.message
            }));
            new Http().send(httpReq);
        }
    }

    public class Request {
        @InvocableVariable(required=true) public String phone;
        @InvocableVariable(required=true) public String message;
    }
}

Store the API token in a Custom Metadata Type or Named Credential rather than hardcoding it — TextMeFlowSecret.getToken() above is a stand-in for whichever secret-storage pattern your org already uses. Add api.textmeflow.eu to Setup → Security → Remote Site Settings, or the callout fails with an UNAUTHORIZED_ENDPOINT error before it ever reaches TextMeFlow.

Step 2: Build the Record-Triggered Flow

In Setup → Flows, create a new Record-Triggered Flow on Opportunity, set it to run "only when a record is updated to meet the condition", and set the entry condition to StageName = 'Closed Won' with "Only when specified changes are made" so it doesn't refire on every save.

Add an Action element after the trigger, pick the invocable method (Send WhatsApp via TextMeFlow), and map:

  • phone → the owner's mobile field (or the Opportunity Owner's related User record's phone)
  • message → a text template built from {!$Record.Name} and {!$Record.Amount}

Run the Flow After Save and asynchronously if you don't need the callout to block the save transaction — most notification use cases don't, and running async avoids adding latency to the user's save action.

Step 3: Normalize phone numbers before the callout

Salesforce phone fields are free text, same as most CRMs, so a rep might have entered 0472 93 22 08, +32 472 93 22 08 or 0032472932208. Normalize before the send rather than after a failed API call:

public static String toE164(String raw, String defaultCountry) {
    String digits = raw.replaceAll('[^0-9]', '');
    if (raw.startsWith('+')) return '+' + digits;
    if (digits.startsWith('00')) return '+' + digits.substring(2);
    if (digits.startsWith('0')) return '+' + defaultCountry + digits.substring(1);
    return '+' + digits;
}

Call this from the Apex class before building the request body, and skip the callout entirely (log it instead) if the result doesn't look like a plausible E.164 number. The same normalization logic works regardless of which language builds the request — PHP, Python and Node examples are in the docs if you're routing the callout through a middleware endpoint instead of Apex directly.

Step 4: Handle bulk updates safely

Record-Triggered Flows can run in bulk — a mass update from a list view or a data-loader import can trigger the Flow for hundreds of records at once. Since the invocable Apex method above accepts a List<Request>, Flow batches callouts across bulk operations automatically, but Salesforce also caps synchronous callouts at 100 per transaction. For anything that could plausibly touch more than 100 records at once (a bulk Opportunity stage change via data loader, for instance), run the Flow asynchronously or route through a Queueable Apex job instead of a direct synchronous callout, or the extra records will simply fail silently past the limit.

Step 5: Keep it to internal notifications

This pattern is for alerting your own team — a rep when their deal closes, a support manager when a Case is escalated to high priority, a finance user when an invoice-related Opportunity moves to a billing stage. It is not a way to message the customer or contact on the record without their prior opt-in; that's customer messaging and needs proper consent handling under TextMeFlow's anti-spam pipeline (rate limits, risk scoring, STOP handling, quiet hours), not an internal Flow callout.

What this is good for

Good fits: notifying an Opportunity owner the instant a deal is marked Closed Won, escalation alerts for high-priority Cases, pinging an approver when a Quote needs sign-off. For anything customer-facing — order confirmations, appointment reminders sent to the contact themselves — build that as a separate flow with documented opt-in, not by reusing the internal notification Flow above.

Start free on TextMeFlow — 50 messages a month, no time limit, enough to build and test a Salesforce Flow callout 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