Google Sheets to GoHighLevel, no more duplicate leads
Your leads are coming in. Great. Then someone submits the form twice, your CRM creates two contacts, and now you’ve got two follow-ups going out like you don’t talk to each other.
This is the stuff that quietly wrecks your pipeline. Marketing managers see it in messy attribution. Sales reps feel it when they call the same person again. And ops ends up playing spreadsheet detective. This Sheets GHL dedupe automation keeps one clean contact record, automatically.
You’ll see exactly how the workflow catches duplicates, updates the right record in GoHighLevel, and logs everything back to Google Sheets so your reporting stays believable.
How This Automation Works
The full n8n workflow, from trigger to final output:
n8n Workflow Template: Google Sheets to GoHighLevel, no more duplicate leads
flowchart LR
subgraph sg0["Google Sheets Flow"]
direction LR
n0@{ icon: "mdi:play-circle", form: "rounded", label: "Google Sheets Trigger1", pos: "b", h: 48 }
n1@{ icon: "mdi:database", form: "rounded", label: "Lookup Lead1", pos: "b", h: 48 }
n2["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>Update Contact (GHL)1"]
n3@{ icon: "mdi:database", form: "rounded", label: "Log New Lead1", pos: "b", h: 48 }
n4@{ icon: "mdi:database", form: "rounded", label: "Log Duplicate Lead1", pos: "b", h: 48 }
n5["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>Create Contact (GHL)1"]
n6@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Check the duplication", pos: "b", h: 48 }
n7@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Check from the data base?1", pos: "b", h: 48 }
n8["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/code.svg' width='40' height='40' /></div><br/>Field extraction"]
n1 --> n7
n8 --> n4
n6 --> n5
n5 --> n3
n2 --> n8
n0 --> n1
n7 --> n6
n7 --> n2
end
%% Styling
classDef trigger fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
classDef ai fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
classDef aiModel fill:#e8eaf6,stroke:#3f51b5,stroke-width:2px
classDef decision fill:#fff8e1,stroke:#f9a825,stroke-width:2px
classDef database fill:#fce4ec,stroke:#c2185b,stroke-width:2px
classDef api fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef code fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef disabled stroke-dasharray: 5 5,opacity: 0.5
class n0 trigger
class n6,n7 decision
class n1,n3,n4 database
class n2,n5 api
class n8 code
classDef customIcon fill:none,stroke:none
class n2,n5,n8 customIcon
The Problem: Duplicate leads ruin follow-up and reporting
Duplicates don’t just create clutter. They create confusion at the exact moment you’re trying to move fast. A lead submits a Google Form, then submits again because they didn’t get an instant reply. Or a teammate imports a list while your form is still running. Now GoHighLevel has two contacts with the same email, different phone numbers, and two conversations tied to the wrong one. You lose time merging. You lose trust in your pipeline. And honestly, you start second-guessing every “new lead” number you show in a meeting.
The friction compounds, because the mess repeats every day.
- Sales follow-ups get duplicated, which can make your business look sloppy or spammy.
- Reporting becomes unreliable because one person is counted multiple times in GoHighLevel and in your Sheets exports.
- Manual cleanup eats real time, usually 30 minutes here and there, and it always lands on the busiest person.
- Updates get lost because the “newer” submission lives on a different contact record than the one your team is actively using.
The Solution: Sync Google Sheets leads into GoHighLevel without duplicates
This n8n workflow watches your Google Sheets form response sheet for new submissions as they come in. Each time a row is added, it pulls key contact fields (typically name, email, phone, and timestamp), then checks your master lead database sheet to see if the person already exists. If it’s a brand-new lead, the workflow creates a contact in GoHighLevel via an HTTP request and appends the lead to your master database sheet. If it’s a duplicate, it updates the existing GoHighLevel contact with the latest details and logs that duplicate event in a separate “duplicate log” sheet for tracking and analysis. No manual sorting. No guessing which record is the real one.
The workflow starts with a real-time Google Sheets trigger. Then it compares the incoming submission against your existing records, routes the lead down a “new” or “duplicate” path, and syncs the outcome back into Sheets and GoHighLevel. Your CRM stays clean, so your team can move faster without stepping on each other.
What You Get: Automation vs. Results
| What This Workflow Automates | Results You’ll Get |
|---|---|
|
|
Example: What This Looks Like
Say your form brings in 30 leads a day, and around 5 are duplicates (repeat submitters, typos, or someone using a work email later). Manually, it’s maybe 6 minutes to search GoHighLevel, compare details, update the right record, then log what happened. That’s about 30 minutes daily, and it never feels like “real work.” With this workflow, the trigger happens instantly when the row lands in Sheets, and the check + sync runs in the background. Your time spent becomes close to zero unless you want to review the duplicate log.
What You’ll Need
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- Google Sheets for form responses, master DB, duplicate log.
- GoHighLevel to create and update CRM contacts.
- GoHighLevel API key (get it from GoHighLevel Settings).
Skill level: Intermediate. You’ll map a few fields, set up credentials, and confirm your duplicate rules (email only, or email plus phone).
Don’t want to set this up yourself? Talk to an automation expert (free 15-minute consultation).
How It Works
A new row hits your form response sheet. The Google Sheets trigger (“Sheets Intake Trigger”) watches for new submissions so you don’t have to export anything or run a daily import.
Your workflow pulls existing leads for comparison. It retrieves records from your master lead database sheet (“Retrieve Lead Lookup”), then prepares the incoming submission fields so comparisons are consistent (email formatting, phone normalization, and so on).
Duplicates get routed, not ignored. An If check (“Database Duplicate Check”) decides if this submission matches someone you already have. If it’s new, it passes a validation filter and creates the contact in GoHighLevel (“Generate CRM Contact”), then appends the row to the master sheet (“Append Lead Record”). If it’s a duplicate, it updates the existing GoHighLevel contact (“Refresh CRM Contact”) and then writes a clean, structured entry into your duplicate log sheet (“Update Duplicate Log”).
Your team gets cleaner data everywhere. GoHighLevel stays deduped, your master sheet stays the source of truth, and the duplicate log becomes a simple way to spot trends like repeat submissions, ad fraud, or forms that confuse people.
You can easily modify the duplicate matching rules to fit your reality. Some teams match on email only, others require email plus phone, and some prefer “company + phone” for B2B. See the full implementation guide below for customization options.
Step-by-Step Implementation Guide
Step 1: Configure the Google Sheets Trigger
Set up the trigger that watches for new lead submissions in Google Sheets.
- Add the Sheets Intake Trigger node to your workflow.
- Set Document to
https://docs.google.com/spreadsheets/d/[YOUR_ID]/edit. - Set Sheet to
https://docs.google.com/spreadsheets/d/[YOUR_ID]/edit. - Confirm the polling schedule is set to Every Minute under Poll Times.
- Credential Required: Connect your
googleSheetsTriggerOAuth2Apicredentials.
Step 2: Connect Google Sheets
Configure the Sheets nodes that read existing leads and update your database.
- Open Retrieve Lead Lookup and set Document to
[YOUR_ID]and Sheet togid=0. - Credential Required: Connect your
googleSheetsOAuth2Apicredentials in Retrieve Lead Lookup. - Open Append Lead Record, set Operation to
append, and select the same Document[YOUR_ID]and Sheetgid=0. - Credential Required: Connect your
googleSheetsOAuth2Apicredentials in Append Lead Record. - Open Update Duplicate Log, set Operation to
updateand Data Mode toautoMapInputData, then select Document[YOUR_ID]and Sheetgid=0. - Credential Required: Connect your
googleSheetsOAuth2Apicredentials in Update Duplicate Log.
Step 3: Configure Duplicate Detection and Branching
Route leads based on whether they already exist in your database.
- In Database Duplicate Check, set the condition to compare the lead email: Left Value
={{ $('Sheets Intake Trigger').item.json['Email address'] }}and Right Value={{ $json["Email address"] }}with Operation set tonotEquals. - In Validate New Lead, set the condition to Left Value
={{ $json["Email address"] }}and Right Value={{ $('Retrieve Lead Lookup').item.json["Email address"] }}with Operation set tonotEquals. - Confirm execution flow: Database Duplicate Check outputs to both Validate New Lead and Refresh CRM Contact in parallel.
Step 4: Configure CRM Requests and Data Processing
Create new CRM contacts for valid leads and refresh existing contacts for duplicates.
- In Generate CRM Contact, set URL to
https://rest.gohighlevel.com/v1/contacts/and Send Body to On with Body Type set tojson. - Paste the JSON body exactly as configured, including expressions like
{{ $('Sheets Intake Trigger').item.json['First Name'] }}and{{ $now }}in the JSON Body field. - Credential Required: Connect your
goHighLevelApicredentials in Generate CRM Contact. - In Refresh CRM Contact, set URL to
=https://rest.gohighlevel.com/v1/contacts?email={{ $json["Email address"] }}and set Authentication togenericCredentialTypewith Generic Auth TypehttpHeaderAuth. - Credential Required: Connect your
goHighLevelApiandhttpHeaderAuthcredentials in Refresh CRM Contact. - In Extract Contact Fields, review the code and replace
"[YOUR_EMAIL]"if you want a fixed email for testing, or ensure$json.emailis passed from the previous node.
"[YOUR_EMAIL]" as a fallback. If you don’t replace it or pass $json.email, the duplicate lookup may always fail.Step 5: Test and Activate Your Workflow
Validate that new leads create CRM contacts and duplicates update the log correctly.
- Click Execute Workflow and submit a test lead in your Google Form or add a row in the sheet.
- Confirm that Generate CRM Contact runs for new leads and that Append Lead Record appends a row in the database sheet.
- Submit a duplicate lead and verify Refresh CRM Contact → Extract Contact Fields → Update Duplicate Log updates the duplicate sheet.
- When the test completes successfully, switch the workflow to Active to enable live processing.
Common Gotchas
- Google Sheets access can fail if the connected Google account loses permission to the spreadsheet. If it suddenly stops, check the n8n credential status and confirm the file is still shared with that account.
- If you’re using Wait nodes or external rendering, processing times vary. Bump up the wait duration if downstream nodes fail on empty responses.
- GoHighLevel API keys get rotated, and the HTTP Request nodes will start failing fast when that happens. Regenerate the key in GoHighLevel Settings and update the credential used by “Generate CRM Contact” and “Refresh CRM Contact.”
Frequently Asked Questions
About an hour if your sheets and GoHighLevel API key are ready.
No. You’ll mostly connect accounts and map fields between Google Sheets and GoHighLevel.
Yes. n8n has a free self-hosted option and a free trial on n8n Cloud. Cloud plans start at $20/month for higher volume. You’ll also need to factor in GoHighLevel costs (your existing plan) since this workflow uses their API.
Two options: n8n Cloud (managed, easiest setup) or self-hosting on a VPS. For self-hosting, Hostinger VPS is affordable and handles n8n well. Self-hosting gives you unlimited executions but requires basic server management.
Yes, and you should if email quality is inconsistent. Update the logic in the duplicate check (the “Database Duplicate Check” If node) so it checks both fields, and make sure “Retrieve Lead Lookup” is returning the columns you want to compare. Common customizations include normalizing phone formats, ignoring empty phone numbers, and treating “+1” variants as the same number.
Usually it’s an expired or replaced API key used by the HTTP Request nodes. Update the credential in n8n, then re-run a single test submission from your form response sheet. If it still fails, check that the API key has access to contacts and that you’re sending the required fields (email or phone) in the request body.
Plenty for most small teams. On n8n Cloud, the limit is mainly your plan’s monthly executions; self-hosting removes that cap and shifts the limit to your server and Google Sheets API quotas. Practically, if you’re adding hundreds of rows a day, consider batching lookups and keeping your master sheet tidy so searches stay fast.
Often, yes. This workflow needs branching logic (new lead vs duplicate), a reliable lookup against an existing database, and two different API actions in GoHighLevel (create vs update), plus logging. n8n handles that kind of “real workflow” without turning every path into another paid step. Zapier or Make can still work if your volume is low and your logic is simple, but costs climb quickly once you add lookups, filters, and extra actions. If you want a second opinion on what’s best for your setup, Talk to an automation expert.
Clean data sounds boring until you see how much it changes daily execution. Set this up once, keep your GoHighLevel contacts sane, and move on to work that actually grows the pipeline.
Need Help Setting This Up?
Our automation experts can build and customize this workflow for your specific needs. Free 15-minute consultation—no commitment required.