Every automation that writes to a CRM eventually faces the same question: is this person already in here? Get the answer wrong and you do not get an error. You get a second record for someone who was already a customer, and a sales team that does not know which one to trust.
This is the quiet failure mode of lead automation. Nothing breaks. The data just slowly stops being reliable.
Email alone is not enough
Most builds match on email, because email feels like an identifier. It is not, quite. People fill in a form from a work address one month and a personal one the next. They mistype. They use a partner's address. A booking form autofills something different from what they typed last time.
Every one of those creates a new record for someone you already know. On a patient intake sync I worked on, the fix was matching on two keys: check email first, then fall back to phone, and update the existing record when either matches. Two keys catch the overwhelming majority of returning people that one key misses.
Check before you write, not after
The other half of the problem is where the check sits. It is tempting to write the record and clean up later — a scheduled dedupe job, a manual merge, a view that flags suspicious pairs. That never really works, because by the time the duplicate exists, other things have attached to it: a deal, a task, a conversation history. Merging becomes a decision rather than a cleanup.
Better to make the lookup an explicit step before anything gets written. Search for the contact, branch on whether one came back, then either update the existing record or create a new one, and bring both paths back together before the rest of the workflow continues. It is two extra nodes and it removes the entire category of problem.
Watch out for the empty-result branch
One practical trap: in most automation platforms, a search step that finds nothing may produce no output at all, which means the branch handling new contacts never runs. The workflow finishes green having done nothing.
n8n has a setting for this on each node — always output data — and Make has its own equivalent. Whatever the platform, if you have a branch that is supposed to handle the not-found case, confirm it actually executes when nothing is found. Test it with a genuinely new contact, not a familiar one.
A checklist
- Match on at least two keys. Email first, phone as fallback, is a sensible default for most businesses.
- Normalise before comparing. Trim whitespace, lowercase the email, and strip formatting from phone numbers, or your match will miss for cosmetic reasons.
- Put the lookup before the write, and branch on the result rather than cleaning up afterwards.
- Make sure the not-found branch actually runs when the search returns nothing.
- Test with a genuinely new record and a genuinely returning one. Both paths need to be exercised.
None of this is platform-specific. The same pattern applies whether you are writing to GoHighLevel, Monday.com, Airtable or a CRM of your own — the shape of the fix is identical.