CSV · Export → Sequencer · CRM
Check and fix your contact data.
Your agent reads the CSV or export and sends the rows to ListPlus as JSON. 100+ checks catch domain typos, disposable and private addresses, addresses without a domain, placeholders, stray spaces, duplicates and countries in three spellings. Back comes a report per row – field, finding, proposed fix. Your agent applies the proposals and hands you the corrected rows; whatever cannot be fixed stays flagged.
One call from Claude Code, n8n or ChatGPT
lena.graf@gmial.com → gmail.comInspect check IDs
email_typoInteractive example · fictional contacts
Watch it
Input and output
What goes in, what comes back.
Eight rows, each with a planted problem: a name in caps and a typo in the domain, a lowercase company and the country in three spellings, a disposable address, a private Gmail address, a doubled space, “(Open to Work)” in the last name, an address without a domain, “n/a” as job title, one person twice. On the right the real answer of check_records: per row field, finding, proposal; below it the summary per check.
curl -X POST https://listplus.ai/a/v1/check_records \ -H "Authorization: Bearer lpk_…" \ -H "Content-Type: application/json" \ --data @records.json
Without a key up to 25 rows (then without the Authorization header), with a key up to 1,000. From Claude Code or ChatGPT the same call is the MCP tool check_records; in n8n an HTTP node.
records.json{
"records": [
{
"first_name": "ANNA",
"last_name": "KELLER",
"company_name": "Nordwerk GmbH",
"job_title": "Head of Sales",
"email": "anna.keller@gmial.com",
"country": "DE"
},
{
"first_name": "Ben",
"last_name": "Fischer",
"company_name": "nordwerk gmbh",
"job_title": "CFO",
"email": "ben.fischer@nordwerk-muster.de",
"country": "Deutschland"
},
{
"first_name": "Tom",
"last_name": "Lenz",
"company_name": "Seeblick Reisen",
"job_title": "Marketing",
"email": "tom@mailinator.com",
"country": "Germany"
},
{
"first_name": "Lena",
"last_name": "Graf",
"company_name": "Kontur",
"job_title": "VP Operations",
"email": "lena.graf@gmail.com",
"country": "DE"
},
{
"first_name": "Noah",
"last_name": "von Wolf",
"company_name": "Nordlicht",
"job_title": "CEO",
"email": "noah.wolf@nordlicht-muster.de",
"country": "DE"
},
{
"first_name": "Ava",
"last_name": "Stein (Open to Work)",
"company_name": "HelioWorks",
"job_title": "n/a",
"email": "ava.stein@",
"country": "DE"
},
{
"first_name": "Mia",
"last_name": "Winter",
"company_name": "HelioWorks",
"job_title": "Head of People",
"email": "mia.winter@helioworks-muster.de",
"country": "DE"
},
{
"first_name": "Mia",
"last_name": "Winter",
"company_name": "HelioWorks",
"job_title": "Head of People",
"email": "mia.winter@helioworks-muster.de",
"country": "DE"
}
],
"categories": [
"name",
"email",
"company",
"job_title",
"address",
"duplicates",
"completeness"
],
"language": "en"
}{
"ok": true,
"action": "check_records",
"fields": {
"first_name": "first_name",
"last_name": "last_name",
"company_name": "company_name",
"job_title": "job_title",
"email": "email",
"country": "country"
},
"score": 8.5,
"records": 8,
"findings_total": 17,
"by_record": [
{
"index": 0,
"findings": [
{
"check": "name_all_caps",
"name": "Name in ALL CAPS",
"category": "name",
"severity": "warning",
"field": "first_name",
"value": "ANNA",
"message": "First name is in ALL CAPS",
"fix": "Anna"
},
{
"check": "name_all_caps",
"name": "Name in ALL CAPS",
"category": "name",
"severity": "warning",
"field": "last_name",
"value": "KELLER",
"message": "Last name is in ALL CAPS",
"fix": "Keller"
},
{
"check": "email_typo",
"name": "Email Domain Typo",
"category": "email",
"severity": "warning",
"field": "email",
"value": "anna.keller@gmial.com",
"message": "Possible domain typo detected",
"fix": "anna.keller@gmail.com"
}
]
},
{
"index": 1,
"findings": [
{
"check": "company_all_lowercase",
"name": "Company in all lowercase",
"category": "company",
"severity": "warning",
"field": "company_name",
"value": "nordwerk gmbh",
"message": "Company name is in all lowercase",
"fix": "Nordwerk GmbH"
},
{
"check": "address_country_inconsistent",
"name": "Country Format Inconsistency",
"category": "address",
"severity": "warning",
"field": "country",
"value": "Deutschland",
"message": "Inconsistent country format (\"Deutschland\" vs \"DE\")",
"fix": "DE"
}
]
},
{
"index": 2,
"findings": [
{
"check": "email_disposable",
"name": "Disposable Email Domain",
"category": "email",
"severity": "critical",
"field": "email",
"value": "tom@mailinator.com",
"message": "Disposable/temporary email domain detected"
},
{
"check": "address_country_inconsistent",
"name": "Country Format Inconsistency",
"category": "address",
"severity": "warning",
"field": "country",
"value": "Germany",
"message": "Inconsistent country format (\"Germany\" vs \"DE\")",
"fix": "DE"
}
]
},
{
"index": 3,
"findings": [
{
"check": "email_personal",
"name": "Personal Email Domain",
"category": "email",
"severity": "info",
"field": "email",
"value": "lena.graf@gmail.com",
"message": "Personal email domain detected"
}
]
},
{
"index": 4,
"findings": [
{
"check": "name_whitespace",
"name": "Extra Spaces in Name",
"category": "name",
"severity": "info",
"field": "last_name",
"value": "von Wolf",
"message": "Last name has extra spaces",
"fix": "von Wolf"
}
]
},
{
"index": 5,
"findings": [
{
"check": "name_linkedin_noise",
"name": "LinkedIn Status Text in Name",
"category": "name",
"severity": "warning",
"field": "last_name",
"value": "Stein (Open to Work)",
"message": "Last name contains a LinkedIn status text",
"fix": "Stein"
},
{
"check": "name_annotation",
"name": "Remark in Name",
"category": "name",
"severity": "info",
"field": "last_name",
"value": "Stein (Open to Work)",
"message": "Name contains a remark in brackets or quotes",
"fix": "Stein"
},
{
"check": "email_format",
"name": "Invalid Email Format",
"category": "email",
"severity": "critical",
"field": "email",
"value": "ava.stein@",
"message": "Email missing top-level domain"
},
{
"check": "jobtitle_placeholder",
"name": "Placeholder Job Title",
"category": "job_title",
"severity": "warning",
"field": "job_title",
"value": "n/a",
"message": "Job title is a placeholder value"
}
]
},
{
"index": 6,
"findings": [
{
"check": "duplicate_email",
"name": "Duplicate Email",
"category": "duplicates",
"severity": "critical",
"field": "email",
"value": "mia.winter@helioworks-muster.de",
"message": "Duplicate email (2 occurrences)"
},
{
"check": "duplicate_name_company",
"name": "Duplicate Name + Company",
"category": "duplicates",
"severity": "warning",
"field": "first_name",
"message": "Duplicate name + company (2 occurrences)"
}
]
},
{
"index": 7,
"findings": [
{
"check": "duplicate_email",
"name": "Duplicate Email",
"category": "duplicates",
"severity": "critical",
"field": "email",
"value": "mia.winter@helioworks-muster.de",
"message": "Duplicate email (2 occurrences)"
},
{
"check": "duplicate_name_company",
"name": "Duplicate Name + Company",
"category": "duplicates",
"severity": "warning",
"field": "first_name",
"message": "Duplicate name + company (2 occurrences)"
}
]
}
],
"summary": [
{
"check": "address_country_inconsistent",
"name": "Country Format Inconsistency",
"severity": "warning",
"records": 2
},
{
"check": "duplicate_email",
"name": "Duplicate Email",
"severity": "critical",
"records": 2
},
{
"check": "duplicate_name_company",
"name": "Duplicate Name + Company",
"severity": "warning",
"records": 2
},
{
"check": "name_all_caps",
"name": "Name in ALL CAPS",
"severity": "warning",
"records": 1
},
{
"check": "email_typo",
"name": "Email Domain Typo",
"severity": "warning",
"records": 1
},
{
"check": "name_whitespace",
"name": "Extra Spaces in Name",
"severity": "info",
"records": 1
},
{
"check": "name_linkedin_noise",
"name": "LinkedIn Status Text in Name",
"severity": "warning",
"records": 1
},
{
"check": "name_annotation",
"name": "Remark in Name",
"severity": "info",
"records": 1
},
{
"check": "email_format",
"name": "Invalid Email Format",
"severity": "critical",
"records": 1
},
{
"check": "jobtitle_placeholder",
"name": "Placeholder Job Title",
"severity": "warning",
"records": 1
},
{
"check": "email_disposable",
"name": "Disposable Email Domain",
"severity": "critical",
"records": 1
},
{
"check": "email_personal",
"name": "Personal Email Domain",
"severity": "info",
"records": 1
},
{
"check": "company_all_lowercase",
"name": "Company in all lowercase",
"severity": "warning",
"records": 1
}
],
"options": "[…]",
"tell_your_user": "Checked 8 records, 17 findings. The main ones: Duplicate Email (2), Invalid Email Format (1), Disposable Email Domain (1). Nothing was changed."
}{
"records": [
{
"first_name": "Anna",
"last_name": "Keller",
"company_name": "Nordwerk GmbH",
"job_title": "Head of Sales",
"email": "anna.keller@gmail.com",
"country": "DE"
},
{
"first_name": "Ben",
"last_name": "Fischer",
"company_name": "Nordwerk GmbH",
"job_title": "CFO",
"email": "ben.fischer@nordwerk-muster.de",
"country": "DE"
},
{
"first_name": "Tom",
"last_name": "Lenz",
"company_name": "Seeblick Reisen",
"job_title": "Marketing",
"email": "tom@mailinator.com",
"country": "DE"
},
{
"first_name": "Lena",
"last_name": "Graf",
"company_name": "Kontur",
"job_title": "VP Operations",
"email": "lena.graf@gmail.com",
"country": "DE"
},
{
"first_name": "Noah",
"last_name": "von Wolf",
"company_name": "Nordlicht",
"job_title": "CEO",
"email": "noah.wolf@nordlicht-muster.de",
"country": "DE"
},
{
"first_name": "Ava",
"last_name": "Stein",
"company_name": "HelioWorks",
"job_title": "n/a",
"email": "ava.stein@",
"country": "DE"
},
{
"first_name": "Mia",
"last_name": "Winter",
"company_name": "HelioWorks",
"job_title": "Head of People",
"email": "mia.winter@helioworks-muster.de",
"country": "DE"
}
],
"left_out": [
{
"index": 7,
"duplicate_of": 6
}
],
"open": [
{
"index": 2,
"check": "email_disposable",
"field": "email"
},
{
"index": 3,
"check": "email_personal",
"field": "email"
},
{
"index": 5,
"check": "email_format",
"field": "email"
},
{
"index": 5,
"check": "jobtitle_placeholder",
"field": "job_title"
}
]
}Mechanical, no judgement: every proposal into its field, the second row of a duplicate left out. The disposable and private addresses, the address without a domain and “n/a” have no proposal – they stay under open, the decision is yours.
Setup
Build this workflow.
Rows from your export as JSON objects – name, company, email, country. The file stays with you: the agent reads it and sends the rows; the same call works from Claude Code, ChatGPT, n8n or Cursor. No ListPlus list is needed.
What does a run cost?
Standard checks cost 0 credits – per call up to 25 rows without a key or 1,000 with a signed-in connection. Fixes in a list (fix_list) cost nothing. Only AI checks and the deliverability check are charged.
- Your agent by call
Pass in the rows
Have your agent read the file and send name, company and email to check_records as JSON. In n8n that is one HTTP node; in Claude Code or ChatGPT it is one sentence.
- ListPlus by call
Read the report
Per row: field, finding, check ID and – where possible – a proposed fix. gmial.com → gmail.com, “US” and “United States” as two spellings of one country, “ACME INC” as a company name in caps, two rows for the same person.
- Your agent by call
Apply the fixes
Without a list, the agent applies the proposals to your file: casing, domain typos, stray spaces, company names, countries to one spelling. In a list, fix_list does it: one check at a time, clean values (whitespace, casing, placeholders) or merge duplicates – merged rows are hidden, never deleted.
- Your team manual in ListPlus
Decide what moves on
Hold disposable addresses, keep team inboxes (office@) separate – one often hides two people. Only then into the sequencer or the CRM.
With your agent
Give your agent this brief.
The standard check needs no ListPlus list. Split large exports into batches; duplicates across batches need a list.
The same call from Claude Code, ChatGPT, Cursor or an n8n HTTP node – MCP and REST offer the same tools.
Agent guide (.md) ↗Check the contacts in my CSV with ListPlus (check_records, standard checks for email, names, countries and duplicates). Give me the report per row: field, finding, proposal. Apply the proposals for email typos, casing and country names to the file, hold disposable addresses and keep people who share a team inbox separate. Tell me at the end what you changed.
Tools and sample call
check_records · add_records · fix_list
POST /a/v1/check_records
{
"records": [
{
"first_name": "Lena",
"last_name": "Graf",
"email": "lena.graf@gmial.com"
},
{
"first_name": "Anna",
"last_name": "Keller",
"email": "office@nordwerk-muster.de"
},
{
"first_name": "Ben",
"last_name": "Fischer",
"email": "office@nordwerk-muster.de"
},
{
"first_name": "Tom",
"last_name": "Lenz",
"email": "tom@mailinator.com"
}
],
"categories": [
"email",
"duplicates"
]
}Try it with your data
Countries get the spelling most frequent in the call – a fixed target format (such as the ISO code) cannot be set yet, and two batches can have different majorities; in a list the whole list counts. A plausible fix does not confirm deliverability. Checking sends nothing and stores nothing.