Clean and enrich a HubSpot contacts export
Take a contacts CSV exported from HubSpot, verify every email, find missing work emails, optionally fill job titles, and produce an import file that updates the existing records. Ravn has no native HubSpot app, so this is a file round-trip: export, enrich with the CLI, re-import.
One command.
Installs into ~/.claude/skills for Claude Code. In Claude, upload the downloaded file under Settings → Capabilities → Skills.
npx ravn skill install hubspot-export-cleanup
When to use
- Bounces are climbing, or a big sequence is coming up.
- Contacts have names and companies but no email, or unchecked emails.
- The user says "clean up my HubSpot contacts" and can export a CSV.
Before you start
- Check the connection:
npx ravn whoami(free). Note lookups left this window. reverse_email_lookupneeds your own provider key connected — runravn keys add pdlfirst; this tool runs on your own provider account. If it returns 403provider_key_required, stop and tell the human to add their key. Verifying emails and finding emails by name are ours and need no key. Every call is one lookup; misses are free.- Ask the user to export with the Record ID column included. That ID is what lets HubSpot update existing contacts on re-import instead of creating new ones.
- Read the header row. Column names vary; common ones are
Record ID,First Name,Last Name,Email,Website URLandJob Title. Use the real names.
Steps
- Remove opted-out contacts first. Filter out anyone unsubscribed and keep them out of every later step.
- Verify existing emails. Quote
--mapvalues that contain spaces.
npx ravn enrich contacts.csv --tool validate_email --column Email --dry-run npx ravn enrich contacts.csv --tool validate_email --column Email --limit 10 --out sample.csv npx ravn enrich contacts.csv --tool validate_email --column Email --out step1.csv
- Split
step1.csvlocally into: rows withrw_verdict=valid, and rows that are blank,invalid,riskyorunknownand have a name plus a company website. Write that second group toneeds-email.csvwith the step 1rw_*columns renamed (for examplerw_verdicttoold_verdict) or dropped, so the next pass does not create clashing column names. - For the second group, try to find a better work email:
npx ravn enrich needs-email.csv --tool search_business_email_by_name --map "first_name=First Name,last_name=Last Name,company=Website URL" --limit 10 --out sample2.csv npx ravn enrich needs-email.csv --tool search_business_email_by_name --map "first_name=First Name,last_name=Last Name,company=Website URL" --out step2.csv
Accept a found address only when its rw_verdict is valid. Treat risky (catch-all) as a suggestion for the user to review. 5. Optional, needs your provider key: for valid rows with an empty job title, run reverse_email_lookup --column Email on a copy with the step 1 rw_* columns renamed, to fill rw_title, rw_seniority and rw_company. The dry run shows how many lookups the pass will use. 6. Build the import file. Keep Record ID. Write the best email into Email, copy rw_title into Job Title only where it was empty, and add a column such as Email Verdict with the verdict. Never overwrite hand-entered data without asking. 7. Tell the user how to import: Contacts, Import, file from computer, and map Record ID so existing records are updated. Any custom property (like Email Verdict) must exist in HubSpot before it can be mapped.
Output
hubspot-import.csv: Record ID plus the updated columns, one row per contact.removed.csv: contacts with no usable email, withrw_verdictandrw_reason.- A summary: contacts in, valid as-is, fixed with a new email, titles filled, still unusable, and lookups used.
Working files keep rw_status (ok, not_found, error, dropped), rw_verdict and other rw_<field> columns.
Rules
- Never re-add or email contacts who unsubscribed or opted out. Ravn's own opt-out page is /privacy#optout.
- Drop invalid and risky addresses from any sending list.
- No FCRA uses: do not clean contacts to decide credit, employment, insurance or housing.
- On a 429
fair_use_exhaustedor a 403provider_key_required, stop and tell the human. Rows not reached aredropped; rerun later to finish.
Example prompt
I exported all 3,200 contacts from HubSpot. Half of them are from an old trade show. Verify the emails, find new ones where they bounced, and give me a file I can import back without duplicates.
More skills
Install them all with npx ravn skill install --all.