Moving your contact spreadsheet into Norbelys without losing data
The real steps for migrating a spreadsheet of contacts into Norbelys: mapping fields, deduplication, verification, and what happens to the rows that don't survive.
By Gabriel Lara, Developer Relations, Norbelys
Founder-reviewed ·How we research and correct articles
“Just upload the CSV” undersells what actually happens during a real migration, and if you’ve never moved a contact list into a real sending platform before, the gap between “drag a file in” and “have a clean, sendable audience” is where most of the actual work is. Here’s what genuinely happens, step by step, when a spreadsheet becomes a Norbelys audience — not the marketing version, the mechanical one.
What’s actually in the spreadsheet you’re about to import
Before the import itself, be honest about what a typical working
spreadsheet looks like after a year or two of accumulation: rows added by
three different people with three different naming conventions for the
same columns, a Company column that’s sometimes a domain and sometimes a
brand name, email addresses copy-pasted with trailing spaces, and the same
person appearing twice because someone re-added them after a title change
without checking first. None of that is unusual — it’s just what
spreadsheets look like once more than one person has touched them.
Nothing that doesn’t make it through the import vanishes — every excluded row keeps a reason attached to it.
The four steps that actually happen
From raw CSV to sendable audience
Map the columns
Standard fields — email, givenName, familyName, jobTitle, and the rest of the Schema.org-based vocabulary the API uses — get matched to your CSV headers, whatever they're actually named in the file. Anything that doesn't map to a standard field becomes a custom field automatically, so a column like "Renewal Date" or "Lead Source" isn't dropped, it's preserved and filterable later.
Deduplicate by email
Rows that share an email address, regardless of casing or surrounding whitespace, get merged into one person rather than imported as duplicates. This is the step that quietly fixes the most damage in an old spreadsheet — the same contact re-added under a slightly different capitalization months apart, which a naive import would treat as two separate people.
Verify every address
Each surviving row runs through address verification in the background — syntax, domain validity, disposable-domain detection, and a check against known bounce and suppression history. This doesn't block the import; it runs asynchronously and attaches a verdict to each row.
Review the import report
The finished job shows exactly how many rows became people, how many were merged as duplicates, and how many were flagged with a specific reason — invalid syntax, disposable domain, already suppressed. Nothing fails silently; every excluded row is explained, not just missing.
The mapping decisions that actually matter
Field mapping sounds like the boring step, but it’s where a sloppy import turns into a genuinely broken one. A few decisions worth getting right before you confirm the mapping, not after:
Split name fields honestly. If your spreadsheet has a single Name
column with “Jane Smith” in it, decide up front whether you want it split
into givenName and familyName (needed for any template that
personalizes with a first name specifically) or left as one field. A
naive split on the first space breaks on anyone with a multi-word first or
last name — worth a manual pass on a sample of rows before trusting an
automatic split across the whole file.
Don’t let a phone or date column become a custom-field free-for-all.
A Last Contacted column exported as 03/14/2026 from one source and
2026-03-14 from another produces two visually different values that
mean the same thing, and a segment filtering on that field won’t
recognize them as equivalent unless the format is normalized before
import. Standardize date and phone formats in the source file first if
you’re relying on them for filtering later — it’s a five-minute
find-and-replace pass that saves a confusing segment result afterward.
Decide what a blank cell means. An empty Company cell could mean
“unknown” or it could mean “this person genuinely has no company” — those
are different facts, and if it matters for how you’ll segment later,
resolve the ambiguity in the source file rather than importing thousands
of blanks and guessing at their meaning after the fact.
None of these are Norbelys-specific quirks — they’re the same data-hygiene questions any import into any real system raises. The difference is that a platform with verification and field mapping surfaces them at import time, where they’re a five-minute fix, instead of three weeks later when a segment returns a count that doesn’t make sense and nobody remembers why.
Doing it from the dashboard versus scripting it
For a one-time list of a few thousand contacts, the dashboard’s CSV upload is the right tool — drag the file in, confirm the field mapping the interface suggests (it guesses reasonably well from header names, but always check it against a real row before confirming), and let verification run. You get a visual report you can actually read, which matters for a one-off where you want a human glancing at the result before trusting it.
For a recurring migration — pulling a fresh export from a CRM every week, or migrating several client lists at once — the same operation is a job through the API instead of a dashboard click:
--on-conflict fill matters specifically for a migration: it fills in
blank fields on an existing person rather than overwriting data you
already have in Norbelys with blanks from an older export. If you’re
scripting this against the REST API directly instead of the CLI, the job
pattern post covers the exact
POST /v1/jobs shape, polling for progress, and reading the completion
report — the same job resource underneath either surface.
The rows that don’t make it, and why that’s the point
The instinct when a smaller number comes out than went in — 4,022 sendable people from 4,912 raw rows — is to treat it as data loss. It’s closer to the opposite. A spreadsheet-to-SMTP setup has no verification step at all: every row in the file gets treated as sendable, including the ones with a typo’d domain, a disposable address from a lead magnet three years ago, or an address that’s hard-bounced on every previous campaign you’ve run. That setup doesn’t lose those rows — it sends to them anyway, and finds out they were bad the expensive way, as a bounce that dings the sending domain’s reputation instead of a flagged row caught before send one. What a spreadsheet-and-relay setup can’t see about a list covers the deliverability side of that gap in full; the migration version of the same point is narrower: verification during import is the difference between finding out a row is bad before it costs you anything, and finding out after it’s already dragged your bounce rate up.
Custom fields: the part people assume they’ll lose
A common worry going into a migration is that anything beyond the obvious name-and-email columns gets dropped. It doesn’t. A column that doesn’t map to a standard field — a lead source, a renewal date, an account tier, a free-text note field — becomes a custom field on import automatically, addressable the same way standard fields are: filterable in a segment definition, visible on the person record, referenceable in a template. The practical effect is that whatever made your spreadsheet useful beyond “name and email” — the columns you built up manually over time — survives the move intact instead of being the thing you quietly lose in exchange for a real platform.
What this replaces: the manual dedupe pass nobody enjoys
If you’ve maintained a growing contact spreadsheet for more than a few months, you already know the manual version of steps two and three above — sorting by email, eyeballing for near-duplicates, spot-checking a handful of addresses in a verifier tool before a big send because checking all of them by hand isn’t realistic. That process is real work, it’s tedious, and it’s exactly the kind of task that degrades under time pressure — the week before a launch is precisely when a manual dedupe pass gets rushed or skipped. Running it as an automated step on every import, not just the ones where someone remembered to be careful, removes the dependency on someone remembering at all.
Migration questions people actually ask
Will importing a CSV overwrite data I've already entered for existing contacts?
Only if you let it. The bulk-create operation supports an on-conflict mode — fill only populates blank fields on an existing person and leaves populated ones untouched, which is the right default for a repeat migration from an older export.
What happens to rows with a malformed or missing email address?
They're flagged and excluded from the created count, with the specific reason recorded in the job's report rather than the row silently disappearing. You get an explicit list of what to go fix in the source file if you want to re-import them later.
Can I import into Norbelys and keep using my spreadsheet as a backup?
Yes, nothing about the import deletes or modifies your original file — it's a one-way copy into Norbelys. Just be aware the spreadsheet stops being the source of truth the moment people start replying, unsubscribing, and getting suppressed inside Norbelys, since none of that activity writes back to the original CSV.
Is there a row limit on a single CSV import?
Check current limits in the OpenAPI spec at the developer portal, since they're tuned over time, but the job pattern underneath the import doesn't impose an architectural ceiling the way importing one row per request would — large migrations are exactly the case it's built for.
Bring the list over, keep what made it useful
The honest reason to make this move isn’t that spreadsheets are bad at storing contacts — it’s that they can’t verify, deduplicate, or protect a list the way a platform built for sending has to. Audience management — people, custom fields, groups, and segments — ships on every plan starting at Starter, so migrating your list doesn’t cost more just because it’s your first import. Check the plan that fits your list size, and bring the spreadsheet over — the columns you built up over time come with it, minus the duplicates and dead addresses you were sending to without knowing it.