Google Sheets to SeaTable, clean contacts no duplicates
Your contact list shouldn’t feel like a crime scene. But once leads start coming in from forms, partners, events, and list uploads, duplicates sneak in, names drift, and suddenly nobody trusts the “final” spreadsheet anymore.
This Sheets SeaTable dedupe automation hits marketing ops first. Then sales teams, founders, and client-facing agencies get pulled in to “quickly clean things up.” It keeps one clean SeaTable database synced from a Google Sheet, with email-based deduplication.
Below, you’ll see exactly what the workflow does, what outcomes to expect, and the practical gotchas that usually trip people up the first time.
How This Automation Works
The full n8n workflow, from trigger to final output:
n8n Workflow Template: Google Sheets to SeaTable, clean contacts no duplicates
flowchart LR
subgraph sg0["When clicking ‘Execute workflow’ Flow"]
direction LR
n0@{ icon: "mdi:play-circle", form: "rounded", label: "When clicking ‘Execute workf..", pos: "b", h: 48 }
n1@{ icon: "mdi:swap-vertical", form: "rounded", label: "Loop Over Items", pos: "b", h: 48 }
n2@{ icon: "mdi:cog", form: "rounded", label: "Replace Me", pos: "b", h: 48 }
n3@{ icon: "mdi:swap-horizontal", form: "rounded", label: "If", pos: "b", h: 48 }
n4["<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/seaTable.svg' width='40' height='40' /></div><br/>seatablelookup"]
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/seaTable.svg' width='40' height='40' /></div><br/>Create a row"]
n6["<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/seaTable.svg' width='40' height='40' /></div><br/>Update a row"]
n8@{ icon: "mdi:database", form: "rounded", label: "contacts", pos: "b", h: 48 }
n9@{ icon: "mdi:swap-vertical", form: "rounded", label: "settings", pos: "b", h: 48 }
n3 --> n5
n3 --> n6
n8 --> n1
n9 --> n8
n5 --> n1
n6 --> n1
n4 --> n3
n1 --> n2
n1 --> n4
n0 --> n9
end
subgraph sg1["Flow 2"]
direction LR
n11["<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/>HTTP Request1"]
n12@{ icon: "mdi:swap-vertical", form: "rounded", label: "Edit Fields2", pos: "b", h: 48 }
n11 --> n12
end
subgraph sg2["Flow 3"]
direction LR
n7["<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/>HTTP Request"]
n10@{ icon: "mdi:swap-vertical", form: "rounded", label: "Edit Fields", pos: "b", h: 48 }
n10 --> n7
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 n3 decision
class n8 database
class n11,n7 api
classDef customIcon fill:none,stroke:none
class n4,n5,n6,n11,n7 customIcon
The Problem: Contact Lists Drift (and Duplicates Multiply)
A Google Sheet is a great intake form for contacts… until it becomes the system of record by accident. Someone sorts a column and forgets to include the header. Another person pastes in a list with “Email ” (with a trailing space) as the column name. Then you export it to a CRM-like tool, import it again later, and now you’ve got “[email protected]” in three places with three slightly different names. Multiply that by a few months of outreach and you get the real cost: wasted time, awkward double-emails, and reporting nobody believes.
It adds up fast. Here’s where it breaks down in the real world.
- People update the Sheet, but the “database” in SeaTable stays stale, so you end up exporting and re-importing just to catch up.
- Duplicates sneak in because manual dedupe relies on someone noticing the same email twice.
- A single wrong import can overwrite good data, which means you spend an afternoon reconstructing what changed.
- Outreach tools pull from inconsistent lists, so segmentation becomes guesswork instead of a reliable process.
The Solution: Sync Google Sheets Contacts into SeaTable (With Email Dedupe)
This n8n workflow turns your Google Sheet into a clean, controlled pipeline into SeaTable. You start with a single spreadsheet that contains your contacts (email, first name, last name, company). When you run the automation, n8n retrieves your Sheet rows, processes them in batches, and checks SeaTable for an existing record using the email address as the unique key. If the email already exists, the workflow updates the record so your SeaTable base stays current. If it doesn’t exist, it inserts a new row, so new leads get added automatically without anyone doing a manual import.
The workflow begins with a manual run in n8n (great for controlled syncs). Then it pulls contacts from Google Sheets, looks up each email in SeaTable, and routes the contact down an “update” or “insert” path. Finally, it loops until every contact is handled, keeping SeaTable aligned with what’s in your Sheet.
What You Get: Automation vs. Results
| What This Workflow Automates | Results You’ll Get |
|---|---|
|
|
Example: What This Looks Like
Say you add and update 100 contacts a week in a shared Google Sheet. Manually, a cautious “export CSV → import to SeaTable → dedupe → spot-check” process is easily 10 minutes per batch, and teams often do it 5 times a week, so you lose about an hour (and attention) just to keep tools aligned. With this workflow, you paste contacts into the Sheet as usual, then run the sync from n8n and let it update or insert by email automatically. The hands-on time is closer to a few minutes, mostly just verifying the first run.
What You’ll Need
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- Google Sheets as the editable contact intake list.
- SeaTable to store the deduped CRM-like database.
- SeaTable API token (get it from SeaTable account settings).
Skill level: Beginner. You’ll connect accounts, paste in the Sheet ID, and confirm your SeaTable table fields match.
Don’t want to set this up yourself? Talk to an automation expert (free 15-minute consultation).
How It Works
You trigger the sync manually. In n8n, you click run when you’re ready to sync (useful if multiple people edit the Sheet and you want control).
The workflow pulls contacts from Google Sheets. It uses the Sheet ID you set in the workflow settings, then retrieves the rows that represent contacts you want in SeaTable.
Each contact gets checked by email. n8n processes rows in batches and does a SeaTable lookup. The “If” condition routes the contact to “update” if it exists, or to “insert” if it’s new.
SeaTable gets updated cleanly. Existing records are modified, new ones are added, and the workflow continues until all contacts are handled without creating duplicates.
You can easily modify the fields (like adding phone, tags, or lead source) to match how you actually use your database. See the full implementation guide below for customization options.
Step-by-Step Implementation Guide
Step 1: Configure the Manual Trigger
Start the workflow with a manual trigger so you can run and verify the sync logic during setup.
- Add the Manual Execution Start node as the trigger.
- Connect Manual Execution Start to Assign Sheet Settings.
- Leave Flowpast Branding as an informational sticky note (no configuration required).
Step 2: Connect Google Sheets
Define which spreadsheet to read and pull the contacts into the workflow.
- Open Assign Sheet Settings and set googlesheetid to
[YOUR_ID]. - Configure Retrieve Sheet Contacts with sheetName set to
contacts. - Set documentId to
={{ $json.googlesheetid }}in Retrieve Sheet Contacts. - Credential Required: Connect your googleSheetsOAuth2Api credentials in Retrieve Sheet Contacts.
Tip: Ensure the Google Sheet has columns named email, firstname, lastname, and company to match the downstream mappings.
Step 3: Set Up Batch Processing and Lookup
Iterate through each contact and search for an existing record in SeaTable.
- Connect Retrieve Sheet Contacts to Iterate Records to process items in batches.
- Connect Iterate Records to SeaTable Lookup for per-record lookup.
- In SeaTable Lookup, set operation to
search, tableName toTable1, searchColumn toemail, and searchTerm to={{ $json.email }}. - Credential Required: Connect your seaTableApi credentials in SeaTable Lookup.
- Open Conditional Check and confirm the condition uses leftValue
={{ $json.isEmpty() }}with the boolean operator set totrue.
Step 4: Configure SeaTable Output Actions
Create new records when none exist and update existing records when a match is found.
- From Conditional Check, connect the true output to Add SeaTable Row and the false output to Modify SeaTable Row.
- In Add SeaTable Row, set tableName to
Table1and map columns:email→={{ $('Retrieve Sheet Contacts').item.json.email }},firstname→={{ $('Retrieve Sheet Contacts').item.json.firstname }},lastname→={{ $('Retrieve Sheet Contacts').item.json.lastname }},company→={{ $('Retrieve Sheet Contacts').item.json.company }}. - In Modify SeaTable Row, set operation to
update, tableName toTable1, rowId to={{ $json._id }}, and use the same column mappings as above. - Credential Required: Connect your seaTableApi credentials in both Add SeaTable Row and Modify SeaTable Row.
- Connect both Add SeaTable Row and Modify SeaTable Row back to Iterate Records to continue the batch loop.
⚠️ Common Pitfall: If the SeaTable table columns do not match email, firstname, lastname, and company, the update/create operations will fail.
Step 5: Configure Utility API Helpers (Optional)
These nodes help provision tables and fetch server details but do not run in the main sync path.
- In Utility: Configure API Fields, set server to
cloud.seatable.io, base_uuid to[YOUR_ID], and access_token to[CONFIGURE_YOUR_TOKEN]. - In Utility: Create Table Request, set method to
POSTand url to=https://{{ $json.server }}/api-gateway/api/v2/dtables/{{ $json.base_uuid }}/tables. - In Utility: Create Table Request, keep jsonBody as provided for the
contacttable definition and ensure headers includeaccept: application/jsonandauthorization: =Bearer {{ $json.access_token }}. - In Utility: Fetch Server Info, set url to
https://cloud.seatable.io/server-infoand keep the accept header asapplication/json. - Connect Utility: Fetch Server Info to Utility: Map Server Response if you want to parse the server info payload.
Tip: Run the utility nodes manually during initial setup to validate your API access token and base UUID before running the main sync.
Step 6: Review the Placeholder Node
The workflow includes a no-op node for future expansion or debugging.
- Confirm Iterate Records routes to Placeholder Step as the batch loop completion path.
- Leave Placeholder Step unconfigured; it simply marks the end of processing.
⚠️ Common Pitfall: Removing Placeholder Step without reconnecting the loop may break the batch iteration flow.
Step 7: Test and Activate Your Workflow
Run a manual test to confirm contacts sync correctly, then activate for production use.
- Click Execute Workflow on Manual Execution Start to run a test.
- Verify that Retrieve Sheet Contacts outputs rows and that SeaTable Lookup returns matches or empty results.
- Confirm that new contacts are created via Add SeaTable Row and existing ones are updated via Modify SeaTable Row in
Table1. - Once confirmed, toggle the workflow to Active for production use.
Common Gotchas
- Google Sheets credentials can expire or lose access to the file. If the sync suddenly returns no rows, check the Google connection in n8n and confirm the account can open the Sheet.
- If you’re using Wait nodes or external rendering, processing times vary. Bump up the wait duration if downstream nodes fail on empty responses.
- SeaTable often fails “silently” when field names don’t match. Double-check the base has a Table1 with fields named exactly email, firstname, lastname, and company, and verify your API token still has permission.
Frequently Asked Questions
About 30 minutes if you already have the Sheet and SeaTable base ready.
No. You’ll connect credentials and paste in your Google Sheet ID. The rest is configuration and field matching.
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 SeaTable subscription costs (API access depends on your plan).
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 it’s a common upgrade. Add the new columns to your Google Sheet, then map them in the “Edit Fields (Set)” nodes that prepare the SeaTable payload. You may also need to update your SeaTable table schema so the field names exist and match what you’re sending. If you want the workflow to treat another field as the unique key (like a CRM ID instead of email), swap the lookup logic in the SeaTable Lookup step.
Usually it’s an expired API token or the token doesn’t have access to the base. Also check your table and field names in SeaTable; if “email” is named “Email” (capitalized) the lookup can fail and you’ll create duplicates. Finally, confirm you’re pointing at the right SeaTable server URL if you use a non-default region.
Thousands, as long as your n8n plan and SeaTable API rate limits can keep up.
Often, yes, if you care about data quality and control. This workflow needs looping, conditional paths, and a “lookup then update/insert” pattern; n8n handles that cleanly without turning it into a fragile chain of mini-zaps. Self-hosting is also a big deal if you run frequent syncs, because you’re not paying per tiny step. Zapier or Make can still be fine for small lists, especially if you want a polished UI and you’ll never touch the logic. If you’re unsure, Talk to an automation expert and you’ll get a straight recommendation.
Once your contacts stop duplicating and drifting, everything downstream gets easier. The workflow keeps the list tidy so you can focus on using it.
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.