# Dedupe and Normalize a Contact List — Ravn

Canonical: https://askravn.com/skills/dedupe-and-normalize-contacts

Claude skill

# Dedupe and normalize a contact list

Tidy a messy contact file: normalise every email, merge duplicate people, and flag role accounts, free-provider addresses and disposable domains. Most of the work is done locally for free; Ravn's `validate_email` supplies the flags that cannot be worked out from the text alone.
[Download SKILL.md](https://askravn.com/skills/dedupe-and-normalize-contacts/SKILL.md) [Start free](https://askravn.com/#start)

Install
## One command.
Installs into `~/.claude/skills` for Claude Code. In Claude, upload the downloaded file under Settings → Capabilities → Skills.
```
npx ravn skill install dedupe-and-normalize-contacts
```

## When to use

- Several lists were merged and the same person appears three times with different casing.
- The user wants a clean master file before importing into a CRM or a sending tool.
- "Dedupe this", "normalise these emails", "which of these are info@ or Gmail?"

## Before you start

- Check the connection: `npx ravn whoami` (free).
- Costs: the local clean-up is free. Flagging with `validate_email` is 1 lookup per unique address after dedupe, so dedupe first and you use fewer lookups.
- Ask which columns identify a person (usually email; sometimes name plus company when emails are missing) and which row should win when duplicates disagree (newest, most complete, or a source column).

## Steps

- Normalise locally, writing to a new column `email_normalized` and leaving the original untouched: - Trim spaces, remove surrounding `<>` and `mailto:`, lowercase the whole address. - Remove obvious junk: blank cells, values without exactly one `@`, trailing punctuation. - Split cells that hold two addresses (`a@x.com; b@x.com`) into separate rows only if the user agrees.
- Build a dedupe key from `email_normalized`. For matching only (not for sending), you may also strip a `+tag` from the local part and, for gmail.com and googlemail.com, remove dots before the `@`. Tell the user if you do this.
- Group rows by key. Merge each group into one row: keep the winning row's values and fill its empty cells from the others. Add `duplicate_count` and `merged_from` (row numbers).
- For rows with no email, dedupe on lowercased name plus company domain, and mark them `match_type: name_company` so the user can review.
- Save the deduped file, then flag it with Ravn. Dry run, sample, full:

```
npx ravn enrich deduped.csv --tool validate_email --column email_normalized --dry-run
npx ravn enrich deduped.csv --tool validate_email --column email_normalized --limit 10 --out sample.csv
npx ravn enrich deduped.csv --tool validate_email --column email_normalized --out clean.csv
```

- Over MCP, a deduped list of up to 1,000 can go in one `validate_emails` call, which already returns one verdict per unique address:

```
{ "emails": ["info@acme.com", "jane@gmail.com", "x7@mailinator.com"] }
```

- Read the flags: `rw_role_account`, `rw_free_provider`, `rw_disposable`, `rw_catch_all`, plus `rw_verdict`. Add a single `contact_flag` column: `disposable`, `role`, `free`, `invalid` or blank.

## Output

`clean.csv` with every original column plus:

- `email_normalized`, `duplicate_count`, `merged_from`, `match_type`
- `rw_status` (`ok`, `not_found`, `error`, `dropped`), `rw_verdict`, `rw_risk_score`, `rw_reason`
- `rw_role_account`, `rw_free_provider`, `rw_disposable`, `rw_catch_all`
- `contact_flag`
And a summary: rows in, rows out, duplicates merged, and counts of role, free, disposable and invalid.

## Rules

- Never delete the user's original file. Write new files.
- Do not rewrite an address the person gave (for example removing Gmail dots) in the sending column. Those rules are for matching only.
- Keep opt-out and unsubscribe status when merging: if any duplicate opted out, the merged row is opted out. See [/privacy#optout](https://askravn.com/privacy#optout).
- Drop disposable, invalid and risky addresses from any send list.
- No FCRA uses. On a 429 `fair_use_exhausted`, stop and tell the human; on exit code 4, the 5-hour window is full and the run will wait unless `--no-wait` was set.

## Example prompt

I merged three exports into contacts-all.csv and there are loads of duplicates. Clean up the emails, merge the dupes, and tag which ones are generic inboxes, Gmail, or throwaway addresses.

## More [skills](https://askravn.com/skills)
Install them all with `npx ravn skill install --all`.
- [Email Address to LinkedIn Profile LookupUse when someone has a list of email addresses and wants the person behind each one, with LinkedIn profile URL, name, title and company..](https://askravn.com/skills/email-to-linkedin-profile)
- [Enrich Any CSV with Contact and Company DataUse when someone hands over any CSV and wants it enriched, picking the right tool and column mapping, sampling, then running the full file safely..](https://askravn.com/skills/enrich-csv)
- [Enrich and Score an Inbound LeadUse when a new sign-up or form fill arrives with just an email and someone wants to know who it is, what company, and whether it fits the ICP..](https://askravn.com/skills/enrich-inbound-lead)
- [Enrich a Conference Attendee ListUse when someone has a conference or trade show attendee list with names and companies and wants verified emails and job titles for follow-up..](https://askravn.com/skills/event-attendee-enrichment)
- [Find Decision Makers at Target CompaniesUse when someone has a list of target company domains and needs the actual buyers at each one, with titles and verified work emails..](https://askravn.com/skills/find-decision-makers)
- [Find Someone's Work Email by NameUse when someone needs one person's work email from their name and company, found from the company's own address pattern and then checked..](https://askravn.com/skills/find-work-email)
