> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dmly.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Import contacts from CSV

> Bring an existing contact list into DMLY with a CSV file, and understand which rows get skipped.

If you already keep customers in a spreadsheet, a booking system or another tool, you can bring
them into DMLY as Contacts with a CSV file. The importer matches rows against people you already
have, so running it twice does not give you two of everyone.

Two things to know before you start, because they decide what actually lands:

<Note>
  **Every row needs a phone number.** Rows with a blank **Name** or a blank phone are skipped —
  silently, with no error. A CSV of email-only contacts imports nothing.

  **You must have a channel selected.** The importer attaches each contact to one channel. With
  no channel active, it stops with *"Select a channel before importing."*
</Note>

## Import a list

<Steps>
  <Step title="Open the importer">
    Go to **Contacts** and select **Import**. The page is headed **Import contacts** and tells you
    where they are going — *"Contacts will be imported into"* your current channel.

    If you don't see **Import**, your role doesn't include it. See
    [Roles and permissions](/account/roles-and-permissions).
  </Step>

  <Step title="Start from the sample">
    Select **Download sample** to get `salesbot-contacts-sample.csv`. It has the exact header row
    the importer expects. Replace the two demo rows with your own data and keep the header as it is.
  </Step>

  <Step title="Upload the file">
    Drop your file on the upload area or select it. Only `.csv` files are accepted.

    **Preview** shows the first 10 rows of your file. It only renders the seven sample columns, so
    an `email` column is read on import but won't show up here.

    It is still your chance to catch a shifted column — if a name is sitting in the phone column
    here, it will import that way too.
  </Step>

  <Step title="Import">
    Submit the file. You land back on **Contacts** with a count of how many contacts were imported.
  </Step>
</Steps>

## The columns

The header row is what matters — column order follows your header, and capitalisation and
surrounding spaces are ignored. Anything not in this list is ignored.

| Column          | What it does                                                                                 |
| --------------- | -------------------------------------------------------------------------------------------- |
| `name`          | Required. A blank name skips the row.                                                        |
| `country_code`  | Joined onto the front of `phone` to make the full number.                                    |
| `phone`         | Required. A blank phone skips the row.                                                       |
| `email`         | Saved on the contact, and used for matching. Not in the sample file, but it works.           |
| `tags`          | One or more tag names, separated by `\|` or `,`. Tags that don't exist yet are created.      |
| `stage`         | A [pipeline stage](/contacts/pipeline-stages), matched by its exact name.                    |
| `notes`         | Saved as a note on the contact.                                                              |
| `custom_fields` | A JSON object of [custom fields](/contacts/custom-fields), for example `{"company":"Acme"}`. |

The sample file looks like this:

```csv theme={"dark"}
name,country_code,phone,tags,stage,notes,custom_fields
Jane Doe,1,5550100,VIP|Buyer,Lead,Met at expo,{"company":"Acme"}
John Smith,44,7700900000,Lead,Subscriber,,
```

<Note>
  Don't trust the sample's second row as a model. It puts `Subscriber` in the **stage** column, but
  the stages a new Workspace starts with are **Lead**, **Engaged** and **Customer** — so that stage
  matches nothing and John Smith arrives with no stage at all. Use your own stage names, exactly as
  they appear on the Pipeline.
</Note>

<Warning>
  Quote any value that contains a comma. An unquoted comma shifts every value after it one column
  to the right and pushes the last columns off the end of the row. With the sample header, a note
  written as `Met at expo, wants a quote` imports `Met at expo` as the note, ` wants a quote` into
  **custom\_fields** where it fails to parse, and drops `{"company":"Acme"}` entirely. Write it as
  `"Met at expo, wants a quote"`.
</Warning>

## What happens to people you already have

Before creating anything, the importer looks for an existing contact anywhere in your Workspace
with the **same phone number** or the **same email address**. If it finds exactly one:

* No duplicate is created.
* The import channel is added to that person as another [channel identity](/contacts/overview).
* **Name**, **email**, **phone** and **stage** are only filled in where they are empty. If the
  contact has a name and your CSV has a different one, the existing name stays.
* Tags are **replaced** with whatever the `tags` column says, not added to.
* Custom fields are merged into what's already there, and the CSV value wins on any key it sets.
  This is the one field the importer overwrites.
* A `notes` value is added as a new note, on every run.

Rows that match nobody create a new contact, recorded with a source of **CSV import**.

So a re-import doesn't duplicate your list, but it isn't read-only either. It tops up blank names,
emails, phones and stages, and leaves everything else alone — while replacing tags, overwriting the
custom fields your CSV sets, and appending the note again.

<Warning>
  **A blank or missing `tags` column strips tags.** Tags are replaced, so a re-import whose `tags`
  column is empty — or that has no `tags` column at all — removes every existing tag from every
  contact it matches. If you are re-importing to update something else, carry your current tags in
  the file.
</Warning>

<Tip>
  **Export** on the Contacts page writes the same seven columns the importer reads, so you can
  export, add a column of tags in a spreadsheet, and import it back to apply them.
</Tip>

## When rows don't arrive

The importer reports how many contacts it imported, and nothing about what it skipped. If that
number is lower than your row count, work down this list.

<Accordion title="Fewer contacts imported than rows in the file">
  Almost always a blank **name** or a blank phone — both skip the row without a message. Sort your
  spreadsheet by each of those columns and check for empties before re-uploading.
</Accordion>

<Accordion title="The import was blocked and nothing was imported">
  Your plan has a cap on how many contacts a Workspace can hold. The whole file is checked before a
  single row is written, so an import that would take you over the cap imports nothing at all —
  there is no partial result to clean up.

  The check is deliberately cautious: rows that would only have matched an existing contact still
  count toward the estimate, so a large re-import of people you already have can be blocked even
  though it wouldn't add anyone. See [Plans](/billing/plans).
</Accordion>

<Accordion title="Contacts imported with no stage">
  The `stage` value must match a stage name on your Pipeline exactly. Anything else is ignored
  quietly. Open [Pipeline stages](/contacts/pipeline-stages) and copy the names from there.
</Accordion>

<Accordion title="Phone numbers look wrong">
  The number stored is `country_code` and `phone` glued together with nothing in between. If your
  `phone` column already includes the country code, leave `country_code` empty — otherwise you get
  it twice.
</Accordion>

<Accordion title="Everything imported, but you can't message them">
  Imported contacts are attached to the channel that was selected at import time. Whether you can
  actually reach someone on WhatsApp depends on your connected number and WhatsApp's messaging
  rules, not on the import. See [WhatsApp limits](/channels/whatsapp-limits).
</Accordion>

## Next

<Columns cols={2}>
  <Card title="Tags and segments" icon="tags" href="/contacts/tags-and-segments">
    Group the contacts you just imported into a reusable audience.
  </Card>

  <Card title="Send a broadcast" icon="bullhorn" href="/broadcasts/overview">
    Message a segment on WhatsApp.
  </Card>

  <Card title="Custom fields" icon="list-check" href="/contacts/custom-fields">
    Set up the fields your `custom_fields` column fills in.
  </Card>

  <Card title="Contacts overview" icon="address-book" href="/contacts/overview">
    How one person can span several channels.
  </Card>
</Columns>
