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 synchronized 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 using the object data from the primary store.
Step 4 Do a list sync to move any new data from the secondary store to the primary (Constant Contact), 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 updated contacts since the last data sync. Constant Contact sets a updated_at timestamp when you create a new contact or modify a contact an existing contact. The updated_after query parameter returns contacts with a updated_at value that is later then the specified date.

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.
  2. GET /contacts?updated_after={date&time_last_sync}

    Endpoint Requirements

    User privileges: contacts:read Authorization scopes: contact_data

  3. Do a diff - compare the Constant Contact data with the local source.
  4. Merge the new and updated contacts in the secondary data store.
  5. If your integration syncs any contact data originating in the local application data with Constant Contact:

  6. Merge any data originating in the secondary data source into the primary data source (Constant Contact) as needed. Use one of the two [Bulk Activity Contact Import][import_contacts] 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

Endpoint Requirements

User privileges: contacts:read

Authorization scopes: contact_data

<?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 Constant Contact data store.

Sync Lists

If you maintain subscriber lists in a local application as well as 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:
  2. GET /contacts?lists={list_id}

    Endpoint Requirements

    User privileges: contacts:read Authorization scopes: contact_data

  3. Compare the local list membership data with the Constant Contact list membership.
  4. Update the secondary store with the new and changed data in the primary store.
  5. Import the updated list membership using the list_ids property of the Import Contacts using JSON endpoint or the Import Contacts from CSV endpoint.

Example JSON Bulk Import Request Payload

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

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

When you use the Import/Update Contacts bulk activity endpoints, you only need to include the contact’s email address and the properties that have changed for each contact. The update contacts endpoint (PUT /Contact/{contact_id} requires that you 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 Constant Contact 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:
  2. GET /contacts?updated_after={date&time_last_sync}&status=unsubscribed

    Endpoint Requirements

    User privileges: contacts:read Authorization scopes: contact_data

  3. Do a diff - compare the Constant Contact data with the local source.
  4. Merge the new unsubscribed contacts into the secondary data store.