Import contacts from CSV

You can import CSV files with contact data to your audience. This option lets you add new contacts, update existing ones, and create segments based on imported contacts.

Typical use cases:

  • Import trade show leads collected at industry events with up-to-date contact preferences.
  • Update contact demographic information from your CRM system or third-party data providers.
  • Migrate contacts from legacy systems while maintaining consent compliance.

Only the columns that you map to contact attributes are imported to the audience. The Consent column is mapped separately within the Consent object.

File requirements

Your CSV file must meet these specifications:

  • Encoding: UTF-8
  • Format: CSV, comma-separated
  • Header row: Optional
  • Maximum size: 5 GB
  • Contact information: All contacts must be reachable by email or phone. Contacts with no contact information won't be imported. To learn more about the processing flow, see Prepare contact data for import.

Phone numbers

If a file contains phone numbers, they must be formatted according to the ISO E.164 standard: +[country code][area code][phone number].

No spaces, dashes, or special characters are allowed. If the + symbol is missing before a phone number, it will be added automatically in Connect.

Dates

If a file contains dates, they must be in one of the following formats:

  • YYYY-MM-DD
  • MM/DD/YYYY
  • DD/MM/YYYY
  • YYYY/MM/DD
  • DD.MM.YYYY
  • YYYY-MM-DDTHH:mm:ssZ

👍

Tip

Be careful when opening CSV files in Excel as it can automatically convert a date like 02/10/2025 to an unsupported format such as 2/10/25. To prevent this, use a text editing app such as Notepad or Visual Studio Code.

Approaches to consent management

When adding new contacts to the audience, it is important to set proper consent statuses that reflect people's willingness (or refusal) to receive communications from your company.

You can take one of the following approaches when importing contacts from a file.

Approach A: Apply the same status to all contacts

You can set a common consent status to all contacts using the status property. Here is an example for the Email channel.

{
  "consent": {
    "channels": {
      "channel": "EMAIL",
      "status": "OPT_IN_UNVERIFIED"
    }
  }
}

If your marketing team manages consent on the consent group basis, use the consentGroups property instead of channels. The common status will be effective within the consent group you specify.

{
  "consent": {
    "consentGroups": {
      "consentGroupId": "{CONSENT_GROUP_ID}",
      "status": "OPT_IN_UNVERIFIED"
    }
  }
}

To get the IDs of available consent groups, use the dataSets query. For more information about it, see Query an audience.

query {
  dataSets {
    nodes {
      consentGroups {
        name
        id
      }
    }
  }
}
{
  "data": {
    "dataSets": {
      "nodes": [
        {
          "consentGroups": [
            {
              "name": "Newsletters",
              "id": "{CONSENT_GROUP_ID_1}"
            },
            {
              "name": "Promotions",
              "id": "{CONSENT_GROUP_ID_2}"
            },
            {
              "name": "Information",
              "id": "{CONSENT_GROUP_ID_3}"
            },
            {
              "name": "Custom",
              "id": "{CONSENT_GROUP_ID_4}"
            }
          ]
        }
      ]
    }
  }
}

🚧

Warning

When submitting consent preferences, use either consent groups or channels in your mutation. Using both at the same time would result in an error.

Example: A retail company imports 50,000 contacts from a recent marketing campaign. Since all contacts filled out the same opt-in form, the team applies OPT_IN_UNVERIFIED status to everyone for the Email channel, streamlining the import process.

Approach B: Import statuses from a file

You can import a current consent status for each contact. If you choose to follow this approach, you must add the Consent column to the file and provide one of the following values for each contact: OPT_IN, OPT_IN_UNVERIFIED or OPT_OUT. Do not use null, N/A or any other values. If you leave the field blank for a contact, their status for the Email channel will be automatically set to OPT_IN.

Here is an example for the Email channel. It means that column 6 in the CSV file has consent statuses for the Email channel.

{
  "consent": {
    "channels": {
      "channel": "EMAIL",
      "columnIndex": 6
    }
  }
}

If your marketing team manages consent on the consent group basis, use the consentGroups property instead of channels. The following example means that column 6 in the CSV file has consent statuses for the specified consent group.

{
  "consent": {
    "consentGroups": {
      "consentGroupId": "{CONSENT_GROUP_ID}",
      "columnIndex": 6
    }
  }
}

Example: An e-commerce business exports contacts from their legacy CRM system, including historical consent preferences. Each contact has their own consent status (opted in, opted out, or unverified), which needs to be preserved during migration to maintain compliance and customer trust.

Approach C: Ignore consent settings

If you omit the consent property, all new contacts will be automatically opted in to the Email channel. The statuses of existing contacts will stay unchanged.

🚧

Warning

This approach should only be used when you have verified consent through other means and documented the legal basis for communication.

Initial setup

To start importing contacts, you need to establish access to our SFTP server.

  1. Run the following command in your terminal emulator. Feel free to replace "Acoustic Connect key" with another unique name.
ssh-keygen -t ed25519 -C "Acoustic Connect key" -f key
  1. Navigate to the directory from which you ran the command and find two new files there: a private key (key) and a public key (key.pub).

🚧

Warning

For security reasons, don't share the private key with anyone.

  1. Run the following mutation to register your public key in our API. Replace "YOUR_PUBLIC_KEY" with the content from key.pub.
mutation updatePublicKey {
  updatePublicKeyForConnectUser(
    publicKey: "YOUR_PUBLIC_KEY"
  ) {
    publicKey
  }
}
{
  "data": {
    "updatePublicKeyForConnectUser": {
      "publicKey": "YOUR_PUBLIC_KEY"
    }
  }
}
  1. Create a new connection in an FTP client such as FileZilla. The settings are described below.
Site Manager in FileZilla
  1. Navigate to the directory allocated for your subscription.
  2. (optional) Create a sub-directory for contacts.
  3. Upload your files.

📘

Note

When the import job is completed, the files will be deleted from the SFTP server.

Settings for the SFTP connection

Host

Use a host associated with your Connect domain.

Connect domainSFTP host
prod-us-east-1s-acca2da9ec374c47b.server.transfer.us-east-1.amazonaws.com
prod-eu-central-1s-0f495b8f35324396a.server.transfer.eu-central-1.amazonaws.com
prod-ap-southeast-2s-9596192dde684690a.server.transfer.ap-southeast-2.amazonaws.com

Other settings

  • Protocol: SFTP – SSH File Transfer Protocol
  • Port: 22
  • Logon type: key file
  • Key file: Specify a path to the private key file on your hard drive.
  • User: Your Acoustic Connect email address
    • Requirements: 3-100 characters, Latin letters, numbers 0-9, underscores (_), hyphens (-), periods (.)
    • Not allowed: plus (+), quotes ("), or other special characters

Mutation structure

Example

Let's say you have uploaded the following contact data to the SFTP server.

Contact KeyEmailFirst NameLast NamePhone NumberLast Order
ESX3845[email protected]KateReese+915555512342022-02-01T16:04:38Z
RUT8175[email protected]MaximilianTsiolkovsky+374555512342024-06-01T16:04:30Z
BBA0627[email protected]AmaliaAhmed+23555512342024-11-01T16:04:38Z
ESX4985[email protected]Antonio
BBN8971[email protected]JillianS.+375555512342025-11-01T16:04:38Z
PPO4467[email protected]ChrisJr.

Use the following mutation to import the file to Connect.

mutation ($importInput: ImportInput!) {
  createImportJob(importInput: $importInput) {
    id
  }
}
{
  "data": {
    "createImportJob": {
      "id": "{JOB_ID}"
    }
  }
}

The mutation requires a JSON object.

{
  "importInput": {
    "dataSetId": "{AUDIENCE_ID}",
    "jobName": "Import new contacts",
    "importType": "ADD_UPDATE",
    "createSegment": false,
    "fileLocation": {
      "type": "SFTP",
      "filename": "prospects-03-05-2025.csv",
      "folder": "contacts"
    },
    "consent": {
      "enableOverrideExistingOptOut": false,
      "channels": {
        "channel": "EMAIL",
        "status": "OPT_IN_UNVERIFIED"
      }
    },
    "skipFirstRow": true,
    "mappings": [
      {
        "columnIndex": 1,
        "attributeName": "Contact Key"
      },
      {
        "columnIndex": 2,
        "attributeName": "Email"
      },
      {
        "columnIndex": 3,
        "attributeName": "First Name"
      },
      {
        "columnIndex": 4,
        "attributeName": "Last Name"
      },
      {
        "columnIndex": 5,
        "attributeName": "Phone Number"
      },
      {
        "columnIndex": 6,
        "attributeName": "Last Order"
      }
    ]
  }
}

Mutation arguments

  • importInput (required): A JSON object with import settings.

Import input object

  • attributes: Object - Use this object if your file contains new audience attributes.
  • consent: Object - Consent settings for the contacts.
  • createSegment (required): Boolean - Lets you create a segment based on the imported contacts. The segment will be available as soon as the import job is complete.
  • dataSetId (required): String - The ID of your audience.
  • dateFormat: Enum - Add this property if your file contains dates and their format isn't YYYY-MM-DDTHH:mm:ssZ. Valid values: YEAR_MONTH_DAY_DASH_SEPARATED_WITH_TIME (default), YEAR_MONTH_DAY_DASH_SEPARATED, MONTH_DAY_YEAR_SLASH_SEPARATED, DAY_MONTH_YEAR_SLASH_SEPARATED, YEAR_MONTH_DAY_SLASH_SEPARATED, DAY_MONTH_YEAR_DOT_SEPARATED.
  • fileLocation (required): Object - Use this object to locate the file on the FTP server.
  • importType (required): Enum - The method of file processing. Valid value: ADD_UPDATE. The ADD_UPDATE method lets you add new contacts to the audience and update existing ones.
  • jobName (required): String - The name you want to assign to the import job.
  • mappings (required): Array of objects - Create a separate object for each imported attribute.
  • notifications: Array of objects - Create an object for each recipient you want to add.
  • segmentName: String - If you set createSegment to true, you must assign a name to the new segment.
  • skipFirstRow: Boolean - Set the value to true if the file has a header row (default: false).

Attributes object

  • create: Array of objects - Create new contact attributes during import. Each object in the array supports the following fields:
    • category: String - Assign an attribute to a category. Use the Query the categories of contact attributes query to get the list of available values.
    • decimalPrecision: Integer - The number of decimal places to display. Applies to numeric attributes.
    • identifyAs: Object - See Identification object.
    • name (required): String - The name of the new attribute.
    • type (required): Enum - The data type of the attribute. Valid values: TEXT, NUMBER, DATE, BOOLEAN.

Channels object

Used within the consent object to manage consent by communication channel.

  • channel (required): Enum - The type of communication channel. Valid values: EMAIL, SMS, WHATSAPP.
  • columnIndex: Integer - The number of the Consent column in your file. If you submit this property, do not use the status property in the same mutation.
  • status: Enum - Use this field to set a common consent status within the channel for all contacts in the import file. Valid values: OPT_IN, OPT_IN_UNVERIFIED, OPT_OUT. If you submit this property, do not map any consent-related columns to the audience using columnIndex.

Consent object

Used within the importInput object to define consent preferences.

  • channels: Object - Use this object to submit consent statuses for a particular media channel. channels cannot be used together with consentGroups in the same mutation.
  • consentGroups: Object - Use this object to submit consent statuses for a particular consent group. consentGroups cannot be used together with channels in the same mutation.
  • enableOverrideExistingOptOut: Boolean - Set the value to true to allow overwriting the OPT_OUT statuses of existing contacts who previously unsubscribed from receiving communications (default: false).

Consent groups object

Used within the consent object to manage consent by consent group.

  • columnIndex: Integer - The number of the Consent column in your file. If you submit this property, do not use the status property in the same mutation.
  • consentGroupId (required): String - The ID of a consent group in your audience.
  • status: Enum - Use this field to set a common consent status within the consent group for all contacts in the import file. Valid values: OPT_IN, OPT_IN_UNVERIFIED, OPT_OUT. If you submit this property, do not map any consent-related columns to the audience using columnIndex.

File location object

Used within the importInput object to specify where the CSV file is located.

  • filename (required): String - The name and extension of the file.
  • folder: String - The subfolder on the SFTP server where the file is located. If you have uploaded the file to the root folder, you don't need this field.
  • type (required): Enum - The method of file delivery. Valid value: SFTP.

Identification object

Used within the attributes object to creating new contact attributes.

  • channels: Enum or array of enums - If a text attribute is addressable, specify its communication channel. Valid values: EMAIL, SMS, WHATSAPP. A phone number can be associated with two channels (SMS and WhatsApp).
  • key: Boolean - Set to true if you are adding a key attribute to uniquely identify contacts in the audience.

Mappings object

Used within the importInput object to map CSV columns to audience attributes.

Both fields are required for each object in the array:

  • attributeName (required): String - The name of the audience attribute that the column from your CSV file will be mapped to. If your audience uses contact keys, you must map the key attribute to a file column name.
  • columnIndex (required): Integer - The index number of the column in the import file.

Notifications object

Used within the importInput object to configure job completion notifications.

  • channel (required): Enum - Add this property if you want to receive a notification when the import job is complete. Valid value: EMAIL.
  • destination (required): String - The email address for notification delivery.

Response fields

The mutation returns a JSON response containing:

  • data (required): Object - Root response object.
    • createImportJob (required): Object - Type of operation performed.
      • id (required): String - The ID assigned to the import job. Use this ID to check the current job status in Connect (Data management > Job monitoring). You will get a configuration summary and a report on how many records have been processed.

Example usage

Let's say your loyalty program categorizes customers as Gold, Silver, or Bronze based on their purchase history. When importing contacts from your CRM, you create the "Customer Tier" attribute and populate it with tier values. By creating a segment of all imported contacts, you can immediately launch a targeted campaign welcoming these loyalty members and offering tier-specific promotions.

{
  "importInput": {
    "dataSetId": "{AUDIENCE_ID}",
    "jobName": "Import contacts with new attribute",
    "importType": "ADD_UPDATE",
    "createSegment": true,
    "segmentName": "Imported contacts - Customer tiers",
    "fileLocation": {
      "type": "SFTP",
      "filename": "contacts-with-tiers.csv",
      "folder": "contacts"
    },
    "consent": {
      "enableOverrideExistingOptOut": false,
      "channels": {
        "channel": "EMAIL",
        "status": "OPT_IN_UNVERIFIED"
      }
    },
    "attributes": {
      "create": [
        {
          "name": "Customer Tier",
          "type": "TEXT",
          "category": "Demographic"
        }
      ]
    },
    "skipFirstRow": true,
    "mappings": [
      {
        "columnIndex": 1,
        "attributeName": "Contact Key"
      },
      {
        "columnIndex": 2,
        "attributeName": "Email"
      },
      {
        "columnIndex": 3,
        "attributeName": "First Name"
      },
      {
        "columnIndex": 4,
        "attributeName": "Last Name"
      },
      {
        "columnIndex": 5,
        "attributeName": "Phone Number"
      },
      {
        "columnIndex": 6,
        "attributeName": "Last Order"
      },
      {
        "columnIndex": 7,
        "attributeName": "Customer Tier"
      }
    ]
  }
}