Skip to main content

Import or migrate contacts

Getting contacts into Orbit is the first real task of onboarding, and four different surfaces can do it. This guide is the decision walkthrough: it tells you which surface to pick for which source, then walks the first-run workflow once — file shape, preview, import, consent mapping, dedupe, verify — so the links it hands off to are ordered stops, not a pile of options.

1. Pick the surface: decision table

Four surfaces move contact-shaped data into Orbit. Answer two questions — where does the data live, and what exactly does a row carry? — and the table resolves to one answer. The dashboard has two adjacent routes that both matter here, and the guide below uses them deliberately:
  • Audience → Import — the CSV wizard itself (upload, map, preview, run). One wizard pass per file.
  • Audience → Imports — the import-history page: every past job with status, per-job counters, the skipped-rows download, and a resume link into in-flight jobs. When you need “which import landed and what did it skip,” this is the answer.
The one-click migration wizard and its operations page (Settings → Migrations) live separately — see run a platform migration end-to-end.

2. Bulk CSV import to a contact list

The full endpoint-by-endpoint walkthrough is import and reconcile contacts. The file-shape decisions are the load-bearing ones, and they are all settled before upload:
  1. One address per row. Each row needs a phone or an email; rows with neither are skipped. Rows you do not fix at export become the skipped-rows CSV after the run.
  2. Phone format. E.164 (+14155552671) is preferred. The server tolerates whitespace and punctuation, and PBX-extension suffixes (x1234). Rows it cannot parse are skipped rather than guessed.
  3. Opt-in columns. Consent is a property of the channel on the row, not of the row itself — decide before import which file column maps to which consent signal. The mapping makes send-time suppression deterministic instead of retroactive (see step 4).
  4. Custom field headers. A header that names a custom field key maps into that field. Define the field first: values are typed and validated on write, and a row that fails the validator is skipped (see step 5).
Run the file deliberately: idempotent re-import of the same file is safe — the merge strategy (skip = leave the existing record; merge = update fields from the row) is what decides whether re-import is a no-op or a refresh. Duplicates on phone anywhere (between the file and the tenant, or inside the file) are surfaced by the preview endpoint before anything is written, so a re-run never silently forks two contacts for one person.

3. The API path

Two endpoints implement the same job shape the dashboard wizard drives; the wizard and the API are not separate pipelines.
  • POST /api/v1/contacts/imports/preview — dry-run: validates row shape, counts invalid rows, and reports duplicates (inside the file and against the existing tenant). Writes nothing.
  • POST /api/v1/contacts/imports — enqueue the job: accepts up to 1,000,000 rows, returns 202 with a job_id. Poll GET /api/v1/contacts/imports/{id} until status leaves pending / processing; statuses are pending → processing → {completed, failed, cancelled}.
  • GET /api/v1/contacts/import-jobs/{id}/skipped.csv — the row-error report: one row per skipped input row, with row_index, a human-readable reason, and the original columns.
  • POST /api/v1/contacts/imports/{id}/cancel and POST /api/v1/contacts/imports/{id}/rollback — cancel stops an in-flight job; rollback hard-deletes the contacts the job created. The rollback window is 24 hours after the job finishes.
The row-shape and retry contract for these endpoints is import and reconcile contacts; the endpoint-by-endpoint schema is the Contacts API reference. A 503 at enqueue means the queue is unavailable — fall back to the synchronous POST /api/v1/contacts/bulk (≤10,000 rows per request) or retry.

4. Opt-in and suppression on import

Importing an address is not consent, and an import job is not a suppression signal. Suppression and opt-out handling is enforced at send time, not at import time — so a row that lands cleanly still cannot receive a marketing send if it is opted out, and a row that imports with an opt-in marker still cannot bypass an existing opt-out. Decide the opt-in mapping in the file shape (step 2), and treat the optics pair as a control:
  • Rows whose opt-in column says “no” should land suppressed via an opt-out list before the first send — not merely tagged.
  • Existing opted-out contacts must stay opted out across a re-import; choose merge on re-import only if the file’s consent column actually carries new, better-granted consent.
  • The import rollback cascade (step 3) removes consent records along with created contacts, so rolling back a bad import never leaves orphaned opt-in flags.
The consent and suppression model — and why enforced-at-send-time import is the safe default — lives in consent and suppression.

5. Map custom fields without inventing headers

Custom-field mapping follows one rule that saves most import errors: the header is a field key, the field was defined first, and values are validated on write.
  • Naming. Map the column header directly to the field key — no prefix, no Casing change. A header that does not match a defined key is either plain contact data (ignored on write) or a mistake that only shows up as skipped rows.
  • Type coercion. CSV cells are strings; the validator coerces them into the field’s declared type on write. date fields reject unparseable dates, number fields reject non-numeric text, and boolean expects the declared truthy set.
  • Common errors. A header that collides with another mapped header (duplicate), a column that maps to a required field but is missing from the file (violates the required-field constant), and a row whose value fails the type check — each lands in the skipped-rows report with its own reason string.
Define the fields up front in custom fields; the wizard’s mapping step will not invent them.

6. CDP profile import

Reach for the CDP path when a row is more than one address — when it carries an identity (userId, an email and a phone together) plus traits and events, so identity resolution decides which existing profile it merges onto.
  • Identity merge on input. The CDP ingest surfaces (POST /api/v1/cdp/file-ingest for CSV/JSON batch files, the HMAC SDK surface for streams) run each row through the same identity rules the identity resolution model defines: an incoming userId deterministically merges onto one contact; the survivorship policy decides which value wins when two profiles collide.
  • Re-import does not duplicate. Because ingest keys on identity, re-importing the same file merges onto the profile it already created — it never forks a second identity. The identity rules and survivorship policy you author in the Integrations → CDP tabs are documented in CDP data catalog and identity rules; preview the merge effects with simulation before any rule change (see that guide).
  • When not to take this path. If all you have is a phone and a list of tags, the plain contact import is simpler — the CDP path pays for itself only when identity resolution is the point.

7. Migration jobs: moving off another CPaaS

When the source is one of the five supported platforms — Twilio, Telnyx, Klaviyo, MessageBird, Front — the migration wizard is the correct surface and a plain CSV import is the wrong one: the wizard validates the credential, lists the entities the source exposes (phone numbers, templates, contacts, configuration objects), and dry-runs before writing anything. Re-shaping a provider export into CSV loses the non-contact objects and adds a manual step. Use a plain import for this only when the source is a provider with no connector and you shaped the file yourself — and then decide it deliberately, not because the wizard looked like the wrong button.

8. Post-import: preview the net reachable audience

An imported audience is not a reachability number — suppressed, opted-out, and unreachable contacts count toward the imported total but not toward send-able recipients. Before the first campaign, dry-run the audience:
The preview returns the matching total plus the excluded cohorts — unreachable (missing address, opted out, suppressed) — with a sample of resolved recipients. A large suppressed cohort after an import almost always means the consent mapping (step 4) was not done in the file, or a prior opt-out list funnels the audience. Fix the mapping, re-import, and re-preview; do not launch until the net count is what you expect.

9. Troubleshooting

See also