An Australian registered training organisation (RTO) came to me with a CRM, not a problem. That's usually the tell. They had GoHighLevel already, paid for, full of contacts, and quietly working against them. Leads came in and nobody owned them. Forecasts looked healthy for reasons that had nothing to do with reality. A follow-up sequence that was supposed to chase students for missing documents had been silently stranding people for weeks.
None of that is a GoHighLevel failure. GHL is a genuinely good front door: pipelines, contacts, calendars, email and SMS, dashboards, all in one place. The failure was that nobody had ever treated the CRM as a system with invariants. It had grown one workflow at a time until the gaps between the tools became the place work went to die.
This is the story of turning that into an operating system: what the audit found, where GoHighLevel runs out of road and n8n picks it up, and the handful of automations that quietly do the work now.
Start with an audit, not a build
The instinct on a job like this is to start building the shiny thing. I did the opposite. Milestone one was a full audit of the account: every pipeline, every custom field, every workflow, each finding paired with a severity and a fix.
The findings were the kind you only see when you actually read the account instead of trusting it:
- Illogical win-probabilities on dead pipeline stages, quietly inflating the revenue forecast, so the number the team planned against was structurally wrong.
- 27 mirror-duplicated custom fields, the likely root of a long-standing bug where business names never displayed on B2B deals.
- Sensitive student fields stored without the access restrictions a regulated training provider needs, flagged in the audit for tightening as a compliance item.
- Around 244 contacts stranded inside five draft workflows that had never been published. They were in the CRM and, functionally, nowhere.
- A lead-assignment gap. Every new inquiry landed with no owner, because a round-robin to assign reps had simply never been built. New leads sat unclaimed until someone happened to notice them.
- A broken missing-documents chase that watched only one of the several document fields, had no "all docs received?" switch, and used mismatched keys. It had stranded 26 students mid-application.
You cannot automate your way out of any of that. Speed on top of a broken process just breaks things faster. So the first deliverable was the map: what's wrong, what it costs, and the order to fix it in. Most of the value of the whole engagement was decided here, before a single new workflow existed.
Where GoHighLevel stops and n8n starts
The central architectural decision was drawing a clear line between what GHL should own and what it can't do.
GoHighLevel is excellent at being the CRM: it holds the contacts, runs the pipelines, sends the email and SMS, books the calendar, and shows the native dashboards. It is the front door and the system of record. I kept it that way on purpose.
But there are things GHL genuinely cannot do natively, and no amount of clicking around the automation builder changes that. It can't average the number of days a deal spends in each stage. It can't build an email with a live, personalised checklist rendered inside it. It can't roll up net profit and amount due from six separate money fields. It can't keep a login token fresh so a background integration stays authenticated.
Those live in n8n, a self-hosted automation engine running on infrastructure the client owns. GHL handles the front end; n8n handles the logic GHL can't express. The trade is honest and I said so up front: this gives you far more capability, and it means there is now a second system to keep running. That's a real cost, not a footnote.
The result is twelve small, single-purpose workflows rather than one monster automation. Each does one thing, each can be toggled and inspected on its own, and each fails in a way you can see.
The automations that earn their keep
On this account the demo was never the point. Whether each workflow is still quietly correct in six months comes down to the parts nobody screenshots: the dedupe key, the failure ping, the token that stays fresh. Here are the ones that pulled the most weight.
A shared inbox that GoHighLevel says it can't have. GHL has no native shared mailbox. The department addresses (admin, sales, and the CEO alias) lived in Gmail, invisible to the team inside the CRM. I mirrored every inbound email into GHL Conversations, threaded under the real sender with attachments, and then built the reply path back out so a staff member can answer from inside GHL and have it send from the correct department alias. The trick that made it safe was posting the inbound mail as a "custom channel" rather than a normal email, so staff replies fire a webhook into n8n while every other email in the account keeps flowing untouched. Two-way shared inbox, no billing surprises.
A token broker so nothing quietly logs out. That shared inbox depends on an integration token that expires. The boring, invisible failure mode is that it lapses on a Friday and nobody notices until Monday. So a small workflow refreshes the token every few hours on its own. It replaced a manual daily re-paste that was never going to survive contact with a busy week.
A "time since last contact" counter on every deal. A card label that reads "2d 5h" and resets to "just now" the moment a rep sends a real, manual message. Bulk sends deliberately don't reset it, because a mass email is not the same as someone actually reaching out. Reps can now see at a glance which deals are going cold.
A personalised evidence checklist that fixed the bug that stranded 26 students. This is the one I'm proudest of. The old missing-documents chase failed because it was built on a blocking task step and only looked at one document field. The rebuild reads all five evidence groups on a student's record, keeps a running baseline of everything ever required, and renders a branded checklist inside every reminder email: each item shown as received (ticked and struck through) or still outstanding, plus a line that says "you've sent 4 of 6, just 2 to go." The cadence stops the instant the file is complete. The design rule I wrote down and never broke: never gate this on a blocking task step, because that is exactly how the old one stranded people.
An estimate "magic accept" link inside a note. When an estimate goes out, the workflow posts a single note on the deal with a link a rep can click to mark the estimate accepted or declined in one action, without digging into the payments area. When the status changes, the note rewrites itself in place with a green tick or a red block, the amount, and a timestamp.
An accounting calculator that only writes when something changed. It keeps each deal's net profit and outstanding balance current by computing them from the underlying money fields. The important detail is the guard: it only writes back when a value actually changed, so it never clutters the audit log with no-op updates. Idempotency is not a buzzword here, it's the difference between a clean history and a thousand phantom edits.
A won deal that files itself. When a deal is marked won and enrolled, it moves itself from Sales into Fulfillment, carrying its contact, value, and every custom field intact. A live test confirmed the move preserves the lead source with no overwrite. Reps stopped re-creating records by hand, which stopped creating duplicates.
Behind these sit the quieter ones: an audit log that records every change to a deal and, by cross-referencing the native log, who made it. A provisioner that spins up a private, access-controlled Google Drive folder the moment a student enrols, so identity documents never live in a "anyone with the link" folder. An hourly sweep that keeps the contact counters honest.
The dashboard GoHighLevel couldn't draw
Reporting had the same shape as everything else: GHL is great at what it can see and blind to what it can't. It knows your pipeline and your email stats. It has no idea what your website traffic, ad spend, or social reach looks like, because that data lives in other systems entirely.
So the reporting split in two. The GHL-native dashboards keep owning the CRM view: lead generation, the enrolment funnel, conversion. Then there's a bespoke, brand-matched dashboard, still being rounded out, hosted separately and embedded straight back into GHL through its embed widget. It already pulls live numbers from analytics, payments, and social into one page, with the paid-ad-spend and email sections on the near-term roadmap. It's token-gated, it refreshes itself every morning, and it has a manual refresh button for the impatient. Crucially, it shows the one thing GHL can't: the average number of days a deal actually spends in each stage, which is the difference between "our pipeline looks busy" and knowing exactly which stage deals quietly stall in.
What actually changed
I try not to sell outcomes I can't defend, so here are the concrete ones:
- The revenue forecast is no longer inflated by broken win-probabilities on dead stages.
- Business names were back-filled across 1,373 of 1,381 B2C RPL opportunities, closing a bug that had persisted for months.
- The bug that had stranded 26 students was fixed at the root, and the rebuilt chase is built so it can't strand anyone the same way again.
- Every new lead now gets an owner automatically, so nothing sits unclaimed.
- Around 244 contacts stranded in unpublished drafts were surfaced for a clear publish-or-archive decision instead of decaying unseen.
- Twelve automations run in the background, and the team can finally see time-in-stage, which no native GHL view offers.
Not everything was in the original contract. A good chunk of this (the token broker, the two-way inbox, the magic-accept note, the accounting calculator, the checklist engine, the custom dashboard) got built on top because the system was better with them and the marginal cost was small once the foundations were right.
The lesson under the build
The thing I keep relearning on jobs like this: the platform is rarely the problem. GoHighLevel was never the reason leads went cold or the forecast drifted from reality. The reason was that nobody had drawn the line between what the CRM owns and what needs a real automation layer behind it, and nobody had gone through the account assuming half of what it reported might be wrong.
Draw that line well, put the logic GHL can't do into a system that can, and make every automation loud when it fails and impossible to run twice. Do that and a leaking CRM stops being the place work goes to die and starts being the thing the business runs on. This business never needed a new platform. It needed someone to draw the line its old one never did.