Contacts CSV import API
contacts_v1
Import this template via API. Import CRM contacts from CSV. Account-linked, email-validated, decision-maker flag, multilingual headers.
30-second curl
curl -X POST https://api.adaptivmapr.com/v1/uploads \ -H "Authorization: Bearer $ADAPTIVMAPR_API_KEY" \ -F "template=contacts_v1" \ -F "file=@your_data.csv"
At a glance
- Pack
- CRM
- FHIR resource
- —
- Risk level
- low
- Fields
- 9 (1 required, 2 validated)
Why it exists
Why this template?
The Contacts template is people at named accounts — the post-qualification population that leads convert into. Each row carries an id, a foreign key account_id, identity fields (first_name, last_name), contact details (email, phone), a job title, a decision-maker boolean, and a last_contacted_at timestamp. Sales-ops teams use this template after a leads import to materialise the contacts-at-accounts relationship, after a CRM migration to reconstruct the org chart, and before a campaign to refresh the decision-maker flag. The is_decision_maker field is a boolean rather than an enum because nuanced authority (economic buyer vs technical buyer vs influencer) belongs in a separate model — the canonical row tracks the binary: does this person sign or not.
account_id is not required because contacts can exist before their account is created — but most downstream systems eventually want the link. last_contacted_at parses across formats; null means the relationship is dormant. Title hints cover DE, FR, IT, ES so a multilingual CRM export does not escalate to the LLM.
Migration scenarios & common foreign headers
Migration scenarios for the Contacts template: CRM migrations (Salesforce → HubSpot, Pipedrive → Close), enrichment runs that refresh titles after a quarterly outreach campaign, post-acquisition imports where the acquiring team needs the acquired company's contact graph in place, and ABM list uploads that target specific personas at named accounts. Foreign headers we see routinely: "Account ID / Konto / Cuenta / Vorname / Prénom / Nombre / Nachname / Nom / Apellido / Email / Telefon / Téléphone / Teléfono / Titel / Titre / Titolo / Cargo / Job Title / Entscheider / Décideur / Decisor / Last Contacted / Letzter Kontakt". The cascade picks up the multilingual job-title vocabulary at zero cost — title is the single most-localised field in CRM data, and the heuristic layer absorbs it without escalating.
Canonical columns
Schema
The canonical column set, with the type each row carries, whether it is required, the field-level validators that fire on commit, and the multilingual header hints the cascade resolves against.
| Column | Type | Required | Validators | Hints |
|---|---|---|---|---|
| id | string | yes | — | — |
| account_id | string | — | — | konto · compte · account · cuenta |
| first_name | string | — | — | vorname · prenom · prénom · nombre · given name |
| last_name | string | — | — | nachname · nom · cognome · apellido · surname |
| — | e-mail · mail · correo | |||
| phone | phone | — | phone | telefon · téléphone · telefono · phone · mobile |
| title | string | — | — | titel · titre · titolo · cargo · job title |
| is_decision_maker | boolean | — | — | entscheider · décideur · decisor |
| last_contacted_at | date | — | — | — |
The cascade
How AdaptivMapr maps your headers to this template
Five layers run in order, cheapest first. Layer 1 (Statistics) auto-accepts headers that have been mapped the same way across past uploads. Layer 2 (Heuristic) compares your header to the column name, the optional label, and every registered hint (DE / FR / IT / EN / ES) after a Unicode-and-punctuation-normalising pass. Layer 3 (Fuzzy) catches typos and reordered words. Layer 4 (Semantic) uses cached embeddings to catch the long tail of paraphrases. Layer 5 (LLM) only fires on genuinely ambiguous columns, constrained to the template’s allowed column set so it cannot invent a field. When a layer auto-accepts, the lower-cost layers below it never run — that is the cost lever.
REST · POST /v1/uploads
Pass the template_id; the cascade picks up the rest.
curl -X POST https://api.adaptivmapr.com/v1/uploads \ -H "Authorization: Bearer $ADAPTIVMAPR_API_KEY" \ -F "template=contacts_v1" \ -F "file=@your_data.csv"
The canonical template definition is read-only at GET /v1/templates/contacts_v1.
MCP · Cursor / Claude Desktop
Drop AdaptivMapr into your IDE. Schema-only calls need no key and draw only the small flat per-map fee.
// In Cursor or Claude Desktop with the AdaptivMapr MCP server installed:
adaptivmapr.match_headers({
template: "contacts_v1",
headers: ["id", "account_id", "first_name", "last_name"]
})MCP install instructions →Questions
FAQ — Contacts CSV import
Why is is_decision_maker a boolean instead of a role enum?
Sales-process role taxonomies (champion, blocker, influencer) are team-specific and shift across deal stages. The canonical row tracks the binary so cross-team reports compose.
Can a contact belong to multiple accounts?
Each row is one membership. Multi-account contacts get one row per account — the contact id stays stable, the account_id varies.
How do I handle contacts who left the company?
Add an `is_active` flag via a fork, or maintain a separate departed-contacts table. The canonical template assumes current.
Does the template validate phone numbers as E.164?
The phone validator checks shape (digits, plus sign, separators) but does not enforce strict E.164. A downstream libphonenumber pass normalises to E.164 cleanly because the cascade guarantees a parseable shape.