The Zero-Downtime HubSpot-to-GoHighLevel Migration Playbook: How a 22-Person Agency Moved 5 Years of Data Without Losing a Single Tag
A staged, parallel-run playbook for moving an established agency off HubSpot to GoHighLevel — no lost tags, no double-sends, no dark campaigns.
In short
Yes — an established agency can move from HubSpot to GoHighLevel with zero downtime, but only with a staged, parallel-run migration rather than a big-bang CSV export-and-import. Zero downtime comes from overlap, not speed: both systems stay live for a two-to-four-week window while GHL is built, loaded, and validated against HubSpot before anything is switched off. The work that actually protects you is a field-mapping matrix built before any data moves, reading workflow enrollment state from HubSpot's API so mid-sequence contacts are never double-sent, and warming a new sending domain for weeks so deliverability holds. You cut over in slices — new lead capture first, then automations, then reporting — with HubSpot as a rollback at every step. Done this way, no tags are lost, no contact is emailed twice, and no client's live campaign goes dark.
Key takeaways
- Zero downtime comes from overlap, not speed — a parallel-run migration keeps HubSpot and GHL live together for two to four weeks so there is always a rollback and no lead ever has nowhere to land.
- The field-mapping matrix, built before any data moves, is the technical heart of a clean migration: every HubSpot property, list, and lifecycle stage gets an explicit GHL destination as a tag or custom field.
- Opt-out and suppression data is the row you cannot get wrong: HubSpot opt-out, marketing-contact status, and Do Not Contact lists must map to GHL's DND settings and suppression tags, verified twice.
- A plain CSV export has no concept of workflow enrollment state, so avoiding double-sends means reading who is at what step from HubSpot's API, then draining short sequences in HubSpot and re-enrolling long ones at the correct step in GHL.
- A new GHL sending domain has no reputation, so authentication and a gradual multi-week warmup must run in parallel with the build, not on cutover day.
You already know the math. That's why you're reading this.
HubSpot's invoice crossed some line it was never supposed to cross. Maybe it was the Marketing Hub Professional seat increase, or the moment you added ClickFunnels and Calendly on top and realized you were paying three separate vendors to do one job across a stack that barely talks to itself. Maybe a client asked why their monthly software line item keeps climbing while their results stay flat. Whatever the trigger, you did the spreadsheet, and GoHighLevel came out thousands of dollars cheaper per month — with the added upside that you could finally white-label the whole thing and stop reselling someone else's logo.
So the destination isn't in question. GoHighLevel wins on cost, on consolidation, and on the agency business model. The destination is easy.
The migration is what keeps you up at night.
Because you're not a fresh startup with a clean contact list and no history. You're an established agency with five years of accumulated data — tags applied by three different team members using three different naming conventions, workflows that have been quietly enrolling contacts and firing emails every single day since 2021, live sequences that your clients' revenue depends on, and a contact database where the history is worth more than the contacts themselves. Lose the tags and you lose your segmentation. Lose the workflow enrollment state and you double-send or drop leads mid-nurture. Break a live sequence at the wrong moment and a client's launch campaign goes dark — and they find out before you do.
This is the real fear, and it's a rational one: not "can we move to GHL" but "can we move to GHL without breaking what's running right now."
The answer is yes. But not by exporting a CSV, importing it, and hoping. It takes a staged, parallel-run migration with rigorous data mapping and a cutover checklist that treats your live campaigns like a surgeon treats a beating heart — you don't stop it, you reroute around it.
This post is that playbook. We're going to walk through an actual migration profile — Cardinal & Co., a 22-person agency that was paying $3,200/month across HubSpot, ClickFunnels, and Calendly — and reconstruct the entire move, from the first data audit to the final DNS cutover, in the level of technical detail you need to either do it yourself or know exactly what to demand from whoever does it for you.
By the end you'll understand: how contact and tag data actually maps between the two platforms, why HubSpot workflows don't translate one-to-one into GHL workflows and what to do about it, how to warm a new sending domain so your deliverability doesn't crater on day one, and — most importantly — how the parallel-run window lets you cut over with a rollback option instead of a leap of faith.
Let's start with why this is hard, so we can be precise about making it safe.
Part 1: The Situation — Why Agency Migrations Are a Different Animal
Cardinal & Co.'s stack, and why it had to go
Cardinal & Co. is a composite, but every number here is drawn from what a real 22-person agency at roughly $2.4M/year actually runs into. Here's what their stack looked like the month they decided to move:
- HubSpot Marketing Hub Professional — the core CRM, marketing automation, forms, landing pages, email, and reporting. This was the expensive centerpiece. With their contact tier and a handful of paid seats, this alone was north of $2,000/month.
- ClickFunnels — used for two of their higher-converting client funnels and a couple of their own lead-gen offers, because the team preferred its funnel builder to HubSpot's landing pages. Roughly $300/month.
- Calendly — team-wide scheduling, embedded on client booking pages and used internally. Several paid seats.
- Plus the usual scattered subscriptions: a separate SMS tool bolted onto HubSpot via a paid integration, a form widget, a review-request tool for a few local-business clients.
All in, about $3,200/month — roughly $38,400/year — for a stack that still required their team to manually stitch data between systems. A lead booked on Calendly didn't automatically know what funnel it came from in ClickFunnels. SMS lived in a bolt-on. And every client sub-account was really just a segment inside one big HubSpot portal, which made client offboarding a nightmare and white-labeling impossible.
GoHighLevel replaces all of it. One agency account, unlimited sub-accounts (one per client), native funnels, native calendars, native email and SMS on one contact timeline, and a snapshot system that lets you standardize a whole client build and clone it. On the Agency Pro plan, Cardinal's software cost was projected to drop from $3,200/month to roughly $500/month — a swing of about $2,700/month, or over $32,000 a year, before you even count the reselling margin they could now make by charging clients for sub-accounts.
The business case wrote itself. Now look at what stood in the way.
What's actually at risk
When people say "migration," they picture moving contacts. Contacts are the easy part. What's genuinely at risk in a HubSpot-to-GHL move is everything attached to those contacts and everything acting on them:
1. Tags and segmentation logic. Cardinal had accumulated hundreds of tags over five years — lead source tags, lifecycle tags, product-interest tags, client-of-record tags, event-attendance tags, suppression tags. Except HubSpot doesn't call them all "tags." Some of that segmentation lived in actual HubSpot tags-equivalents, but a huge amount lived in custom properties, lists (active and static), and lifecycle stages. GHL has tags, custom fields, and smart lists — but the mapping is not one-to-one. Get it wrong and a contact that should be suppressed from a promo gets the promo.
2. Custom fields / properties. Five years of custom HubSpot properties — some system, some created by team members, some created by a former contractor nobody can ask anymore. Each one needs a decision: migrate as a GHL custom field, migrate as a tag, or drop.
3. Workflow and sequence enrollment state. This is the one that scares experienced operators. In HubSpot, a contact isn't just "in a workflow" — they're at a specific step, with a specific next action scheduled. A contact might be on day 4 of a 9-email onboarding sequence, with email 5 scheduled to send Tuesday. Nothing in a standard CSV export captures "this person is mid-sequence at step 4." If you naively rebuild the sequence in GHL and re-enroll everyone from the top, that day-4 contact gets email 1 again. Now multiply that by every active enrollment across every client.
4. Live automations that fire on real events. HubSpot workflows are running right now. Form submissions trigger internal notifications and lead-routing. Deal-stage changes trigger client-facing emails. Cart or booking events kick off nurture. The moment you "turn off" HubSpot, those triggers stop — and if GHL isn't ready to catch them at that exact moment, leads fall on the floor.
5. Deliverability and sending reputation. HubSpot sends Cardinal's marketing email from a warmed, authenticated sending setup that's been building reputation for years. Spin up a brand-new sending domain in GHL and blast your full list on day one, and mailbox providers treat you like a spammer who appeared overnight. Open rates crater, and some of that reputation damage is slow to undo.
6. Live client-facing assets with real URLs. ClickFunnels funnels and Calendly links are embedded on client websites, in email signatures, in running ad campaigns, and on business cards. Those URLs are load-bearing. You can't just delete the funnel and expect traffic to find the new one.
7. Team knowledge. Twenty-two people know HubSpot. Nobody knows GHL yet. A migration that succeeds technically but leaves the team unable to operate the new system on Monday morning has failed operationally.
Every one of these is a place where a careless migration loses data, breaks a live campaign, or embarrasses you in front of a client. The rest of this playbook is about neutralizing each one — on purpose, in order, with a safety net.
The one principle that makes it safe: parallel-run, not big-bang
Here's the core idea, and everything downstream flows from it.
A big-bang migration turns HubSpot off and GHL on at the same instant. Everything rides on that instant being perfect. There's no rollback. If you discover at hour two that workflow enrollments didn't carry and half your onboarding sequences are re-firing from step one, your only options are to scramble or to eat the damage. This is how migrations become disasters, and it's why "we tried to switch and it was a mess" is such a common story.
A parallel-run migration keeps both systems alive at once for a defined window — typically two to four weeks. HubSpot keeps doing its job. GHL gets built, loaded, tested, and validated against the live system while the live system is still catching everything. You cut over one slice at a time — new lead capture first, then sequences, then reporting — and you don't decommission HubSpot until GHL has proven it's catching everything HubSpot was. If something breaks, HubSpot is still there. You reroute back, fix it, and try again.
That's the whole philosophy: zero downtime comes from overlap, not speed. You are never in a state where a lead has nowhere to land. Now let's build it.
Part 2: Phase One — Audit and Data Mapping (Before You Move a Single Contact)
The temptation is to start by exporting contacts. Resist it. The single biggest predictor of a clean migration is the quality of the audit and mapping you do before anything moves. For Cardinal, this phase took about a week of focused work, and it's the phase that pays for itself ten times over.
Step 1: Inventory everything in the source system
You cannot migrate what you haven't catalogued. Before touching GHL, produce a written inventory of the HubSpot portal (and the ClickFunnels and Calendly accounts). This is a spreadsheet, not a vibe. HubSpot's own export records documentation covers how to pull each object type out, and there is a CRM exports API if you would rather script it than click it. Document:
- Contacts: total count, broken down by lifecycle stage and by owner. Cardinal had ~74,000 contacts across the portal.
- Properties / custom fields: export the full list of contact, company, and deal properties. For each, note the internal name, field type (single-line text, dropdown, date, number, multi-checkbox), and whether it's actively used. Cardinal had 180+ custom properties; about 60 were genuinely in use.
- Tags / lists: every active list, static list, and the criteria behind each. Note which are used as suppression lists — those are safety-critical.
- Lifecycle stages and deal pipelines: the full stage definitions for every pipeline. Cardinal ran four pipelines across their client work.
- Workflows: every active workflow, its enrollment trigger, its actions, its enrollment criteria (does it re-enroll?), and roughly how many contacts are currently enrolled. Cardinal had 90+ workflows; about 55 were active and meaningful.
- Forms: every form, where it's embedded, and what it triggers.
- Email sequences and marketing emails: active sequences, templates, and any scheduled sends.
- ClickFunnels: every funnel, its pages, its URLs, and its integrations.
- Calendly: every event type, its URL, its availability rules, and where it's embedded.
This inventory is your map and your acceptance test. When you later ask "did everything migrate?", this is the document you check against.
Step 2: Decide what NOT to migrate
A migration is the best chance you'll ever get to shed dead weight. Of Cardinal's 180 custom properties, 120 were unused, duplicated, or created for a one-off campaign in 2022. Of 90 workflows, 35 were inactive, redundant, or replaced. Do not migrate garbage — you'll just be paying to maintain it in a new system.
For each item in the inventory, mark a disposition: Migrate, Consolidate (merge with another), or Archive (leave behind, keep in a HubSpot export for the record). Get client sign-off where a client-facing asset is involved. This decision pass typically cuts the migration surface by 40–60%, which is 40–60% less that can go wrong.
Step 3: Build the field-mapping matrix
This is the technical heart of the audit. You're building a table that says, for every piece of data in HubSpot, exactly where it lands in GHL. A row looks like this:
| HubSpot source | Type in HubSpot | GHL destination | Type in GHL | Transform notes |
|---|---|---|---|---|
| Standard property | Standard | Primary match key; dedupe on this | ||
| First/Last Name | Standard | First/Last Name | Standard | Direct |
| Phone | Standard | Phone | Standard | Normalize to E.164 (+1…) for SMS |
| Lifecycle Stage | Enum property | Tag + pipeline stage | Tag / Opp stage | "Customer" → tag lifecycle-customer AND opportunity in Won |
| Lead Source | Dropdown property | Custom field lead_source | Single-line | Direct value copy |
| Product Interest | Multi-checkbox | Multiple tags | Tags | Each checkbox value becomes a tag interest-{value} |
hs_email_optout | System | DND (Do Not Disturb) email | DND flag | CRITICAL: must carry, or you email people who opted out |
| Static list "Do Not Contact" | List membership | Tag suppress-all + DND | Tag + DND | Safety-critical suppression |
| Marketing contact status | System | Tag marketing-ok | Tag | Drives who's allowed into bulk sends |
A few mapping principles that matter:
Email is your primary key. GHL dedupes primarily on email (and can be configured to consider phone). Your export must have clean, deduplicated emails. Decide upfront how you'll handle contacts with no email but a valid phone — many SMS-only leads.
HubSpot's data model doesn't map cleanly, so pick a convention and hold it. HubSpot uses enumerated properties, lists, and lifecycle stages where GHL leans on tags and custom fields. The general rule Cardinal followed: anything used for segmentation and automation triggers became a tag (GHL's automation reads tags fast and natively); anything used for storing a value you display or reference became a custom field. A property like "Lead Source = Facebook" is arguably both — Cardinal stored it as a custom field and applied a source-facebook tag, so it was available to both display and workflow logic. Redundant, cheap, and it prevented a whole class of "the workflow can't see this value" bugs.
Opt-out and suppression data is sacred. The single most damaging migration error is emailing people who opted out of the old system. HubSpot's hs_email_optout, marketing-contact status, and any "Do Not Contact" lists must map to GHL's DND (Do Not Disturb) settings and suppression tags — and this mapping gets verified twice. Legally and reputationally, this is the row you cannot get wrong.
Normalize phone numbers. GHL SMS needs E.164 format (+15551234567). HubSpot phone data is often a mess of formats. Plan a normalization pass in your transform step.
By the end of Step 3, Cardinal had a mapping matrix covering ~60 active fields, every suppression list, and every lifecycle stage. This document became the spec that everything downstream was built and tested against.
Part 3: Phase Two — Standing Up GHL and Loading the Data
With the map in hand, you build the destination before you drive anything into it.
Step 4: Architect the agency account and standardize with snapshots
This is where GHL's model pays off for an agency, and where Cardinal made a decision that shaped everything after: one sub-account per client, standardized from a master snapshot.
In HubSpot, every client was a segment inside one portal — messy to separate, impossible to white-label per client, painful to offboard. In GHL, each client gets their own sub-account (location). To avoid rebuilding each one by hand, you build one gold-standard sub-account — the master template — containing your standardized:
- Pipeline and opportunity stages
- Core custom fields (the ~60 from the mapping matrix)
- Standard tag taxonomy (this is your chance to finally impose a clean naming convention:
source-*,lifecycle-*,interest-*,suppress-*) - Base workflow library (the rebuilt automations — more on that next)
- Calendar templates
- Email/SMS templates
- Funnel templates
Then you capture that build as a snapshot and load it into every client sub-account. This is the single biggest efficiency in the whole migration and the thing a DIY effort almost always skips — they hand-build 22 sub-accounts and end up with 22 inconsistent messes. Standardizing via snapshot means every client sub-account is identical in structure, which makes support, training, and future onboarding dramatically cheaper. Cardinal built one master, snapshotted it, and stamped out the rest.
A note on structure: decide deliberately what lives at the agency level versus the sub-account level. Cardinal's own agency lead-gen lived in one sub-account; each client got theirs. Shared assets (brand templates, the snapshot itself) live at agency level.
Step 5: Export from HubSpot — CSV and API
Now you extract. There are two mechanisms, and a real migration uses both.
CSV export handles the bulk of contact and company data. In HubSpot you export contacts with all mapped properties selected — not the default handful. Export in slices that match your mapping (one export per object type: contacts, companies, deals). Watch for:
- Column completeness: make sure every property in your mapping matrix is included as a column.
- Enum values exported as labels vs internal values: confirm which you're getting so your transform matches.
- Multi-value properties (multi-checkbox) come out delimited (often semicolon-separated) — your transform has to split these into multiple GHL tags.
- Association data: contact-to-company and contact-to-deal relationships need their own exports so you can rebuild associations.
API export handles what CSV can't cleanly represent, and where you need reliability at scale. HubSpot's CRM API lets you pull contacts, companies, deals, engagements, and — critically — data that the CSV flattens badly. For Cardinal's 74,000 contacts with rich property sets and associations, an API pull (paginated, rate-limit-aware) produced a cleaner, more complete dataset than CSV alone, and it let the transform run programmatically rather than in a fragile spreadsheet. The API is also how you pull timeline/engagement history if you're preserving it, and how you reliably read workflow enrollment state — the thing CSVs simply don't give you (we handle enrollment in Part 4).
The practical pattern: API pull into a working dataset → run transforms (normalize phones, split multi-values into tags, map opt-out to DND, apply the field matrix) → validate → import into GHL. GHL ingests via CSV import and via its own API; for 74,000 contacts, an API-driven import with the transform baked in beats hand-mapping a giant CSV in the import wizard.
Step 6: Transform, then load — with a test batch first
Never load the full dataset first. Load a test batch of 100–500 representative contacts — deliberately chosen to include the tricky cases: opted-out contacts, SMS-only (no email) contacts, contacts with multi-value properties, contacts in multiple lifecycle stages, contacts on suppression lists.
Import the test batch into a staging sub-account. Then verify against the mapping matrix, row by row:
- Did the opted-out contacts land with DND set? (If even one didn't, stop and fix the transform — this is the sacred row.)
- Did multi-checkbox values become the right multiple tags?
- Did phone numbers normalize to E.164?
- Did lifecycle stages produce both the right tag and the right opportunity stage?
- Did custom fields land in the right GHL fields with the right values?
- Did no contact silently drop because of a missing email?
Only when the test batch passes clean do you run the full import. Cardinal caught two transform bugs in the test batch — a semicolon-split that was creating a malformed tag, and a subset of opt-outs that weren't mapping because they were stored in a list rather than the property. Both would have been catastrophic at full scale. That's the entire point of the test batch: find the catastrophe when it's 300 contacts, not 74,000.
Then, and only then, run the full load. Reconcile counts: contacts in should equal contacts loaded minus known, logged exclusions (true duplicates, invalid records). Any unexplained gap is investigated before you move on.
Part 4: Phase Three — Rebuilding Workflows and Sequences (The Hard Part)
If data mapping is the heart of the audit, workflow re-architecture is the heart of the migration. This is where a naive move breaks live campaigns, and where doing it right earns the fee.
Why HubSpot workflows don't translate one-to-one
You cannot "export" a HubSpot workflow into GHL. There is no import. Every workflow is rebuilt by hand in GHL's workflow builder — and more importantly, the two systems think differently, so a literal copy would be wrong even if it were possible.
Key differences to design around:
- Enrollment triggers: HubSpot enrolls on property changes, list membership, form submissions, page views, and more. GHL triggers on tag added/removed, form/survey submitted, appointment status, opportunity stage change, inbound message, and so on. Much maps directly; some HubSpot triggers (e.g., "property X changed to Y") become "tag added" in GHL because you've modeled that property as a tag.
- Branching logic: both support if/then branches, but GHL's condition model reads tags, custom fields, and DND. Your branch conditions get rewritten against the GHL data model you defined in the mapping matrix. This is why the matrix comes first — your workflow logic is only as good as the fields and tags it can see.
- Delays and scheduling: HubSpot's delays and GHL's "wait" steps are conceptually similar, but wait behavior, business-hours handling, and time-zone logic differ. Re-verify every delay.
- Re-enrollment: HubSpot has explicit re-enrollment settings. GHL controls this differently (workflow settings + how the trigger is configured). Getting this wrong causes either missed enrollments or infinite loops. Check every workflow.
- Internal notifications, lead routing, deal automation: all rebuildable, all different in the details.
Step 7: Categorize and rebuild in priority order
Not all 55 active workflows matter equally. Cardinal sorted them into three tiers:
Tier 1 — Live, revenue-critical, contact-facing. Onboarding sequences, active lead nurtures, appointment reminders, client-facing lifecycle emails. These cannot have a gap and cannot double-fire. These get rebuilt first, tested hardest, and are the focus of the enrollment-state problem below.
Tier 2 — Internal operations. Lead routing, internal notifications, task creation, deal-stage automation. Important, but a brief gap is an inconvenience, not a client-facing disaster. Rebuilt second.
Tier 3 — Long-tail and occasional. Re-engagement campaigns, annual renewals, edge-case flows. These can be rebuilt after cutover during the retainer/stabilization period. Trying to rebuild all 55 before cutover is how migrations stall for months. You migrate the critical mass, cut over, and finish the long tail live — which, not coincidentally, is exactly the kind of work an ongoing retainer covers.
Step 8: Solve the enrollment-state problem (the double-send trap)
Here's the scenario that terrifies experienced operators, stated plainly: A contact is on day 4 of a 9-step onboarding sequence in HubSpot. Email 5 is scheduled for Tuesday. You rebuild that sequence in GHL and go live. If you enroll that contact into the GHL sequence from the top, they receive email 1 again — a jarring, obviously-broken experience that tells your client the migration went sideways. Do that across thousands of active enrollments and you've turned a migration into a mass embarrassment.
The CSV doesn't tell you anyone is on day 4. So you handle it deliberately:
First, read the enrollment state. Via HubSpot's API and workflow reporting, extract who is currently enrolled in which workflow and at what step. For Cardinal's Tier 1 sequences, this produced a list: contact, sequence, current step, next scheduled action. This is the data that makes safe cutover possible.
Then choose a strategy per sequence. There are three, and you pick based on the sequence:
-
Let it finish in HubSpot (drain-down). For short sequences (a few days left), the cleanest option is: stop new enrollments in HubSpot at cutover, but let contacts already mid-sequence finish there. HubSpot keeps sending their remaining steps; GHL handles everyone new. The old sequence naturally drains to empty over its remaining length, then you shut it off. Zero double-sends, zero dropped contacts. This is why the parallel run exists — HubSpot is still alive to drain these.
-
Re-enroll at the correct step. For longer sequences where draining takes too long, rebuild the sequence in GHL with entry points that let you enroll a contact at a specific step. Using the enrollment-state export, you place each mid-sequence contact into the GHL version at their correct next step (day-4 contact enters at step 5). This is more work but moves everyone onto GHL cleanly. GHL workflows can be structured with tag-gated entry points to make this reliable.
-
Suppress and manually bridge. For small numbers of high-value contacts mid-critical-sequence, some agencies simply tag them, hold them out of automation, and bridge manually until they're past the sensitive step. Low-tech, but for a handful of enterprise leads it's the safest.
Cardinal used drain-down for short sequences and re-enroll-at-step for the long onboarding flows, driven off the enrollment-state export. Not a single contact received a duplicate email. That outcome is not luck — it's the direct result of reading enrollment state before cutover instead of assuming a CSV told you everything.
Step 9: Rebuild funnels and forms
The ClickFunnels funnels get rebuilt as GHL funnels. Two of Cardinal's high-converting client funnels were reconstructed page-by-page in GHL's builder, with the forms wired to the new GHL workflows and the correct tags applied on submission. Native GHL forms replaced the HubSpot forms, embedded at the same positions on client sites.
The critical detail here is URL continuity, which we handle at cutover (Part 5) — the funnel pages exist at new GHL URLs, and traffic to the old ClickFunnels URLs has to be routed to them without a broken window.
Step 10: Rebuild calendars
Calendly event types become GHL calendars, with matching availability rules, buffers, and round-robin/team assignment where used. Each calendar is wired to the appropriate reminder and confirmation workflows (which you rebuilt in Step 7). The embed points on client sites and the booking links in email signatures all need to point to the new GHL calendar URLs — again, handled as part of cutover.
Part 5: Phase Four — Deliverability and the Sending Domain Warmup
You can migrate every contact and rebuild every workflow perfectly and still torpedo the launch if you ignore email deliverability. This is the silent killer, and it needs its own runway that starts weeks before cutover.
Why you can't just start sending
HubSpot has been sending Cardinal's email from an authenticated, reputation-aged sending setup for years. Mailbox providers (Gmail, Outlook, Yahoo) trust it because it has a track record. Your new GHL sending domain has no track record. If you point 74,000 contacts at a cold domain and send a full campaign on day one, providers see a brand-new sender suddenly blasting a huge volume — the textbook signature of a spammer — and they throttle, filter, or block you. The reputation hit from one bad launch send can take weeks to recover.
Step 11: Set up authentication early
Well before cutover, configure the new sending domain's authentication:
- SPF — authorize GHL's sending infrastructure to send for your domain.
- DKIM — cryptographic signing so providers can verify the mail is really from you.
- DMARC — a policy record that tells providers what to do with mail that fails SPF/DKIM, and gives you reporting. Start with a monitoring policy (
p=none) and tighten later.
These are not optional any more. Since February 2024, Google's email sender guidelines require SPF or DKIM for every sender, and bulk senders — anyone sending roughly 5,000+ messages a day to Gmail addresses — must have SPF, DKIM and DMARC, keep spam complaints under 0.3% in Postmaster Tools, and pass domain alignment. Yahoo published matching requirements. If you are migrating a list of any size, treat that spec as the acceptance criteria for your sending setup (sender guidelines FAQ).
- A dedicated sending subdomain (e.g.,
mail.yourbrand.comor a client-specific sending domain per sub-account) so your primary domain's reputation is insulated.
These DNS records need to be in place and verified early, because DNS propagation and provider recognition take time. This is not a cutover-day task.
Step 12: Warm the domain gradually
Warming means ramping send volume slowly so providers build trust. A representative ramp:
- Week 1: send low volume to your most engaged contacts only — people who opened/clicked recently. High engagement signals to providers that people want your mail. Start in the hundreds per day, not thousands.
- Week 2: increase volume, still weighted toward engaged segments. Watch bounce rates, spam-complaint rates, and open rates like a hawk.
- Week 3–4: continue ramping toward full volume, gradually including less-engaged segments.
Throughout, monitor the signals: bounce rate should stay low, complaint rate near zero, opens healthy. If any metric spikes, you slow down. By the time you cut over full marketing sends to GHL, the domain has a reputation and your deliverability holds.
For Cardinal, warmup ran in parallel with the whole build — it started the day the mapping matrix was approved, so by cutover week the domain was warm and ready. This is another reason the parallel run matters: it gives deliverability the runway it needs while HubSpot keeps carrying the live sends.
Part 6: The Zero-Downtime Cutover Playbook
Everything so far has been preparation. This is the cutover — the sequence of moves that takes you from "GHL is built and loaded" to "GHL is live and HubSpot is off," without a single moment where a lead has nowhere to land or a live campaign goes dark.
The parallel-run window: how it actually works
For a defined window — Cardinal ran three weeks — both systems are live simultaneously, but they're doing different jobs, coordinated so nothing is caught twice and nothing is missed:
- HubSpot continues to (a) send the remaining steps of drain-down sequences, and (b) serve as the fallback/system of record until GHL is validated.
- GHL takes over new activity first — new lead capture, new enrollments, new bookings — while its data load is validated against HubSpot.
You cut over in slices, in this order, because each slice de-risks the next:
Slice 1 — New lead capture (do this first). Point new lead sources at GHL: new form embeds, new funnel traffic, new ad destinations. From this moment, everything new is born in GHL. HubSpot stops acquiring, but keeps serving existing contacts. Why first? Because the worst failure mode is a lead with nowhere to land, and getting capture onto GHL early — while HubSpot is still fully alive as a fallback — means you validate the most important path with maximum safety net.
Slice 2 — Sequences and automations. With new leads flowing into GHL and enrollment-state handled (drain-down + re-enroll-at-step from Part 4), the automation load shifts to GHL. HubSpot's active sequences drain to empty and get switched off one by one as they complete.
Slice 3 — Reporting and system of record. Once GHL has been catching everything cleanly for the validation period and counts reconcile, GHL becomes the source of truth. HubSpot moves to read-only.
Slice 4 — Decommission. Only after a clean validation window with no dropped leads and no double-sends do you export a final archive from HubSpot, ClickFunnels, and Calendly (for the record) and cancel the subscriptions. This is the moment the $3,200/month stops.
At no point is there a gap. If any slice reveals a problem, HubSpot is still there to catch the load while you fix and retry. That is zero downtime — not a heroic flawless instant, but a controlled overlap with a rollback at every step.
The DNS and URL cutover
This is the part people underestimate, and it's where live client-facing assets break if you're careless. You have three categories of load-bearing URLs:
1. Sending domain (email). Covered in Part 5 — SPF/DKIM/DMARC for the GHL sending domain are already live and warmed before cutover. Nothing dramatic happens on cutover day here; the warmup already did the work. At cutover you simply shift full send volume to the now-warm domain.
2. Funnel and landing page URLs. The old ClickFunnels funnels live at specific URLs embedded across client sites, ads, and emails. The new GHL funnels live at new URLs. You have two clean options:
- Custom domain / subdomain pointing: point the funnel's custom domain (e.g.,
offer.clientsite.com) at GHL instead of ClickFunnels by updating the domain's DNS (CNAME/A record) to GHL. If the client funnel used a custom domain, this is the cleanest path — the same URL now serves the GHL page. Traffic, ads, and existing links keep working; only the backend changed. Plan for SSL certificate issuance on the GHL side and a short propagation window. - 301 redirects: where a URL has to change, put a permanent 301 redirect from the old ClickFunnels URL to the new GHL URL so existing links, ad clicks, and bookmarks land in the right place. Keep ClickFunnels alive just long enough to serve the redirects during the transition, or handle redirects at the domain/CDN level.
The rule: no live URL is ever allowed to 404. Every old funnel URL either becomes the new page (domain re-point) or redirects to it (301). You map every URL in the audit inventory and verify each one after cutover.
3. Calendar / booking URLs. Calendly links in email signatures, on client sites, and in campaigns point to Calendly. The new GHL calendars have new URLs. Same treatment: update embeds to the new GHL calendar, and where a raw Calendly link is circulating that you can't update everywhere, keep the Calendly event alive briefly or redirect. Booking is revenue-adjacent — a broken booking link means a lost appointment — so these get verified with special care.
The pre-cutover QA checklist
Before you flip Slice 1, you run a full QA pass. Cardinal's checklist, which is a reasonable template for yours:
Data integrity
- Contact count in GHL reconciles with HubSpot minus logged exclusions
- Opt-out / DND contacts verified: a random sample of known opt-outs are all DND in GHL
- Suppression lists mapped and verified — no suppressed contact is in an active send segment
- Custom fields spot-checked against source for a sample across segments
- Tags applied correctly, including multi-value splits
- Phone numbers in E.164; SMS-capable contacts verified
- Contact-company-deal associations rebuilt where migrated
Automation
- Every Tier 1 workflow tested end-to-end with a test contact (trigger → each step → branch → exit)
- Enrollment-state plan executed: drain-down sequences flagged, re-enroll-at-step contacts placed correctly
- No workflow re-enrolls a contact into a sequence they already completed (double-send check)
- Internal notifications and lead routing fire to the right people
- Appointment reminders/confirmations fire correctly
Funnels, forms, calendars
- Every migrated funnel loads correctly on desktop and mobile
- Every form submits and applies the correct tags and triggers the correct workflow
- Every calendar shows correct availability, buffers, and assignments; a test booking flows end-to-end
- SSL valid on all custom domains pointed to GHL
URLs and DNS
- Every old funnel URL either re-points or 301-redirects — none 404s
- Every calendar/booking link resolves to the new GHL calendar
- Sending domain SPF/DKIM/DMARC verified and passing; domain warmed
Deliverability
- Test sends to seed inboxes (Gmail, Outlook, Yahoo) land in the inbox, not spam
- Warmup ramp complete; reputation metrics healthy
Rollback
- HubSpot still live and able to catch load if a slice is reverted
- Documented rollback steps for each slice
You do not flip Slice 1 until this checklist is green. And because HubSpot stays live through the whole parallel run, even a checklist item you missed has a safety net.
The post-cutover stabilization period
The migration doesn't end at cutover — it ends when the new system has run clean for long enough to trust. For a week or two after Slice 1, you watch closely: monitor deliverability daily, confirm no leads are falling through, confirm no sequences are misbehaving, and keep HubSpot alive as the fallback. Cardinal held their parallel run for three weeks total, decommissioned HubSpot/ClickFunnels/Calendly only after two clean weeks post-cutover, and then cancelled — capturing the full $2,700/month savings only once it was truly safe to do so.
Part 7: The People Problem — Training 22 Humans to Actually Use GHL
A migration that's technically flawless and operationally abandoned still fails. On the Monday after cutover, 22 people who fluently used HubSpot now stare at an unfamiliar interface. If they can't work in it, they'll route around it — updating spreadsheets, forgetting to tag, breaking the clean data model you just built.
So training is part of the migration, not an afterthought:
- Role-based sessions. Salespeople need to know pipelines, opportunities, and the contact timeline. Marketers need workflows, funnels, and email/SMS. Admins need sub-account management and snapshots. One generic training bores everyone; role-based training sticks.
- Documentation built on YOUR build. Generic GHL tutorials don't reflect the tag taxonomy and workflow structure you just created. Short, specific docs — "here's how we tag a new lead," "here's our onboarding workflow and when it fires" — are what people actually use. This documentation is also what makes the standardized snapshot pay off: every sub-account works the same way, so the docs apply everywhere.
- A stabilization support window. For the first few weeks, someone needs to be reachable for "how do I do X in GHL" questions before they become "I gave up and did X in a spreadsheet" problems.
This is precisely the work that flows naturally into an ongoing retainer — finishing the Tier 3 long-tail workflows, running training sessions, writing documentation, and being on priority support during stabilization. The one-time migration gets you live; the retainer keeps you optimized and keeps the team fluent.
Part 8: What This Actually Costs vs. What It Saves
Let's put the economics next to the risk, because for you this is a business decision, not a technical one.
What Cardinal was spending: $3,200/month = $38,400/year across HubSpot, ClickFunnels, Calendly, and bolt-ons.
What Cardinal spends on GHL: roughly $500/month on the Agency plan = $6,000/year — with unlimited client sub-accounts they can now resell at margin.
Gross software savings: ~$2,700/month, ~$32,400/year. Plus the reselling upside, plus dozens of hours a month previously lost to stitching systems together manually.
What a done-for-you migration costs: a one-time project fee starting around $1,000+ (scaling with contact volume, workflow count, and number of client sub-accounts — a 74,000-contact, 55-workflow, multi-sub-account job like Cardinal's sits above the floor), optionally plus a retainer of $500–$2,000/month for the stabilization period and ongoing optimization.
Do the math the way you'd do it for a client. The migration fee is recovered in well under a month of software savings. And that comparison assumes the migration goes fine either way. It doesn't account for the real risk: a botched DIY switch that double-sends thousands of contacts, drops leads during a gap, torches your sending reputation, or breaks a client's live funnel mid-campaign. That cost — in refunds, in churned clients, in reputation, in the frantic weekend spent firefighting — dwarfs the migration fee. You are not paying $1,000+ to move data. You're paying to make the scenarios in Part 1 not happen.
Part 9: Your Move — Migrate to GoHighLevel With Zero Downtime
Here's the promise, stated as plainly as it deserves to be: We move the data, rebuild the funnels, and keep your clients live. No lost tags. No lost history. No double-sends. No dark campaigns. No client finding out before you do.
That's not a slogan — it's the direct output of the process you just read. The audit and mapping matrix protect your data. The parallel run protects your uptime. The enrollment-state handling protects against double-sends. The domain warmup protects your deliverability. The URL/DNS cutover protects your live client assets. The QA checklist and rollback protect against surprises. And the training and retainer protect the investment after go-live.
If you're an established agency staring at a HubSpot invoice that keeps climbing, cancelling or downgrading old tools, consolidating your clients' stacks, or posting migration questions in every GHL group you're in — you're exactly who this process is built for. You don't need to become a migration expert or gamble a weekend on a big-bang switch and hope. You need someone who's moved five years of accumulated data before and knows where the bodies are buried.
Here's how to start. Book a migration audit call. We'll inventory your current stack — HubSpot, ClickFunnels, Calendly, whatever you're running — map the real scope (contacts, custom fields, workflows, client sub-accounts), flag the risky spots specific to your setup, and give you a fixed migration plan with a timeline and a cutover date. No obligation, and you'll walk away with a clearer picture of your own stack than you have right now.
Then, if it's a fit, we execute the playbook above: audit, map, build, load, rebuild, warm, parallel-run, cut over, stabilize, train. You keep serving clients the entire time. One day the old tools just… turn off, and the $3,200/month goes with them.
Migrate to GoHighLevel with zero downtime. Book your migration audit call →
Sources and further reading
- HubSpot: export your records — pulling contacts, companies and deals out with their property values and associations.
- HubSpot: CRM exports API — scripting the extraction for larger portals.
- Google: email sender guidelines — the SPF/DKIM/DMARC, spam-rate and alignment requirements your new sending domain must satisfy.
- Google: email sender guidelines FAQ — clarifications on the bulk-sender threshold and enforcement.
Frequently asked questions
Will I lose my tags, custom fields, or contact history when moving from HubSpot to GHL?
How do you migrate without double-sending emails to contacts who are mid-sequence?
How long does a HubSpot-to-GHL migration actually take?
Can my agency keep running campaigns during the migration, or does everything go dark?
What happens to my ClickFunnels funnels and Calendly links that are embedded on client sites and in ads?
Will my email deliverability drop when I switch sending platforms?
Why not just do the migration myself to save the fee?
What's included in the ongoing retainer after the migration, and do I need it?
About the author

Farhad
Founder, GHL Spark
Farhad is the founder of GHL Spark, where he builds and white-labels GoHighLevel SaaS platforms for agencies and SaaS operators. He writes about the parts of GoHighLevel that actually break in production — A2P registration, onboarding, support load and automation.