Best practices for syncing contacts and lists between Constant Contact and a local data store.

Before You Start

It is very important for bulk email marketers to:

  • be aware of anti-spam laws - these require that you respect the wishes of individuals who have unsubscribed and no longer wish to receive email from an organization.
  • respect subscriber preferences – if an email marketer offers multiple email subscriptions, they must send subscribers only the content they have indicated they want to receive.

Providing sync functionality in your app

Subscriber preferences change over time, so it's important for apps integrating with Constant Contact to provide sync functionality so users can easily keep their local data in sync with Constant Contact. Constant Contact is the primary source for subscriber preferences with respect to any local data sources. Subscriber preferences in Constant Contact must not be overwritten with stale preferences held in a local data source when updating subscriber data.

Sync Contacts Data Flow

Syncing data implies that there are two data stores of the same or nearly the same information, with at least one of them being modified on a regular basis. Here is a simple example:

  • The primary data store – this is the source of record, it always has the latest and greatest, with data changes occur here live. Constant Contact is considered the primary data store in all examples.
  • The secondary data store – this store is always updated with any changes occurring in the primary data store.

Before updating a Constant Contact list with data from a local source, make sure you have sync’d the secondary source with the primary data source (Constant Contact). This way any email preference changes that subscribers have made in Contact Constant are captured locally. Otherwise, during a list import contacts who have unsubscribed recently from a list will be automatically resubscribed to that list after the import.

Here is a basic work flow to follow when syncing contact data.

Step 1 Get data from the primary and secondary data stores.
Step 2 Compare the data to identify differences between objects in each store.
Step 3 Update the compared objects in the secondary store with the object data from the primary store.
Step 4 Do a [list sync][#sync_lists] to move any new data from the secondary store to the primary (CTCT), adding new contacts to lists.

Performing data syncs on a regular cadence is an important element to consider and design into your integration. Sync contacts on a regular periodic schedule using `updated_after` query parameter.

Sync new or changed contacts

Use the updated_after=<date-time> query parameter to retrieve only new or changed contacts since the last data sync. Whenever a contact is created, or when any properties are changed on an existing contact, the updated_at property is modified. Including updated_after searches for contacts with an updated_at time stamp (ISO-8601 format) that is later than the updated_after value.

Sync Contacts

Use the updated_after query parameter to retrieve all contacts that were updated since the last sync activity.

  1. Get all new or updated contacts since last sync.

    GET /contacts?updated_after={date&time_last_sync}
  2. Do a diff - compare the Constant Contact data with the local source.

  3. Merge the new and updated contacts in the secondary data store.

    If your integration syncs any contact data originating in the local application data with the CTCT:

  4. Merge any data originating in the secondary data source into the primary (CTCT) as needed. Use one of the two Bulk Activity Contact Import endpoints to bring the unified data back into Constant Contact.

Example GET /Contacts call using updated_after Parameter

Retrieves all contacts that have been created or modified after December 01, 2017. Total number of contacts returned is included at the bottom of the first page of the response.

GET https://api.cc.email/v3/contacts?updated_after=2017-12-01&include_count=true

<?php

$request = new HttpRequest();
$request->setUrl('https://api.cc.email/v3/contacts');
$request->setMethod(HTTP_METH_GET);

$request->setQueryData(array(
  'updated_after' => '2017-12-01',
  'include_count' => 'true'
));

$request->setHeaders(array(
  'Cache-Control' => 'no-cache',
  'Authorization' => 'Bearer {access_token}',
  'Content-Type' => 'application/json',
  'Accept' => 'application/json'
));

try {
  $response = $request->send();

  echo $response->getBody();
} catch (HttpException $ex) {
  echo $ex;
}
curl -X GET \
  'https://api.cc.email/v3/contacts?updated_after=2017-12-01&include_count=true' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access_token}' \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/json' \
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
Request request = new Request.Builder()
  .url("https://api.cc.email/v3/contacts?updated_after=2017-12-01&include_count=true")
  .get()
  .addHeader("Accept", "application/json")
  .addHeader("Content-Type", "application/json")
  .addHeader("Authorization", "Bearer {access_token}")
  .addHeader("Cache-Control", "no-cache")
  .build();

Response response = client.newCall(request).execute();

Response

{
    "contacts": [
      {
        "contact_id": "{contact_id}",
        "email_address": {
            "address": "example@example.com",
            "permission_to_send": "unsubscribed",
            "created_at": "2018-02-16T15:49:30-05:00",
            "updated_at": "2018-02-16T15:56:41-05:00",
            "opt_out_source": "Contact",
            "opt_out_date": "2018-02-16T15:56:41-05:00",
            "opt_out_reason": "No longer interested",
            "confirm_status": "off"
        }
        "first_name": "John",
        "last_name": "Byrd",
        "update_source": "Contact",
        "create_source": "Account",
        "created_at": "2013-11-26T15:45:52-05:00",
        "updated_at": "2018-02-16T15:56:41-05:00"
      },
      {
        "contact_id": "{contact_id}",
        "email_address": {
            "address": "example@example.com",
            "permission_to_send": "implicit",
            "created_at": "2013-04-01T15:07:07-04:00",
            "updated_at": "2018-02-20T16:47:40-05:00",
            "opt_in_source": "Account",
            "opt_in_date": "2015-03-17T11:13:28-04:00",
            "confirm_status": "off"
        },
        "first_name": "John",
        "last_name": "Johnson",
        "job_title": "Purchasing agent",
        "company_name": "Caterers to the Stars",
        "update_source": "Account",
        "create_source": "Contact",
        "created_at": "2013-04-01T15:07:07-04:00",
        "updated_at": "2018-02-20T16:47:40-05:00"
      },      
    ],
    "contacts_count": 2
}

Permission to Send

The permission_to_send property is set internally when importing new contacts or updating existing contacts using the bulk activity contact import endpoints:

  • New contacts - permission_to_send=implicit
  • Existing contacts - permission_to_send is unchanged, retaining the value set in the CTCT data store.

Syncing Lists

If you maintain subscriber lists in both the local application and Constant Contact, it’s important to keep the list memberships in sync. This will help to avoid sending communications to contacts who no longer wish to receive it, or who did not sign up for a list.

  1. Retrieve all contacts in a list from Constant Contact:

    GET /contacts?lists={list_id}
  2. Compare the local list membership data with the CTCT list membership.
  3. Update the secondary store with the new and changed data in the primary store.
  4. Import the updated list membership using one of the two Bulk Activity Contact Import endpoints.

    Be sure to specify the list you are working with in the list_ids property. If you don’t, the contacts may not be added to the correct list.

Example JSON Bulk Import Request Payload

This example of a partial JSON import contacts request payload shows a contact that has only a new or changed anniversary date, along with the list_id that it is being sync’d to.

{
  "import_data": [
    {
      "email": "joe.jones@example.com",
      "anniversary": "2006-11-15"
    }
  ],
  "list_ids": [
    {list_id}
  ]
}

Using the Import/Update Contacts bulk activity endpoints, you only need to include the contact’s email address and any properties that have changed for each contact. If you make a PUT /Contact/{contact_id} call, you will need to include all of the unchanged properties for the contact as well.

Sync Unsubscribed Contacts

Some integrations may track unsubscribed contacts in the local application data store. Use this process to maintain synchronicity with the CTCT primary data. Use the updated_after and status query parameters retrieve all contacts that were unsubscribed since the last sync.

  1. Get all contacts who have unsubscribed since last sync.

    GET /contacts?updated_after={date&time_last_sync}&status=unsubscribed
  2. Do a diff - compare the Constant Contact data with the local source.

  3. Merge the new unsubscribed contacts into the secondary data store.