> ## 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.

# Contact

> Mapping between WeGive Donors and Salesforce Contacts

The Contact mapping between WeGive and Salesforce synchronizes individual donor information between the two platforms, from basic personal details to household relationships, across both systems.

**Salesforce Object:** Contact **WeGive Model:** Donor (type: individual)

## Sync Process Overview

### Contact-Level Synchronization

WeGive syncs individual donors at the Contact level in Salesforce. Each individual Donor in WeGive corresponds to one Contact. The Contact's `AccountId` is stored on the donor as `salesforce_account_id` and is used to link the donor to a WeGive Household when the Account is an NPSP Household Account.

### Pulling Data from Salesforce

Pulling Contacts is enabled by the **Pull donors** toggle under Sync Configuration. When pulling, WeGive queries Contacts whose pull column is within the sync window. The pull column defaults to `LastModifiedDate` and can be changed with the `pull_by` integration setting.

The query always selects `Id`, `AccountId`, and `CreatedDate`, plus every Salesforce field referenced by an import or both-ways mapping rule for the Contact object. Fields that are not referenced by a rule are not requested from Salesforce.

If the `contacts_with_emails_only` setting is enabled, the query adds `Email != null`, so Contacts with no standard Email value are never pulled, merged, or deleted through this integration.

For each Contact returned, WeGive:

1. Looks for an existing individual donor with a matching `salesforce_id`.
2. If none is found and the mapped data includes an `email_1` value, looks for an individual donor with the same `email_1` that has no `salesforce_id` yet, and links it.
3. Otherwise creates a new donor.
4. Applies the mapped standard fields, custom fields, and address blocks, then saves without triggering an outbound push.

### Pushing Data to Salesforce

Pushing Contacts is enabled by the **Push donors** toggle under Sync Configuration. When an individual donor is created or updated in WeGive, the integration compiles a payload for the Salesforce Contact object from the hard-coded defaults listed below plus every export or both-ways mapping rule for the Contact object, then inserts or updates the Contact. See [Contact Matching and Create/Update Logic](#contact-matching-and-createupdate-logic) for how the target Contact is chosen.

Donors flagged as marketing-only contacts and donors excluded by an integration ignore rule are not pushed.

## How Contact Data Syncs

**Direction:**

* **Import from Salesforce** - Data imports from Salesforce into WeGive only
* **Export to Salesforce** - Data exports from WeGive to Salesforce only
* **Both Ways** - Data syncs in both directions

**Mapping Types:**

* **Configurable** - Provided by a mapping rule that can be customized in the integration settings
* **Configurable (mapping rule)** - Reaches Salesforce only if a mapping rule references it; there is no built-in default
* **Hard-coded** - Built into the integration logic and cannot be changed

## Standard Field Mappings

| Salesforce Field | WeGive Field | WeGive API Field | Direction | Type | Notes |
| :- | :- | :- | :- | :- | :- |
| Id | Salesforce ID | `salesforce_id` | Import from Salesforce | Hard-coded | Salesforce's unique identifier for this Contact. Written to the donor on import and after a successful insert. |
| AccountId | Salesforce Account ID | `salesforce_account_id` | Import from Salesforce | Hard-coded | Stored on import and read back after WeGive inserts a Contact. Not sent on export by default; see [AccountId handling](#accountid-handling). |
| CreatedDate | Created At | `created_at` | Import from Salesforce | Hard-coded | When the Contact was created in Salesforce |
| FirstName | First Name | `first_name` | Both Ways | Configurable import, Hard-coded export | Exported as stored. See [Name defaults](#name-defaults). |
| MiddleName | Middle Name | `middle_name` | Both Ways | Configurable | |
| LastName | Last Name | `last_name` | Both Ways | Configurable import, Hard-coded export | Exported as stored. See [Name defaults](#name-defaults). |
| Birthdate | Birthdate | `birthdate` | Both Ways | Configurable | |
| Gender\_\_c | Gender | `gender` | Both Ways | Configurable | |
| Email | Primary Email | Dynamic based on `preferred_email` | Export to Salesforce | Hard-coded | WeGive selects `email_1`, `email_2`, or `email_3` based on Preferred Email. Also used for matching; see [Contact Matching](#contact-matching-and-createupdate-logic). |
| npe01\_\_HomeEmail\_\_c | Email 1 | `email_1` | Both Ways | Configurable | |
| npe01\_\_WorkEmail\_\_c | Email 2 | `email_2` | Both Ways | Configurable | |
| npe01\_\_AlternateEmail\_\_c | Email 3 | `email_3` | Both Ways | Configurable | |
| npe01\_\_Preferred\_Email\_\_c | Preferred Email | `preferred_email` | Both Ways | Configurable | Controls which email is exported to the standard Email field. Defaults to `personal` on import when empty. |
| MobilePhone | Mobile Phone | `mobile_phone` | Both Ways | Configurable | |
| HomePhone | Home Phone | `home_phone` | Both Ways | Configurable | |
| OtherPhone | Office Phone | `office_phone` | Both Ways | Configurable | |
| Fax | Fax | `fax` | Both Ways | Configurable | |
| npe01\_\_PreferredPhone\_\_c | Preferred Phone | `preferred_phone` | Both Ways | Configurable | Defaults to `mobile` on import when empty. |
| MailingStreet | Mailing Address - Street | `mailing_address.address_1` | Both Ways | Configurable import, Hard-coded export | Address 1 and Address 2 are combined on export |
| MailingCity | Mailing Address - City | `mailing_address.city` | Both Ways | Configurable import, Hard-coded export | |
| MailingState | Mailing Address - State | `mailing_address.state` | Import from Salesforce | Configurable | Not exported by default. See [State and Country Picklists](#state-and-country-picklists). |
| MailingPostalCode | Mailing Address - Zip Code | `mailing_address.zip` | Both Ways | Configurable import, Hard-coded export | |
| MailingCountry | Mailing Address - Country | `mailing_address.country` | Import from Salesforce | Configurable | Not exported by default. See [State and Country Picklists](#state-and-country-picklists). |
| OtherStreet | Billing Address - Street | `billing_address.address_1` | Both Ways | Configurable import, Hard-coded export | Address 1 and Address 2 are combined on export |
| OtherCity | Billing Address - City | `billing_address.city` | Both Ways | Configurable import, Hard-coded export | |
| OtherState | Billing Address - State | `billing_address.state` | Import from Salesforce | Configurable | Not exported by default. See [State and Country Picklists](#state-and-country-picklists). |
| OtherPostalCode | Billing Address - Zip Code | `billing_address.zip` | Both Ways | Configurable import, Hard-coded export | |
| OtherCountry | Billing Address - Country | `billing_address.country` | Import from Salesforce | Configurable | Not exported by default. See [State and Country Picklists](#state-and-country-picklists). |
| npsp\_\_Do\_Not\_Contact\_\_c | Do Not Contact | `do_not_contact` | Both Ways | Configurable | Parsed as a boolean on import. |

### Address field pairing

Contact pairs the Salesforce **Mailing Address** (primary) with WeGive `mailing_address`, and the Salesforce **Other Address** (secondary) with WeGive `billing_address`. This differs from Account, which pairs Billing (primary) with `billing_address` and Shipping (secondary) with `mailing_address`.

On import, address values that come back empty are stored as empty strings. When a donor is created from Salesforce, both a mailing and a billing address row are always created, even if no address fields are mapped.

### Communication preference flags

On import, the integration recognizes four opt-out fields and converts them to booleans: `do_not_email`, `do_not_sms`, `do_not_mail`, and `do_not_contact`. Any of them can be populated from a Salesforce field through a mapping rule. Only `npsp__Do_Not_Contact__c` is listed above because it is the only one with a standard NPSP counterpart.

### Communication list subscriptions

A mapping rule whose WeGive target is named `CL_<list id>` (for example `CL_42`) is treated as a subscription flag for WeGive communication list 42. On import, a truthy value subscribes the donor and a falsy value unsubscribes them. On export, each communication list's `api_name` is available to mapping rules with the donor's subscribed status as its value.

## WeGive Package Fields

(Requires WeGive Salesforce managed package installation.)

| Salesforce Field | WeGive Field | WeGive API Field | Direction | Type | Notes |
| :- | :- | :- | :- | :- | :- |
| wegive\_\_WeGive\_Id\_\_c | WeGive Donor ID | `id` | Export to Salesforce | Hard-coded | Always included in the export payload. Dropped automatically if the org's package does not have the field, and dropped for multi-entity Salesforce orgs where Contact is shared across WeGive organizations. |
| WeGive\_Type\_\_c | Donor Type | `type` | Both Ways | Configurable | Donor type in WeGive (individual, company) |
| WeGive\_Status\_\_c | Donor Status | `status` | Both Ways | Configurable | Donor status in WeGive |
| WeGive\_Created\_Date\_\_c | WeGive Created Date | `created_at` | Both Ways | Configurable | When the donor was created in WeGive |
| WeGive\_Modified\_Date\_\_c | WeGive Modified Date | `updated_at` | Both Ways | Configurable | When the donor was last modified in WeGive |

If these fields are not visible in your Salesforce org, contact WeGive support about installing the WeGive Salesforce managed package.

## State and Country Picklists

By default, Contact state and country are **imported from Salesforce only**, not exported. The integration deliberately omits `MailingState`, `MailingCountry`, `OtherState`, and `OtherCountry` from the hard-coded export payload because writing free-text values to these fields fails in orgs that use Salesforce picklists. To sync state and country in both directions, add mapping rules:

* **Free-text orgs:** add a both-ways rule on the plain fields (`MailingState`, `MailingCountry`, `OtherState`, `OtherCountry`) mapped to the WeGive value fields (`mailing_address.state`, `mailing_address.country`, `billing_address.state`, `billing_address.country`).
* **Picklist orgs:** Salesforce State and Country/Territory Picklists is an org-wide setting. When enabled, state and country must be mapped through the code fields using a one-directional export/import split.

On export, each address block exposes the stored values plus four read-only derived fields to mapping rules: `state_code`, `state_full`, `country_code`, and `country_full`.

State and country mapping for picklist-enabled orgs (Contact):

| Salesforce Field | WeGive API Field | Direction | Type |
| :- | :- | :- | :- |
| MailingStateCode | `mailing_address.state_code` | Export to Salesforce | Configurable |
| MailingStateCode | `mailing_address.state` | Import from Salesforce | Configurable |
| MailingCountryCode | `mailing_address.country_code` | Export to Salesforce | Configurable |
| MailingCountryCode | `mailing_address.country` | Import from Salesforce | Configurable |
| OtherStateCode | `billing_address.state_code` | Export to Salesforce | Configurable |
| OtherStateCode | `billing_address.state` | Import from Salesforce | Configurable |
| OtherCountryCode | `billing_address.country_code` | Export to Salesforce | Configurable |
| OtherCountryCode | `billing_address.country` | Import from Salesforce | Configurable |

The split is required because the WeGive value field (`state`, `country`) is the writable column, while the code accessor (`state_code`, `country_code`) is read-only and derives the ISO code from the stored value. Exporting from the code accessor sends a valid ISO code that the restricted picklist accepts; importing into the value field lets the incoming code land in a writable column. A single both-ways rule cannot serve both directions.

For the customer-facing setup walkthrough (including the equivalent Account mapping and how to remove conflicting rules), see [Configuring Salesforce State and Country Picklist Mappings](https://wegive-help-center.help.usepylon.com/articles/3843908042-configuring-salesforce-state-and-country-picklist-mappings-in-wegive) in the Knowledge Base.

## Important Notes

### Email Logic

When sending donor data to Salesforce, WeGive selects which email to populate in the standard Email field based on the donor's Preferred Email setting:

| `preferred_email` value | Exported to Email |
| :- | :- |
| `personal` or `personal_email` | `email_1` |
| `work` or `work_email` | `email_2` |
| `alternate` or `alternate_email` | `email_3` |
| anything else, or empty | `email_1` |

This is hard-coded logic.

### Name defaults

The hard-coded export sends `FirstName` and `LastName` exactly as stored on the donor. Separately, the data made available to export mapping rules substitutes `FNU` (First Name Unknown) for an empty `first_name` and `LNU` (Last Name Unknown) for an empty `last_name`. Whether a Contact ends up with `FNU`/`LNU` therefore depends on a mapping rule for `first_name` or `last_name` being present. Salesforce requires `LastName`, so a donor with no last name and no rule for `last_name` will fail to insert.

### Address Concatenation

When sending addresses to Salesforce, Address Line 1 and Address Line 2 are joined with a single space into the Street field (`MailingStreet` or `OtherStreet`). Address fields are only sent when the donor actually has that address record; otherwise they are sent as null. Empty strings in any payload are converted to null before the request is made.

### AccountId handling

`AccountId` is never part of the hard-coded export payload. There is one exception: when WeGive reuses an existing Salesforce Contact found by email (see below) and no mapping rule has set `AccountId`, the integration copies that Contact's current `AccountId` back into the update payload so Salesforce portal-user validation rules are satisfied. It does not send the donor's own `salesforce_account_id`.

After WeGive inserts a brand new Contact, it reads the Contact back from Salesforce to capture the `AccountId` that NPSP assigned (normally the automatically created Household Account) and stores it on the donor as `salesforce_account_id`. If the donor belongs to a WeGive family household that has no Salesforce ID yet, that household's `salesforce_id` is set to the same Account ID. This is the only way a WeGive household receives a Salesforce ID from the WeGive side; households themselves are not pushed. See [Account](./account).

### Understanding Configurable vs Hard-coded

* **Configurable mappings** can be customized through integration settings if needed for your organization's specific field setup.
* **Hard-coded mappings** are built into the integration's core logic and handle special business rules (like email selection and address formatting).

## Contact Matching and Create/Update Logic

When WeGive exports an individual donor to Salesforce, the integration determines whether to create a new Contact or update an existing one:

**Step 1: Check for an existing Salesforce ID.** If the WeGive donor already has a `salesforce_id`, the integration updates that Contact. If the update returns a 404 (the Contact was deleted or merged away in Salesforce), the stale `salesforce_id` is cleared and the donor goes through the create path below. If there is no `salesforce_id`, it proceeds to Step 2.

**Step 2: Search by Email 1.** If the donor has an `email_1` value, the integration queries Salesforce for Contacts whose standard `Email` equals that value, ordered by most recently modified, and takes the first result. Only `email_1` is used; `email_2`, `email_3`, and the Preferred Email setting do not affect matching. If the donor has no `email_1`, or no Contact matches, a new Contact is created.

**Step 3: Verify Contact availability.** If no other WeGive donor in the organization is already linked to the matched Contact (same `salesforce_id` and `salesforce_account_id`), the integration updates that Contact with the donor's payload, and stores its `Id` and `AccountId` on the donor. If another WeGive donor is already linked to it, the integration inserts a new Contact instead, and leaves the existing Contact untouched.

A short-lived lock prevents two concurrent pushes of the same donor from creating duplicate Contacts.

<Note>
  Matching also happens in the import direction. A pulled Contact that does not match any donor by `salesforce_id` is matched to an existing individual donor by `email_1` when that donor has no `salesforce_id` yet. Otherwise a new donor is created.
</Note>

## Household Membership on Import

When a Contact's `AccountId` matches the `salesforce_id` of an existing WeGive family household, the imported donor is attached to that household and WeGive does not create an automatic household for them. If the donor previously belonged to a different household, they are moved. See [Account](./account) for how households are created from Household Accounts.

## Merged Contacts

The integration detects Contacts merged in Salesforce by pulling Contacts whose `MasterRecordId` is set (always filtered by `LastModifiedDate`, regardless of the `pull_by` setting, because Salesforce only stamps standard system fields during a merge). If both the losing Contact and the master Contact are linked to WeGive individual donors, those two donors are merged in WeGive, with the donor linked to the master Contact surviving. If either side is not linked to a WeGive donor, nothing happens. The `contacts_with_emails_only` filter also applies to this query.

## Deleted Contacts

If the `pull_deleted_donors` setting is enabled, the integration queries Contacts with `IsDeleted = true` in the sync window and deletes the matching WeGive individual donor (matched by `salesforce_id`). Contacts not linked to a WeGive donor are ignored. The `contacts_with_emails_only` filter applies here too.

<Warning>
  Deleting a Contact in Salesforce with `pull_deleted_donors` enabled removes the donor in WeGive. Leave this setting off if Salesforce is not your system of record for supporter deletions.
</Warning>

## Settings Reference

| Setting | Effect |
| :- | :- |
| Pull donors (Sync Configuration) | Enables pulling Contacts into WeGive |
| Push donors (Sync Configuration) | Enables pushing individual donors to Contacts |
| `pull_by` | Salesforce column used for the sync window. Default `LastModifiedDate`. |
| `contacts_with_emails_only` | Adds `Email != null` to every Contact pull, merge, and delete query |
| `pull_deleted_donors` | Enables deleting WeGive donors when their Contact is deleted in Salesforce |

## Related Documentation

* [Data Mapping Overview](./overview) - object index and cross-cutting data conventions
* [Account](./account) - company and household mapping
* [Configuring Salesforce State and Country Picklist Mappings](https://wegive-help-center.help.usepylon.com/articles/3843908042-configuring-salesforce-state-and-country-picklist-mappings-in-wegive) - customer setup walkthrough in the Knowledge Base

*Verified against the integration source, September 2026.*


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