> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wegive.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Communication Lists & Preferences

> Mapping between WeGive Communication Lists and Preferences to Salesforce custom objects

**Salesforce Objects:**

* `wegive__Communication_List__c` (custom object - Lists)
* `wegive__Communication_Preference__c` (custom object - Preferences/Subscriptions)

**WeGive Models:**

* CommunicationList
* CommunicationListDonor (pivot/junction table)

## Overview

This document describes how communication list and preference data syncs between WeGive and Salesforce:

* **Communication Lists** define the list itself (e.g., "Monthly Newsletter"). Lists are **pushed from WeGive to Salesforce only**.
* **Communication Preferences** track which donors are subscribed to which lists. Preferences sync **both ways**.

<Note>
  These features require the WeGive managed package to be installed in Salesforce, which includes the custom communication objects.
</Note>

***

# Part 1: Communication Lists

**Salesforce Object:** `wegive__Communication_List__c`<br />**WeGive Model:** CommunicationList

## Direction and Mapping

* **Export to Salesforce** only. There is no import of Communication Lists from Salesforce. Lists created or edited in Salesforce are not reflected in WeGive.
* All list fields are **Hard-coded**; there are no mapping rules for this object.

## Sync Configuration

* **Push communication lists** - Enables the export of WeGive communication lists

## Sync Triggers - Lists

A list is exported when it is created or updated in WeGive, and also automatically whenever a preference is pushed for a list that does not yet have a `salesforce_id`.

## Field Mappings - Lists

| Salesforce Field | WeGive Field | WeGive API Field | Direction | Type | Notes |
| :- | :- | :- | :- | :- | :- |
| Id | Salesforce ID | salesforce\_id | Import from Salesforce | Hard-coded | Stored on the WeGive list after the first push |
| Name | List Name | name | Export to Salesforce | Hard-coded | Truncated to 80 characters |
| wegive\_\_WeGive\_Id\_\_c | WeGive List ID | id | Export to Salesforce | Hard-coded | Omitted for shared objects in multi-entity orgs |
| wegive\_\_Description\_\_c | Description | description | Export to Salesforce | Hard-coded | HTML stripped, truncated to 255 characters |
| wegive\_\_UUID\_\_c | UUID | uuid | Export to Salesforce | Hard-coded | Stable external identifier |
| wegive\_\_Visible\_\_c | Visible | visible | Export to Salesforce | Hard-coded | Whether the list is publicly visible for opt-in |
| wegive\_\_Double\_Opt\_In\_\_c | Double Opt-In | double\_opt\_in | Export to Salesforce | Hard-coded | Whether double opt-in is required |
| wegive\_\_Show\_To\_\_c | Show To | show\_to | Export to Salesforce | Hard-coded | Audience setting for the list |
| wegive\_\_Created\_At\_\_c | Created At | created\_at | Export to Salesforce | Hard-coded | `Y-m-d` |
| wegive\_\_Updated\_At\_\_c | Updated At | updated\_at | Export to Salesforce | Hard-coded | `Y-m-d` |
| wegive\_\_Deleted\_At\_\_c | Deleted At | deleted\_at | Export to Salesforce | Hard-coded | `Y-m-d`; set when the list is deleted in WeGive |
| wegive\_\_Created\_DateTime\_\_c | Created At | created\_at | Export to Salesforce | Hard-coded | ISO-8601 |
| wegive\_\_Updated\_DateTime\_\_c | Updated At | updated\_at | Export to Salesforce | Hard-coded | ISO-8601 |
| wegive\_\_Deleted\_DateTime\_\_c | Deleted At | deleted\_at | Export to Salesforce | Hard-coded | ISO-8601 |
| wegive\_\_Last\_Sync\_Date\_\_c | Last Sync | (generated) | Export to Salesforce | Hard-coded | Timestamp of the push |

## Important Notes - Lists

* **Truncation:** Salesforce caps `Name` at 80 characters and the description field at 255; WeGive values are truncated on export. The full values remain in WeGive.
* **Deletion:** Deleting a list in WeGive does not delete the Salesforce record; the deleted timestamps are stamped instead.
* **Multi-entity orgs:** `wegive__WeGive_Entity__c` is stamped on the list when the communication list domain is entity-scoped.
* **Matching:** If the list has a `salesforce_id` it is **UPDATED**; otherwise a new record is **CREATED**. Lists are matched only by Salesforce ID.

***

# Part 2: Communication Preferences (Subscriptions)

**Salesforce Object:** `wegive__Communication_Preference__c`<br />**WeGive Model:** CommunicationListDonor

## Direction and Mapping

* **Both Ways.** WeGive pushes preferences and pulls changes made in Salesforce.
* All fields are **Hard-coded**; there are no mapping rules for this object.

## Sync Configuration

* **Push communication list donors** - Enables the export of subscriptions
* **Pull communication list donors** - Enables the scheduled import of subscriptions
* **`pull_deleted_communication_list_donors`** - When enabled, preferences deleted in Salesforce are removed in WeGive

## Sync Triggers - Preferences

### From WeGive to Salesforce (Export)

A preference is exported when a donor subscribes to a list in WeGive or a subscription changes. Before pushing, the integration ensures:

1. The donor has a `salesforce_id` (individual, Contact) or `salesforce_account_id` (company, Account); if not, the donor is pushed first
2. The list has a `salesforce_id`; if not, the list is pushed first

### From Salesforce to WeGive (Import)

WeGive periodically polls for preference records whose `LastModifiedDate` falls in the window since the last sync. When `pull_deleted_communication_list_donors` is enabled, records with `IsDeleted = true` are also queried and the matching WeGive subscriptions are deleted.

## Field Mappings - Preferences

| Salesforce Field | WeGive Field | WeGive API Field | Direction | Type | Notes |
| :- | :- | :- | :- | :- | :- |
| Id | Salesforce ID | salesforce\_id | Import from Salesforce | Hard-coded | Stored on the WeGive subscription |
| wegive\_\_WeGive\_Id\_\_c | WeGive Subscription ID | id | Both Ways | Hard-coded | Written on push; read on pull (as `wegive__WeGive_ID__c`) to match records |
| wegive\_\_Contact\_\_c | Donor Salesforce ID | donor.salesforce\_id | Both Ways | Hard-coded | Populated for individual donors; null for companies |
| wegive\_\_Account\_\_c | Donor Salesforce Account ID | donor.salesforce\_account\_id | Both Ways | Hard-coded | Populated for company donors; null for individuals |
| wegive\_\_Communication\_List\_\_c | List Salesforce ID | communication\_list.salesforce\_id | Both Ways | Hard-coded | Lookup to the list record |
| wegive\_\_Communication\_List\_Id\_\_c | WeGive List ID | communication\_list\_id | Export to Salesforce | Hard-coded | |
| wegive\_\_Donor\_Id\_\_c | WeGive Donor ID | donor\_id | Export to Salesforce | Hard-coded | |
| wegive\_\_Subscribed\_\_c | Subscribed | subscribed | Both Ways | Hard-coded | The primary subscription state |
| wegive\_\_Double\_Opt\_In\_\_c | Double Opt-In Required | double\_opt\_in\_required | Export to Salesforce | Hard-coded | Not read on import |
| wegive\_\_Created\_At\_\_c | Created At | created\_at | Export to Salesforce | Hard-coded | `Y-m-d` |
| wegive\_\_Updated\_At\_\_c | Updated At | updated\_at | Export to Salesforce | Hard-coded | `Y-m-d` |
| wegive\_\_Created\_DateTime\_\_c | Created At | created\_at | Export to Salesforce | Hard-coded | ISO-8601 |
| wegive\_\_Updated\_DateTime\_\_c | Updated At | updated\_at | Export to Salesforce | Hard-coded | ISO-8601 |
| wegive\_\_Deleted\_DateTime\_\_c | Deleted At | deleted\_at | Export to Salesforce | Hard-coded | ISO-8601 only — unlike Communication Lists, Preferences have no `wegive__Deleted_At__c` date-only counterpart in the push payload |
| wegive\_\_Last\_Sync\_Date\_\_c | Last Sync | (generated) | Export to Salesforce | Hard-coded | Timestamp of the push |

On import the integration reads `Id`, `wegive__Contact__c`, `wegive__Account__c`, `wegive__Communication_List__c`, `wegive__Subscribed__c`, and `wegive__WeGive_ID__c`, and writes only the list link, donor link, `salesforce_id`, and `subscribed` on the WeGive side.

## Important Notes - Preferences

### Contact or Account

Exactly one of `wegive__Contact__c` or `wegive__Account__c` is populated, chosen by donor type. On import, a `wegive__Contact__c` value is matched against individual donors' `salesforce_id`; a `wegive__Account__c` value is matched against company donors' `salesforce_account_id`.

### Subscription Status

`wegive__Subscribed__c` is the field that syncs in both directions. Changing it in either system updates the other on the next sync.

### Double Opt-In

`wegive__Double_Opt_In__c` records whether the subscription required confirmation. It is export-only; editing it in Salesforce has no effect in WeGive.

### Deletion Handling

* **WeGive to Salesforce:** the Salesforce record is not deleted; `wegive__Deleted_DateTime__c` is stamped.
* **Salesforce to WeGive:** with `pull_deleted_communication_list_donors` enabled, deleted preference records cause the matching WeGive subscription to be deleted.

### Multi-Entity Orgs

Pulled preferences are scoped by the parent list's `wegive__WeGive_Entity__c` when the communication list domain is entity-scoped.

***

## Communication Preference Matching & Create/Update Logic

### When WeGive Exports a Preference to Salesforce

1. Ensure the donor and list exist in Salesforce (pushing them if needed)
2. If the WeGive subscription has a `salesforce_id`: **UPDATE** the record
3. Otherwise: **CREATE** a new record and store its ID (a lock prevents concurrent duplicate creation)

### When Salesforce Exports a Preference to WeGive

1. **Find related records.** Resolve the donor from `wegive__Contact__c` or `wegive__Account__c`, and the list from `wegive__Communication_List__c`. If either is not found in WeGive, the record is skipped with an error.
2. **Find or create the subscription**, in order: by `salesforce_id`; by `wegive__WeGive_ID__c`; by donor + list; otherwise create new.
3. **Update** the list link, donor link, `salesforce_id`, and `subscribed`, then save.

***

## Integration with Contact Fields

Subscriptions can also be represented as fields on the Contact record for push purposes. This is driven entirely by Contact mapping rules:

* Each WeGive communication list has an API name of the form `CL_[list_id]_[list_slug]` (`CommunicationList::getApiNameAttribute()`).
* **On Contact export**, each list's API name is exposed to Contact mapping rules with the donor's subscribed value (true/false) — confirmed in `compileDonorPayload()`, which iterates `$donor->communicationLists` and sets `$wegiveData[$cl->api_name] = $cl->pivot->subscribed`. A field is written to Salesforce only if a Contact mapping rule maps that API name to a Salesforce field.

<Warning>
  **This mechanism is push-only, not bidirectional as previously described.** There is no code path anywhere in the Salesforce integration that reads a `CL_`-prefixed key back off an imported Contact record. The only custom-field import path, `fillCustomFieldsValues()` (`app/Traits/HasCustomFields.php`), matches keys against the regex `^CF_(\d+)(?:_.*)?$` — `CL_` (Communication List) keys never match, and any such value arriving via a Contact mapping rule is silently dropped, not converted into a subscription. If you need bidirectional subscription sync via Contact fields, request it from support/engineering rather than assuming the mapping-rule workflow already provides it — only the dedicated Preference object sync (Part 2 above) is genuinely bidirectional.
</Warning>

Without a Contact mapping rule referencing the list's API name, no Contact field is written on export. The preference object sync described above works independently of this mechanism and remains the only bidirectional path.

**Example:** List ID 123 with slug `monthly_newsletter` has API name `CL_123_monthly_newsletter`. A Contact mapping rule from `CL_123_monthly_newsletter` to a checkbox field on Contact keeps that checkbox in step with the subscription.

***

## Required Fields

**Communication Lists (WeGive to Salesforce):** Name

**Communication Preferences (WeGive to Salesforce):** `wegive__Contact__c` or `wegive__Account__c`, `wegive__Communication_List__c`, `wegive__Subscribed__c`

**Communication Preferences (Salesforce to WeGive):** Id, a Contact or Account that already exists in WeGive, a Communication List that already exists in WeGive, `wegive__Subscribed__c`

***

## Troubleshooting

**Communication list not syncing:**

* Verify the managed package is installed and the Push communication lists toggle is enabled
* Remember that lists are push-only; edits made in Salesforce do not flow back

**Preference not syncing:**

* Verify the donor has a Salesforce ID (Contact or Account) and the list has a `salesforce_id`
* On import, the donor and list must already exist in WeGive or the record is skipped

**Subscription status not updating:**

* Confirm `wegive__Subscribed__c` is the field being changed; other fields are export-only

**Deleted preferences reappearing in WeGive:**

* Enable `pull_deleted_communication_list_donors`

**Contact field showing the wrong subscription value:**

* Confirm a Contact mapping rule references the list's `CL_[id]_[slug]` API name
* Remember this direction is push-only — editing the mapped field in Salesforce never updates the WeGive subscription; only the dedicated Preference object sync is bidirectional

***

## Related Documentation

* [Contact](./contact)
* [Account](./account)

*Verified against the integration source, September 2026.*


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.