This recipe describes Constant Contact’s data model based on general public documentation. Like all third-party platforms, Constant Contact’s API evolves — confirm against their current documentation before using specific field names. The Virtuous-side mapping is the stable part.
How Constant Contact differs from Mailchimp
Three architectural differences affect the integration pattern:
The most impactful difference is webhook coverage. Where the Mailchimp recipe is fully event-driven, the Constant Contact recipe typically blends webhooks (for the events available) with periodic polling (for the events not delivered via webhook). The result is a hybrid: event-driven where possible, polled where necessary.
Architecture
The hybrid pattern: webhooks handle real-time events where available, and a polling worker fills in the gaps by querying Constant Contact’s API for changes since the last poll.Field mapping
Constant Contact Contact → Virtuous Contact
Constant Contact Lists → Virtuous Tags
The recommended mapping:
As with Mailchimp, confirm the mapping with the customer’s marketing team. Some customers prefer to map Constant Contact lists to Virtuous custom field values (e.g., a “Subscriber Status” custom field with options “Newsletter,” “Donor Updates”) rather than tags.
Step 1: receive webhook events (where available)
The webhook handler for Constant Contact events that are delivered via webhook:JavaScript
Step 2: poll for changes not delivered via webhook
For events not in your customer’s Constant Contact webhook configuration, run a polling worker that queries the Constant Contact API for changes since the last poll:JavaScript
Step 3: submit subscriber events to Virtuous
The submitter worker is largely the same as the Mailchimp version — drain the queue and submit Contact Transactions:JavaScript
Handling unsubscribes
When a subscriber opts out of all Constant Contact emails (opt_out state in CC, or the contact’s email status changes to unsubscribed), update the Virtuous Contact’s email ContactMethod with isOptedIn: false. The implementation matches the Mailchimp recipe’s unsubscribe handler.
Handling bounces
Constant Contact’s bounce categorization (hard bounce, soft bounce, abuse complaint) maps to the same Virtuous fields used for Mailchimpcleaned events: set isOptedIn: false and surface a ContactNote with the reason.
Step 4: propagate Virtuous changes back to Constant Contact
The reverse direction mirrors the Mailchimp pattern:JavaScript
Bulk import on initial connection
When a customer first connects Constant Contact to Virtuous, the import pattern is the same as for Mailchimp: page through Constant Contact’s full contact list, treat each as a synthetic event, and let the submitter drain the queue.JavaScript
Common edge cases
A subscriber exists in multiple lists
Constant Contact subscribers can belong to multiple Lists. Each List membership maps to a separate Virtuous Tag with the"CC:" prefix. A subscriber in three lists has three CC-prefixed tags on their Virtuous Contact.
A subscriber changes their email address
Constant Contact identifies subscribers by ID, not by email — an email change is a profile update, not a new subscriber. Find the existing Virtuous Contact byreferenceId and update the email ContactMethod’s value.
A Contact is deleted in Constant Contact
If Constant Contact webhooks includecontact.deleted events (or the polling worker detects a contact that disappeared), the recommended Virtuous-side action is not to delete the Contact — donations and other history attached to the Contact are still valuable. Instead, set isOptedIn: false on the email and add a ContactNote explaining the deletion source.
Constant Contact tier doesn’t support webhooks
If the customer’s tier doesn’t include webhooks at all, drop the webhook receiver entirely and rely on polling alone. The integration becomes effectively a nightly (or hourly) sync — covered in Build a Nightly Data Sync.Production readiness checklist
- Constant Contact OAuth flow implemented for per-customer authentication.
- Webhook signature verification implemented per Constant Contact’s documentation.
- Polling worker fills in the gaps for events not delivered via webhook.
- The Constant Contact Contact ID is used as the Virtuous
referenceId. - Unsubscribe / opt-out / bounce events set
isOptedIn: falseon the email ContactMethod. - Constant Contact-derived tags use the
"CC:"prefix. - Sync-loop defense in place to prevent echoes from Virtuous changes back to Constant Contact.
- Multi-tenant isolation: per-customer tokens, per-customer queues, per-customer polling state.
- Polling cadence balances freshness against Constant Contact API quota.
- Bulk-import path throttles below the Virtuous 1,500/hour rate limit.
Where to go next
Mailchimp to Virtuous CRM
The fully event-driven companion recipe — same Virtuous-side patterns, different source platform.
Build a Nightly Data Sync
The pure-polling alternative when webhook support is unavailable.
Build a Two-Way Sync
The general two-way architecture this recipe instantiates.
Reconcile Failed Syncs
Reconciliation is especially important when webhook coverage is incomplete — polling gaps are a regular source of sync drift.