Skip to content
← Back

Case study · Interactive prototype

CRM Import Module

Bulk data migration that does not block onboarding. Error isolation and heuristic column matching turn a first CRM migration from hundreds of identical row-by-row fixes into a handful of decisions grouped by cause.

Role
Product design, prototyping
Scope
Import funnel, four stages
Surface
Web, desktop-first
Status
Interactive prototype
Origin
Self-initiated
Live prototype: data onboarding and quarantineOpen full screen

This is a dense desktop workspace, and a phone-width frame misrepresents it. Open it full screen on a larger display.

Open full screen

Context & problem

Bulk import is where CRM onboarding fails.

A CRM rollout opens with the same task: move the contacts you have into the system you just bought. The file is dirty. Someone merged two columns, dates arrive in three formats, the same company is spelled four ways, and a few hundred contacts point at a parent account nobody has created.

The hard part is the accounting. A conventional importer guesses the column mapping, writes what it guessed, then reports failure as a list of row numbers. You make the same decision several hundred times, with no way to see that it is the same decision.

Constraints

Four rules I held to.

I settled these first, and everything downstream followed:

Fixed destination schema
Fifteen CRM fields. Email and Company Name are mandatory, and Continue stays disabled until a settled mapping fills both.
Nothing writes until the end
Every stage writes to a staging layer in the browser. Nothing reaches the CRM until you press Finalise Import, which is what makes going backwards safe.
One column, one field
Two source columns must not claim the same destination without telling you. A collision you cannot see costs more than a decision you have to make.
Dense data, no type identity
A native system font stack rather than a web font. On a grid this dense, loading one costs a reflow you watch happen. People operate this screen.

Solution

Spend the user's attention where the machine is least certain.

The importer runs in four stages: Upload, Map columns, Review data, Resolve & import. A stepper keeps finished stages navigable.

  1. 01

    Confidence decides how much attention a column earns

    I score each source header against the schema on exact match, substring coverage and token overlap, which gives it a confidence from 0 to 100. Above 95 a column maps itself and costs zero clicks. Between 50 and 94 it pre-fills and takes one click to confirm. Below 50 it stays empty and waits for a manual choice. Those three bands become the three tabs, Action required, Needs review and Auto-mapped, so the queue sits in the order of how much you are needed. When two columns compete for one field, the strongest keeps it and the others drop to manual.

    The mapping stage split into Action required, Needs review and Auto-mapped tabs, with low-confidence source columns — Region Code at 44%, Segment (legacy) at 38% — still waiting on a field decision.
    Column mapping: confidence tiers and review tabs
  2. 02

    Review the staged records, not the file

    A paginated pass over the staged dataset, showing rows as the CRM will store them rather than as you uploaded them. This is the last point where changing the shape of the import stays cheap.

    The full 50,000-row dataset staged in the browser, paginated 50 to a page, with the flagged rows grouped into cause buckets — Missing Email, Bad Date Format, Duplicate IDs, Blocked by Parent — as filters above the table.
    Review stage: the paginated staged dataset
  3. 03

    Quarantine is organised by cause, not by row

    Conflicts fall into four buckets: Missing Email, Bad Date Format, Duplicate IDs and Blocked by Parent. Each bucket carries only the bulk actions that suit it, so you generate placeholders, reformat dates, merge duplicates, keep the newest, or convert to leads. One decision clears hundreds of rows, and you can undo any bulk action as a batch. Email is mandatory, so the flow never offers to import without one. Those rows become Leads, which need no email.

    The quarantine split into its four cause buckets — Missing Email, Bad Date Format, Duplicate IDs and Blocked by Parent — each carrying only the bulk actions that fit it.
    Quarantine: buckets and their bulk actions

Impact

What the prototype shows.

How it changes the shape of the work:

Unblocked user flow
Quarantine holds the broken rows, so the valid ones import straight away.
Reduced cognitive load
Confidence scoring clears the exact matches, so you decide only on the columns it could not place.
Protected backend
Validating in the browser keeps malformed CSVs from reaching the server.

The claim is narrow and testable. Group decisions by cause rather than by row, and the number of decisions falls to the count of distinct causes.

Target audience

CRM Administrators and SalesOps managers running their first bulk data migration.