A client called us in a panic last spring. One of their leads had posted a screenshot on Facebook: six identical "Hi Sarah, still interested in a quote?" texts, all inside 52 minutes, all from the same business. The comments were brutal. The lead was gone, and so was any goodwill.

Nobody hit "send" six times. Nobody wrote a bad message. The copy was fine. What happened was three workflows all firing off the same tag, a re-entry setting nobody checked, and no wait-step to catch the overlap. Classic GoHighLevel build rot — the kind that accumulates quietly across a few dozen automations until one contact trips every wire at once.

Full disclosure: I work for ReadySMS, and part of why we built our GHL integration the way we did is that we kept watching agencies discover this problem after the send, on the invoice. Let me walk through how duplicate sends actually happen in GHL, and the guardrails that stop them.

Why one contact ends up in six workflows

Duplicate texts almost never come from one workflow misbehaving. They come from several workflows agreeing to fire on the same signal. Here are the four patterns I see most:

  1. Stacked triggers on one event. You've got a "New Lead Welcome" workflow, an "SMS Nurture Day 0" workflow, and a "Speed-to-Lead Auto-Text," all triggered by Contact Created or Tag Added: lead. One form submission = three texts, back to back.
  1. Tag loops. Workflow A adds the tag hot. Workflow B is triggered by hot and, somewhere in its logic, re-adds it or adds nurture, which re-triggers Workflow A. The contact ping-pongs between two automations until something breaks the cycle — usually your budget.
  1. Re-entry left on. GHL's Allow Re-Entry toggle does exactly what it says: if a contact matches the trigger again, they run the workflow again. Fine for a monthly newsletter. Catastrophic on a "form submitted" trigger when someone double-clicks the submit button or refills the form.
  1. Trigger-link and campaign migration overlap. Old Campaigns (the deprecated ones) still running alongside new Workflows, both watching the same pipeline stage. The contact moves one stage, both fire.

The nasty part is that none of these are visible from any single workflow's canvas. You have to look across the whole account, which is why the 20-minute audit approach we've written about matters — you're hunting for overlap, not for one broken node.

The wait-step is your cheapest insurance

The single most effective guardrail costs nothing and takes 30 seconds to add: a wait step at the very top of any SMS-sending workflow, gated on a condition.

The pattern:

  • Trigger fires → Wait 2 minutesIf/Else: has this contact been texted in the last 10 minutes? → if yes, drop out of the workflow; if no, send.

You track "last texted" with a custom date/time field that each SMS workflow stamps when it sends. Any other workflow that's about to fire reads that field first. It's crude, but it turns "six texts in an hour" into "one text, and five workflows that quietly exit."

The 2-minute wait also absorbs the double-click problem. Re-entry from a form resubmit lands inside the wait window, hits the "already texted" branch, and dies.

Dedup logic that survives complex builds

For agencies running 40+ workflows per sub-account, per-workflow guards aren't enough — you need a single choke point. The cleanest version:

  • Every workflow that wants to send an SMS doesn't send directly. It adds a tag like sms-queue-welcome and exits.
  • One dedicated "SMS Dispatcher" workflow is triggered by any sms-queue-* tag. It checks the last-texted field, enforces a minimum gap, sends, stamps the field, and removes the queue tag.

Now every outbound text funnels through one place with one set of rules. When something goes wrong, you have exactly one workflow to audit instead of forty. It also gives you a natural home for a frequency cap — "no more than 3 marketing texts to any contact per 7 days" — which is genuinely hard to enforce when sends are scattered.

Is this over-engineering for a five-workflow account? Yes. Don't build it until you feel the pain. For a shop managing complex client builds, it pays for itself the first time it catches a tag loop.

Filters that prevent the loop in the first place

Wait-steps catch overlap after it happens. Filters stop it from starting.

  • Never re-add a tag inside a workflow that another workflow triggers on. If Workflow B needs to know a contact is hot, have it read the tag, not re-stamp it.
  • Turn off Allow Re-Entry on anything triggered by contact creation, form submission, or a one-time pipeline move. Leave it on only for genuinely recurring intents.
  • Add trigger filters, not just workflow filters. GHL lets you filter at the trigger level (e.g. "Tag Added: lead AND contact does not have tag already-welcomed"). Filtering at the trigger means the contact never enters the workflow, so there's nothing to undo.
  • One trigger event, one workflow. If three things need to happen on Contact Created, chain them inside a single workflow with wait-steps, rather than three workflows racing.

Where the money actually leaks

Here's the part that makes this a budget problem, not just a reputation one. Duplicate sends multiply your segment count silently.

Say your welcome text is 172 characters — over the 160-character single-segment limit, so it splits into 2 segments at 153 chars each. (If you're not sure why 172 characters isn't "one long text," the 160-vs-153 boundary post explains the cliff.)

Now a tag loop fires that welcome 6 times to 500 new leads in a bad week:

Segments per contactContactsTotal segmentsCost @ $0.02 + $0.0045
Intended (1 send)25001,000$24.50
Loop (6 sends)125006,000$147.00

That's $122.50 burned on a single week's leak — and that's a small account. The intended spend was $24.50. Trim that welcome text to under 153 characters and each intended send is 1 segment, halving even the correct number (here's how a 12-character trim did exactly that). But no amount of copy-trimming saves you from a 6x multiplier.

How the ReadySMS sync surfaces it before it burns

This is where the integration earns its keep. Because ReadySMS syncs two-way with GHL over OAuth — mapped per location, so agency sub-accounts stay isolated — every outbound send lands in a unified inbox and in a per-location send log with real segment counts and spend.

That matters because GHL's own conversation view shows you messages, but the segment math and the running dollar total live where the sending actually happens. A tag loop that fires 6x shows up as a spend spike on the ReadySMS side within minutes, not on next month's carrier statement. You can watch a per-location send count climb in real time and kill the workflow before it drains prepaid credits.

Since billing is prepaid credits, a runaway loop hits a wall — it can't quietly rack up a five-figure overage the way a postpaid metered account can. And because the carrier pass-through is a separate line item rather than baked into a rounded rate, the spend spike is legible when you go looking for the cause.

None of this prevents the loop — your workflow guardrails do that. But it shortens the gap between "the loop started" and "someone noticed" from a billing cycle to a coffee break.

A pre-launch checklist for any SMS workflow

Before you flip a client workflow live, run this:

  • [ ] Is Allow Re-Entry off (unless the intent is genuinely recurring)?
  • [ ] Does any other workflow trigger on the same event or tag? Map it.
  • [ ] Does this workflow re-add a tag that another workflow triggers on? (Loop risk.)
  • [ ] Is there a wait-step + "recently texted?" check before the send?
  • [ ] Is the message under 153 characters, or do you intend to pay for multipart?
  • [ ] Have you sent a test through a real number and watched it land once in the ReadySMS inbox?

The takeaway

Duplicate texts are an architecture problem wearing a copywriting costume. The fix isn't better message wording — it's turning off re-entry, filtering at the trigger, adding a wait-step gate, and for bigger builds, funneling every send through one dispatcher. Then you use the send log as your smoke alarm.

If you're auditing an account you inherited, start with the four-mistakes teardown and work outward from your busiest trigger. And if you want to see the per-location spend surface as it happens, the GHL setup guide walks through connecting a location — you get 2,500 free credits to test a workflow end-to-end before a single real lead is on the line.