If you've ever collected customer data through a Google Form, you already know what happens next: the spreadsheet turns into chaos. Names in different cases, duplicate emails, phone numbers missing digits, dates written five different ways in the same column.
I got tired of manually cleaning this every week, so I built an automated pipeline in n8n that fixes all of it the moment a form is submitted.
The Problem
A raw form submissions sheet usually looks something like this:
| Full Name | Phone | City | Registration Date | |
|---|---|---|---|---|
| John doe | j0hn@gmail.com | 1712345678 | Dhaka | 01/05/2025 |
| JOHN DOE | j0hn@gmail.com | 1712345678 | Chittagong | May 1, 2025 |
| tanvir ahmed | tanvir@yahoo.com | 171122 | Dhaka | 2025/06/12 |
Same person entered twice with different casing. A copy-pasted email attached to the wrong name. A phone number missing half its digits. Three different date formats in three rows. Multiply this by a few hundred submissions and manual cleanup becomes a part-time job.
The Solution: An n8n Pipeline
Here's the flow:
flowchart LR
A[Form Submission Trigger] --> B[Messy Data]
B --> C[Filter Code]
C --> D[Clean File]
C --> E[Summary Report Filter Code]
E --> F[Summary Report]
A form submission triggers the workflow, pulls in the raw row, runs it through a Code node that handles all the normalization logic, then splits into two outputs — a clean master file and an auto-generated summary report.
What the Cleaning Logic Actually Handles
- Names → consistent title-case formatting
- Emails → deduplicated and validated against the right record
-
Phone numbers → normalized into one consistent format (in my case, Bangladeshi numbers into
01XXXXXXXXX) -
Dates → every format converted into
YYYY-MM-DD - Customer ID → auto-generated and assigned per record
The trickiest part was date and phone parsing — real-world form input is way messier than test data. A rough example of the kind of normalization logic involved:
function normalizePhone(raw) {
const digits = raw.replace(/\D/g, '');
if (digits.length === 10 && !digits.startsWith('0')) {
return '0' + digits;
}
return digits;
}
(Simplified for illustration — the actual node handles a few more edge cases, like missing digits and stray country-code prefixes.)
The Result
| Customer ID | Full Name | Phone | City | Registration Date | |
|---|---|---|---|---|---|
| CUST-0001 | John Doe | john@gmail.com | 01712345678 | Dhaka | 2025-01-05 |
| CUST-0002 | Sarah Khan | sarah@gmail.com | 01987654321 | Chittagong | 2025-05-03 |
| CUST-0004 | Tanvir Ahmed | tanvir@yahoo.com | 01711223344 | Dhaka | 2025-06-12 |
Same data, but now every row is consistent, deduplicated, and immediately usable — in a CRM, a mailing list, or anywhere else it needs to go.
Stack
- n8n for orchestration
- Google Forms & Sheets for data collection
- Excel as the cleaned output
Why This Is Worth Automating
- No more weekly manual cleanup
- Fewer errors from duplicate or mistyped records
- A summary report that's ready the second new data comes in
- Scales identically whether you get 10 submissions a week or 10,000
If you're building something similar, or you've handled messier real-world data than this, I'd genuinely like to hear how you approached deduplication or date parsing — always looking to improve the edge-case handling.
Contact With me Linkedin : www.linkedin.com/in/bastullah-batas-54ab23335


Top comments (0)