# Import people or events via CSV

## How it works[](#how-it-works)

You can upload CSVs or Google Sheets to add people, update people, and send events to Customer.io outside your normal integration path. When you upload a CSV of people or events, each row in your CSV represents a person or an event respectively; each column in your CSV represents an attribute or event property respectively.

When you go to upload a CSV, you’ll select **People** or **Events**. The option you choose determines the format of the CSV you upload.

*   **People**: You can add new people and update existing people. Each column in the sheet represents an [attributeA key-value pair that you associate with a person or an object—like a person’s name, the date they were created in your workspace, or a company’s billing date etc. Use attributes to target people and personalize messages.](/journeys/attributes/) you want to set on your audience.
*   **Events**: You can upload events for existing people, but you cannot add new people when you upload events. Each column in your sheet represents an event property.

To import relationships between people and [objectsAn object is a non-person entity that you can associate with one or more people—like a company, account, or online course.](/journeys/objects/), check out [Add/update objects via CSV](/journeys/import-objects/).

### Import a Google Sheet[](#google-import)

If you want to import Google Sheets, you must login to your Google account and allow us access to your sheets. You’ll see this includes the ability for us to read, edit, create, and delete the specific files that you share with us. However, **we will only ever read files**; we don’t write changes to your documents.

After you grant access, you can select the individual sheets that you want to share with Customer.io.

[![Authorize Customer.io to use your google sheets](https://docs.customer.io/images/google-oauth.png)](#e8ac43b3e79859c47aa49e011f038b3a-lightbox)

## Import people from a CSV file[](#import-people)

Before you import people, make sure that your CSV file is ready to import. It must contain a column that maps to `id` or `email`. If you want to import a Google sheet, you must [grant Customer.io access](#google-import) to sheets in your Google account.

1.  Go to *People*. Click **Add People** then scroll to the bottom of the modal and click **CSV** or **Google Sheets**.
    
    [![After clicking Add People in the top right of the People landing, a modal appears in the center of the screen. The modal contains a grid of options for adding people to your workspace. At the bottom of the modal, the options CSV and Google Sheets are highlighted in red.](https://docs.customer.io/images/people-modal-import.png)](#41ab39b2a60d3750fef9253604ead485-lightbox)
    
2.  Select **People**. See [import events from a CSV file](#import-events) if you want to upload events for people in your workspace.
    
3.  Select the file you want to import. If your CSV does not validate, it may not meet our [CSV requirements](#csv-requirements).
    
4.  Set up your import and then click **Next**.
    
    *   Set a *Name* and *Description* for your import, helping you identify your CSV on the **Imports** page.
    *   Select whether add new people or not.
    *   Select whether to update existing people or not. If you update existing people, determine whether to update people by `id`, `email`, or `cio_id`. Use `cio_id` if you want to update people’s `id` or `email` values.
    *   Determine how to handle empty values—ignore them or nullify existing attribute values.
    
     Looking for the *email* option?
    
    If you want to identify people by `email` but you don’t see that option, you can [enable email as an identifier](/journeys/workspaces/#migrate-workspace) in your workspace settings.
    
    [![CSV import settings](https://docs.customer.io/images/csv-import-settings.png)](#8afe591be4ad5446a8307ea3f5ac2277-lightbox)
    
5.  Map fields from your CSV to attributes in Customer.io and click **Next**.
    
    You must map `id` or `email` attributes to a column if your sheet did not include columns labeled `id` or `email`; all other columns are optional.
    
     Creating new attributes
    
    If you import a column, but you don’t map it to an existing attribute, we create a new attribute using the column title.
    
    [![map_fields.png](https://docs.customer.io/images/map_fields.png)](#b57bd11bcd55698d290d0571eca4e1d4-lightbox)
    
6.  Review your import for errors and warnings.
    
    *   Check out our section about [import for errors and warnings](#review) for help reviewing your CSV.
    *   (Optional) Click **Preview Import** to download a CSV file that reflects your final import, including all data mappings, skipped attributes, etc. Make sure that your import is correct. You cannot stop the import process after you click **Import**.
    *   (Optional) Add people to a new or existing [manual segmentA segment of people you maintain manually. You must explicitly add people to, or remove people from, the segment.](/journeys/manual-segments/).
    
     Each row in your CSV can trigger a campaign
    
    Customer.io processes imports row-by-row. Segment-triggered campaigns may fire as we create new people or change attributes, so review your import carefully! Learn about when [backfilled people data can trigger campaigns](/journeys/importing-old-data/#backfill-data-campaigns).
    
7.  Click **Complete import** to begin importing people. The import process takes approximately one minute per 20-30 thousand rows. You can navigate away from the page, and we will send you an email when your import is complete.
    

Under **Configure data**, click **More** > **Imports** to revisit this import or see your previous imports. On the table, you’ll see how many rows were imported. Hover over the count to check how many people were updated or created.

### Map CSV headers to attributes[](#map-attributes)

When you import a CSV, you match the headers in your CSV to attributes for people in Customer.io, at least one of which represents the identifier for the people you want to update (ID or email). If your column names match attributes in Customer.io, we try to map them automatically. Otherwise, you can re-map columns in your attribute to existing attributes or create new attributes.

We sample the first three rows of your data for each column, helping you understand the data that you’re mapping to each attribute. Use *Import as:* to select the attribute that you want to map a column to. If you do not want to import the values in a column, select *Skip*.

[![map_fields.png](https://docs.customer.io/images/map_fields.png)](#b57bd11bcd55698d290d0571eca4e1d4-lightbox)

Customer.io has some [reserved attributes](/journeys/attributes/#reserved-attributes): `id`, `email`, `created_at`, and `unsubscribed`. These attributes have a defined purpose in Customer.io and expect certain types of values.

You can create your own attributes for similar, non-reserved purposes. For example, if you want to import a column representing an email address, but you don’t want to map that to the reserved Customer.io `email` attribute, you can change the column name and import it as a different attribute. You cannot have multiple columns called `email`, nor can an email cell contain multiple values.

 Mapping warnings

You may see some warnings when mapping attributes. These generally represent best practices and recommendations, and are not issues that you need to fix. For example, we may recommend that you remove spaces from your column names because spaces in attributes can make things difficult when using Liquid in your messages.

### People CSV Requirements[](#csv-requirements)

You can upload a CSV directly to Customer. io, or link us to a Google Sheet. [Take a look at an example CSV](https://docs.google.com/spreadsheets/d/1zVgwD_9nYC4jXk9LMUaJJFXlTtKIEQQBbO6Gt489sBo/edit#gid=0). Each row in your CSV represents a person you want to add to, or update in, your workspace, and each column represents an attribute that you want to assign to that person.

Your file must:

*   be in [CSV format](https://en.wikipedia.org/wiki/Comma-separated_values) OR a Google Sheet
*   not exceed 100 MB in size
*   not contain more than 100 columns
*   contain at least one column that maps to an identifierThe attributes you use to add, modify, and target people. Each unique identifier value represents an individual person in your workspace. in your workspace—`id`, `email`, or `cio_id` (when updating people). This requirement changes based on the identifiers used in your workspace and whether you want to add or update people. See the [section below](#identifiers) for more information.
*   To share **Google Sheets**, you must login to your google account and grant Customer.io access to your sheets.

#### Identifiers and Required Columns in your CSV[](#identifiers)

Your CSV generally needs to include at least one identifierThe attributes you use to add, modify, and target people. Each unique identifier value represents an individual person in your workspace.. However, the identifiers your sheet can include depend on whether you want to add or update people and your [workspace settings](/journeys/workspaces/#selecting-identifiers-for-people-your-workspace)—whether you identify people by `id`, `email`, or both.

For **Email and ID** workspaces, your CSV must include at least one of `id` or `email` but may include both.

*   When you **update** people: if a person does not already have an `id` or an `email` attribute, your CSV can update these values for that person. For example, if you add a person by `email` and then want to assign an `id` later, you can do that. If ID or email values are *not* empty, you cannot change them unless you [identify people by `cio_id`](#cio_id). Attempting to change identifiers without using `cio_id` results in a *Failed Attribute Change* error.

For **ID only (classic)** workspaces:

*   When you **add** people: your sheet must include an `id` column (or a column that you map to `id`).
*   When you **update** people: you can update people by `email` (without an `id` column). But, if multiple people in your workspace have the same `email` address, the row produces an error; in this case, we don’t know which person you want to update.

 Use `cio_id` to update people’s identifiers

If you want to update a person’s email or ID, you must identify them by `cio_id`—an identifier assigned by Customer.io. [See the section below](#cio_id) for more information.

Workspace type

Add

Update

Required in CSV

Notes

ID-only/Classic

✅

`id`

✅

`id` or `email`

`email` is not a unique identifier. If you update by email, and multiple people have the same email address, the row produces an error.

✅

✅

`id`

email or ID

✅

at least one of `id` or `email`

✅

at least one of `id` or `email`

Your request cannot change existing `id` or `email` values

✅

✅

at least one of `id` or `email`

Your request cannot change existing `id` or `email` values

 There are other reserved attributes

See our [Attributes](/journeys/attributes/#reserved-attributes) page for a description of attributes with specific uses in Customer.io.

#### Updating people identifiers (email or ID)[](#cio_id)

If you want to update a person’s email or ID, and those identifiersThe attributes you use to add, modify, and target people. Each unique identifier value represents an individual person in your workspace. are already set, your sheet must identify people by their [cio\_idAn identifier for a person that is automatically generated by Customer.io and cannot be changed. This identifier provides a complete, unbroken record of a person across changes to their other identifiers (id, email, etc).](/identifying-people/#cio_id). Otherwise, trying to change a person’s email or ID value (after it was already set) results in an *Failed Attribute Change* error. You can find people’s `cio_id` values by performing an export, or on the **People** page.

When you import people, select the option to *Update* people, and use `cio_id` as the identifier. Identifying people by their canonical, CIO ID lets you change their other identifiers.

You cannot add people when you identify people by `cio_id`; this value is reserved and assigned by Customer.io automatically when you add someone using an email or ID.

[![Update people's identifiers](https://docs.customer.io/images/csv-update-cio_id.png)](#e89582a9d3411fbd9403f9eb9f2a1f25-lightbox)

### Upload subscription preferences via CSV[](#subscription-preferences-csv)

If you use our [subscription center](/journeys/subscription-center/) feature, you can set or backfill subscription preferences when you upload people.

You can set some or all subscription topic preferences for people by importing a CSV in the [People tab](https://fly.customer.io/workspaces/last/journeys/people).

#### Set one or more topic preferences for a person[](#update-specific-prefs)

You can use this method to update any and all subscription preferences for people without overwriting preferences for topics not specified in the CSV.

Add each subscription center topic name you want to set as its own column header. Upon upload, you’ll map each header to the attribute `cio_subscription_preferences.topics.topic_<topic ID>` where the topic ID corresponds to the topic name. You can find this on your subscription center landing page or by retrieving subscription center topics in our App API.

[![An image of two tables. The table on the left has a header titled Product Updates. Under this are the boolean values being assigned to two users' preferences. At the bottom, the header is mapped to the attribute cio_subscription_preferences.topics.topic_1. The other table maps the header Marketing to topic 3.](https://docs.customer.io/images/subscription-center-csv-import-partial.png)](#4d73ca62d9b4dc205ba615676e141e6d-lightbox)

After you complete the import, you’ll see that only the topic preferences you specified in the CSV show changes on the person’s profile.

Before uploading, another option is to add a column header that already matches the JSON dot notation above. When you go to map fields, the correct attribute name automatically populates.

#### Set all topic preferences for a person[](#update-all-prefs)

You can use this method to update ALL topic preferences per person, not a selection of topic preferences.

 Include every topic and value per person

If you do not include all topic preferences for each person using this method, the person’s preferences that are not specified in the import will be overwritten to match the default opt-in/out status of the topic.

You can import subscription preferences for a person by adding a column header `cio_subscription_preferences` and including the same JSON structure that our API expects. The contents of this column are `topics` objects, as follows:

```fallback
email,first_name,cio_subscription_preferences
person@example.com,person,{"topics":{"topic_1":true,"topic_2":false}}
another.person@example.com,another,{"topics":{"topic_1":true,"topic_2":true}}
```

See this [spreadsheet](https://docs.google.com/spreadsheets/d/1zVgwD_9nYC4jXk9LMUaJJFXlTtKIEQQBbO6Gt489sBo/edit?usp=sharing) for an example.

### Upload people via the API[](#api-import-people)

You can upload a CSV of people via our [App API](/integrations/api/app/#operation/import) by providing the URL of your CSV file. CSVs that you import this way conform to the [same rules](#csv-requirements) as a CSV that you upload via the UI. The main difference is that we’ll automatically map columns to [attributesA key-value pair that you associate with a person or an object—like a person’s name, the date they were created in your workspace, or a company’s billing date etc. Use attributes to target people and personalize messages.](/journeys/attributes/), so you should make sure that your CSV’s column names match attribute names in your workspace before you upload a CSV.

This means that your CSV **must** contain a column titled `id` or `email`—an identifierThe attributes you use to add, modify, and target people. Each unique identifier value represents an individual person in your workspace. we can use to add or update people.

We recommend that you host your CSV from a short-lived URLs. Ideally, your URLs will expire 2 hours after you initiate imports. We have to be able to reach your files, but you probably don’t want files containing your customers’ information to remain publicly available after you’ve uploaded them to us.

The response from a CSV upload contains a `result.id`. You can use this value with a [companion endpoint](/integrations/api/app/#operation/getImport) to return the results of your import—including whether the import is complete and how many rows we were able to import.

```shell
curl --request POST \
  --url https://api.customer.io/v1/imports \
  --header 'Authorization: Bearer REPLACE_BEARER_TOKEN' \
  --header 'content-type: application/json' \
  --data-raw
   '{
      "import": {
        "name":"my upload",
        "data_file_url":"https://www.example.com/myfile.csv",
        "type":"people",
        "identifier":"id",
        "data_to_process": "all",
        "description":"uploading people"
      }
    }'
```

*   data\_file\_url string
    
    Required The URL or path to the CSV file you want to import.
    
*   type string
    
    Required The type of import.
    
    Accepted values:`people`
    

*   data\_file\_url string
    
    Required The URL or path to the CSV file you want to import.
    
*   type string
    
    Required The type of import.
    
    Accepted values:`event`
    

*   data\_file\_url string
    
    Required The URL or path to the CSV file you want to import.
    
*   type string
    
    Required The type of import.
    
    Accepted values:`relationship`
    

*   data\_file\_url string
    
    Required The URL or path to the CSV file you want to import.
    
*   type string
    
    Required The type of import.
    
    Accepted values:`object`
    

## Import events from a CSV[](#import-events)

When you upload a list of people, you can upload events rather than attributes! You might do this if you need to backfill events that your audience performed outside of your normal integration path—things your audience did before you integrate with Customer.io.

Before you import people, make sure that your CSV file is ready to import. The first column must be `_cio_name` and contain the event name; the second column must be `_cio_customer_id`. See [Event CSV requirements](#event-csv-requirements) for more information.

1.  Under **Configure data**, click **More** > **Imports** and click **Import**.
    
    [![A screenshot of the Imports page. An arrow points to the Import button in the top right.](https://docs.customer.io/images/data-import.png)](#e26609c02707a630bfce5b489205d861-lightbox)
    
2.  Select **Events**. See [import people from a CSV file](#import-people) if you want to add or update people.
    
3.  Select the file you want to import. If your CSV does not validate, it may not meet our [CSV requirements](#event-csv-requirements).
    
4.  Set up your import and then click **Next**.
    
    *   Set a *Name* and *Description* for your import, helping you identify your CSV on the **Imports** page.
    *   Select the type of identifier in your CSV’s `_cio_customer_id` field: `id` or `email`.
    
    [![Settings when importing events via csv](https://docs.customer.io/images/import-events-setup.png)](#3db76f0ca4d4a28d95554e633a0c1a63-lightbox)
    
5.  In the **Preview** step, check that event properties are mapped properly. Click any row in the preview to see the JSON representation of your event.
    
    [![See the JSON representation of your event](https://docs.customer.io/images/import-events-preview.png)](#210bd5bdccb15eee94746f45b87b4c49-lightbox)
    
6.  Review your import.
    
    *   Check out our section about [import for errors and warnings](#review) for help reviewing your CSV.
    *   (Optional) Click **Preview Import** to download a CSV file that reflects your final import, including all data mappings, skipped attributes, etc. Make sure that your import is correct. You cannot stop the import process after you click **Import**.
    
     Events can trigger campaigns!
    
    If your sheet doesn’t contain a `_cio_timestamp` field, or your timestamps are within the past 72 hours, your events can trigger campaigns. Make sure that you understand the impact of your events before you finish your upload. Learn about when [backfilled event data can trigger campaigns](/journeys/importing-old-data/#backfill-data-campaigns).
    
7.  Click **Complete import** to import your events. The import process takes approximately one minute per 20-30 thousand rows. You can navigate away from the page. We’ll send you an email when your import is complete.
    

Under **Configure data**, click **More** > **Imports** to revisit this import or see your previous imports. On the table, you’ll see how many rows were imported. Hover over the count to check how many events were added and how many people were updated or created.

### Import events via the API[](#import-events-via-the-api)

You can upload a CSV of events via our [App API](/integrations/api/track/#operation/import) by providing the URL of your CSV file. CSVs that you import this way conform to the [same rules](#csv-requirements) as a CSV that you upload via the UI. The main difference is that we’ll automatically map columns to event `data` properties, so you should make sure that your CSV column names match event properties that you might use in [segmentsA group of people who match a series of conditions. People enter and exit the segment automatically when they match or stop matching conditions.](/journeys/data-driven-segments/) and other assets.

Your CSV **must** contain a column titled `_cio_customer_id`—the `id` or `email` address of your audience, so we can identify who performed each event. You must use the same identifier for everybody in the sheet.

We recommend that you host your CSV from a short-lived URLs. Ideally, your URLs will expire 2 hours after you initiate imports. We have to be able to reach your files, but you probably don’t want files containing your customers’ information to remain publicly available after you’ve uploaded them to us.

The response from a CSV upload contains a `result.id`. You can use this value with a [companion endpoint](/integrations/api/app/#operation/getImport) to return the results of your import—including whether the import is complete and how many rows we were able to import.

```shell
curl --request POST \
  --url https://api.customer.io/v1/imports \
  --header 'Authorization: Bearer YOUR_APP_API_KEY' \
  --header 'content-type: application/json' \
  --data-raw \
   '{
        "import": {
            "name":"my event upload",
            "data_file_url":"https://www.example.com/events.csv",
            "type":"event",
            "identifier":"id",
            "description":"uploading people"
        }
    }'
```

*   data\_file\_url string
    
    Required The URL or path to the CSV file you want to import.
    
*   type string
    
    Required The type of import.
    
    Accepted values:`people`
    

*   data\_file\_url string
    
    Required The URL or path to the CSV file you want to import.
    
*   type string
    
    Required The type of import.
    
    Accepted values:`event`
    

*   data\_file\_url string
    
    Required The URL or path to the CSV file you want to import.
    
*   type string
    
    Required The type of import.
    
    Accepted values:`relationship`
    

*   data\_file\_url string
    
    Required The URL or path to the CSV file you want to import.
    
*   type string
    
    Required The type of import.
    
    Accepted values:`object`
    

### Event CSV requirements[](#event-csv-requirements)

The first two columns in your CSV must be `_cio_name`, `_cio_customer_id`. [Take a look at an example CSV.](https://docs.google.com/spreadsheets/d/1LTENBpzZkB926vwNc4z2WRct7H53CLGHavBf2pQEqiI/edit#gid=0) You can also include a third `_cio_timestamp` column representing the date-time when the event occurred if you want to back-date your event. If you don’t include this column, we’ll use time that we process the event as the event’s timestamp. Additional columns are event data attributes that you want to associate with the person.

Event data provided via CSV (columns 4 and greater) is flattened and cannot contain nested JSON. For example, a column called `property.subproperty` will simply become a stringified key called `"property.subproperty"`.

col

col name

required

description

1

`_cio_name`

The name of the event.

2

`_cio_customer_id`

The identifier for a person. Depending on your workspace settings, this can be the person's `id` or `email`. You must use the same identifier for the entire CSV—you cannot mix IDs and email addresses.

3

`_cio_timestamp`

The date-time when the event occurred.

4+

Subsequent columns contain additional attributes for the `data` object in the event; one column per event property. These are properties you'll use in [liquidA syntax that supports variables, letting you personalize messages for your audience. For example, if you want to reference a person’s first name, you might use the variable `{{customer.first_name}}`.](/using-liquid)—i.e. `{{event.column_name}}`. These columns can have any name, though you may want to make sure your column names don't contain spaces or special characters that might make them hard to use in Customer.io

## Review import errors and warnings[](#review)

[![image.png](https://docs.customer.io/images/image%2894%29.png)](#4419cf3c8cd5fe2f8a73b907f66a011b-lightbox)

On the final **Review** step, we validate your import and return errors and warnings for rows in your CSV file. Rows with *errors* **will not** be imported, but rows with *warnings* will. Depending on the size of your CSV file, it may take a moment for us to validate your import.

If there are no errors or warnings, you can continue importing your file as normal. If there are issues, you may want to correct your CSV file so you import all items.

*   **Errors** are issues that prevent us from importing a row:
    *   The row is missing a value in the “id” column.
    *   “object\_id” is empty.
    *   The specified Person does not exist.
    *   The specified Object does not exist.
*   **Warnings** are issues that do **not** prevent us from importing a row, but that you may want to address to make sure your data is well formed and consistent:
    *   Same “id” paired with multiple “email” values.
    *   Multiple rows have the same “email” value.

Click **Preview Import** to see which rows would successfully import.

You can also click **Export errors file** or **Export warnings file** to download a CSV containing the issues found. Each file contains the rows from your original CSV file that resulted in errors or warnings respectively, including 2 new columns:

*   \_row: contains the row number from your original file that contained an error or warning.
*   \_errors or \_warnings: lists errors/warnings for a row.

 Re-import your error CSV

You can import users directly from an error or warning CSV file after you correct the errors. Make sure you remove the “\_row”, “\_errors” and/or “\_warnings” columns.

## Export files from an import[](#export-files)

You can export files that you uploaded, or were otherwise generated as a part of the upload process (like errors and warnings), for up to 30 days after your import. After 30 days, these files expire and are no longer available to download.