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

# Recurring Donation

> Mapping between WeGive Recurring Plans and Salesforce Recurring Donations

# **Recurring Donation (Scheduled Donations) - Field Mapping**

**Salesforce Object:** npe03\_\_Recurring\_Donation\_\_c<br />**WeGive Model:** ScheduledDonation

## **NPSP Requirements**

**Required NPSP Settings:**

* Enable Enhanced Recurring Donations
* Installment Opportunity Auto-Creation: **Disable All Installments**
* Next Donation Date Match Range: **NULL**

***

## **How Recurring Donation 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** - Can be customized via integration settings or mapping rules
* **Configurable (mapping rule)** - Reaches Salesforce only when a mapping rule for it exists
* **Hard-coded** - Built into the integration logic and cannot be changed

***

## **Sync Configuration**

Recurring Donation pulls and pushes are controlled by the **Pull recurring donations** and **Push recurring donations** toggles in Sync Configuration. Related settings:

| Setting | Effect |
| :- | :- |
| `sync_all_recurring_donations` | Pull Recurring Donations of every status. When off, only `npsp__Status__c = 'Active'` records are pulled |
| `push_rd_household_for_individuals` | Populate both `npe03__Contact__c` and `npe03__Organization__c` (see Organization vs Contact) |
| `household_record_type_name` | Account Record Type name that identifies Household Accounts when resolving the donor on import (default "Household Account") |
| `card_payment_method_name`, `bank_payment_method_name`, `paypal_payment_method_name` | Value written to `npsp__PaymentMethod__c` |
| `pull_deleted_scheduled_donations` | Remove WeGive plans whose Recurring Donation was deleted in Salesforce |
| `enable_fund_allocations` (organization setting) | Master switch for syncing GAU Allocations on transactions and Recurring Donations |
| `push_fund_allocations` | When `enable_fund_allocations` is also on, actually pushes the plan's fund allocations to `npsp__Allocation__c` after each push. `enable_fund_allocations` alone does not push allocations — both settings must be on |

***

## **Sync Triggers**

### **From WeGive to Salesforce (Export)**

Recurring donation data is exported from WeGive to Salesforce when:

* **Recurring Donation Created:** A new scheduled donation is created in WeGive
* **Recurring Donation Updated:** An existing scheduled donation is modified in WeGive (e.g., amount changed, status changed, payment method updated, paused/resumed)

A recurring gift's first installment may also push the plan inline if the plan has not yet received a Salesforce ID, so the Opportunity can be linked to it.

### **From Salesforce to WeGive (Import)**

Recurring donation data is imported from Salesforce to WeGive based on:

* **Last Modified Date:** WeGive periodically polls Salesforce for Recurring Donation records that have been modified since the last sync
* **Status filter:** Only records with `npsp__Status__c = 'Active'` are pulled unless `sync_all_recurring_donations` is enabled
* **Modified Field Tracking:** Only records with a `LastModifiedDate` newer than the last successful sync are pulled into WeGive

### **Deleted Recurring Donations**

When `pull_deleted_scheduled_donations` is enabled, Recurring Donations with `IsDeleted = true` in the sync window soft-delete the matching WeGive plan.

***

## **Sync Process Overview**

### **Pulling Data from Salesforce**

The base query reads `Id`, `CreatedDate` and the Organization Account's Record Type name; all other fields come from your Recurring Donation mapping rules. When `enable_fund_allocations` is on, the plan's `npsp__Allocation__c` children are fetched in the same query.

The import then:

* Matches the WeGive plan by stored `salesforce_id`, creating a new plan if none exists
* **For new plans only**, resolves the donor from the mapped Contact (`source.salesforce_id`) and/or Account (`source.salesforce_account_id`). If the Account's Record Type is not the household Record Type it is treated as an Organization Account and the company donor is used; otherwise the Contact is used. The record fails if no donor is found
* Resolves campaign and fund from mapped Salesforce IDs
* Resolves the WeGive frequency by matching the mapped `period` and `frequency` values against the frequency map
* Applies `CreatedDate` to `created_at` on create only
* Converts `last_change_amount` and `campaign_change_amount` from dollars to cents if mapped

<Warning>
  **Import overwrite guard.** Salesforce owns a plan's schedule only when the plan has no payment method in WeGive and has never been charged through WeGive. For such record-only plans the import writes `amount`, `frequency`, `start_date` and sets `fee_amount` to 0. For live plans those fields are left untouched. In all cases the import never writes WeGive-owned columns such as the payment method, pause state, source or deletion timestamp, even if a mapping rule points at them.
</Warning>

### **Pushing Data to Salesforce**

Before the plan is pushed, the donor is pushed if it has no Salesforce ID and the payment method is pushed as an accessory (a payment method failure does not block the plan). The payload then includes:

* **Amount Calculation:** If the donor opted to cover fees, the donation amount plus the fee amount
* **Status Mapping:** Active, Paused, or Closed based on the plan state
* **Frequency Mapping:** WeGive frequency converted to NPSP Installment Period and Installment Frequency
* **Day of Month:** Taken from the start date
* **Payment Method:** From the payment method name settings
* **Card/ACH Last 4:** Last four digits of the payment method
* **Lifecycle timestamps:** Managed-package DateTime fields for pause, cancellation, last failure and last sync

The same payload is used for create and update.

**NPSP Installment Auto-Creation Prevention:**

`npsp__DisableFirstInstallment__c` is always sent as `true` so NPSP does not create the first installment Opportunity. WeGive creates Opportunities as donations are processed.

### 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 |
| CreatedDate | Created At | `created_at` | Import from Salesforce | **Hard-coded** | Applied on create only |
| npe03\_\_Amount\_\_c | Donation Amount | `amount` | Both Ways | **Hard-coded** export<br />**Configurable** import | Export: (amount + fee\_amount) / 100 when cover\_fees, else amount / 100. Import: value x 100, only when Salesforce owns the schedule |
| npe03\_\_Installment\_Period\_\_c | Installment Period | `period` | Both Ways | **Hard-coded** export<br />**Configurable** import | See Picklist Values |
| npsp\_\_InstallmentFrequency\_\_c | Installment Frequency | `frequency` | Both Ways | **Hard-coded** export<br />**Configurable** import | Numeric (e.g. 1, 2, 3) |
| npe03\_\_Next\_Payment\_Date\_\_c | Next Payment Date | `start_date` | Per rule | **Configurable (mapping rule)** | Not in the default payload. Import into `start_date` only applies when Salesforce owns the schedule |
| npe03\_\_Contact\_\_c | Donor Contact ID | `source.salesforce_id` | Both Ways | **Hard-coded** export<br />**Configurable** import | Export: always the donor's Contact ID (see Organization vs Contact) |
| npe03\_\_Organization\_\_c | Donor Account ID | `source.salesforce_account_id` | Both Ways | **Hard-coded** export<br />**Configurable** import | Export: company donors only, unless `push_rd_household_for_individuals` |
| npe03\_\_Recurring\_Donation\_Campaign\_\_c | Campaign ID | `campaign.salesforce_id` | Both Ways | **Hard-coded** export<br />**Configurable** import | |
| npsp\_\_Status\_\_c | Recurring Status | Dynamic | Export to Salesforce | **Hard-coded** | Active / Paused / Closed. Also the pull filter unless `sync_all_recurring_donations` |
| npsp\_\_Day\_of\_Month\_\_c | Day of Month | Dynamic | Export to Salesforce | **Hard-coded** | Day of `start_date`, falling back to `created_at`, then today |
| npsp\_\_StartDate\_\_c | Start Date | `start_date` | Export to Salesforce | **Hard-coded** | `start_date`, falling back to `created_at`, then today |
| npsp\_\_PaymentMethod\_\_c | Payment Method | `payment_method_type` | Export to Salesforce | **Configurable** | See Picklist Values |
| npsp\_\_CardLast4\_\_c | Card Last 4 Digits | `paymentMethod.last_four` | Export to Salesforce | **Hard-coded** | Populated for any payment method type |
| npsp\_\_ACH\_Last\_4\_\_c | ACH Last 4 Digits | `paymentMethod.last_four` | Export to Salesforce | **Hard-coded** | Populated for any payment method type |
| npsp\_\_DisableFirstInstallment\_\_c | Disable First Installment | Static: true | Export to Salesforce | **Hard-coded** | Prevents NPSP from auto-creating Opportunities |
| wegive\_\_WeGive\_ID\_\_c | WeGive ID | `id` | Export to Salesforce | **Hard-coded** | Stripped for shared objects in multi-entity orgs |
| wegive\_\_Paused\_At\_DateTime\_\_c | Paused At | `paused_at` | Export to Salesforce | **Hard-coded** | ISO 8601; dropped if the org's package version lacks the field |
| wegive\_\_Pause\_Until\_DateTime\_\_c | Paused Until | `paused_until` | Export to Salesforce | **Hard-coded** | Same |
| wegive\_\_Deleted\_At\_DateTime\_\_c | Deleted At | `deleted_at` | Export to Salesforce | **Hard-coded** | Same |
| wegive\_\_Canceled\_At\_DateTime\_\_c | Canceled At | `deleted_at` | Export to Salesforce | **Hard-coded** | Same value as Deleted At |
| wegive\_\_Last\_Failed\_At\_DateTime\_\_c | Last Failed At | last failed transaction date | Export to Salesforce | **Hard-coded** | Same |
| wegive\_\_Last\_Sync\_Date\_\_c | Last Sync | now | Export to Salesforce | **Hard-coded** | Same |
| wegive\_\_WeGive\_Entity\_\_c | WeGive Entity | Organization setting | Export to Salesforce | **Hard-coded** | Multi-entity orgs only |
| Any custom field | Custom field or attribute | Mapping rule path | Per rule | **Configurable (mapping rule)** | See Values Available to Mapping Rules |

### Values Available to Mapping Rules

In addition to the plan's attributes and custom fields, export rules can map:

* `status` - WeGive's own plan status vocabulary (Active, Paused, Cancelled), as opposed to the NPSP vocabulary written to `npsp__Status__c`
* `ends_at` - formatted `Y-m-d`
* `last_change_amount`, `campaign_change_amount` - change-history deltas in dollars
* `source`, `fund`, `campaign`, `paymentMethod` attributes

<Note>
  **Pause cause is not in the default payload, but is mappable.** The plan's raw `pause_initiator` (`payment_failure` / `donor` / `admin`) and `pause_reason` columns are real, first-class fields (they distinguish a donor-initiated pause from a system pause after repeated failed payments), and both ride the same attribute spread available to export mapping rules — but neither is one of the hardcoded default fields above, so out of the box a paused plan shows the same generic `npsp__Status__c = 'Paused'` in Salesforce regardless of who paused it or why. To report on pause cause in Salesforce, add export mapping rules for `pause_initiator` and/or `pause_reason` to a custom field of your choice.
</Note>

## **Picklist Values**

### **npe03\_\_Installment\_Period\_\_c and npsp\_\_InstallmentFrequency\_\_c**

Each WeGive frequency maps to a period plus a numeric frequency through the integration's frequency map. For example, "biweekly" is Installment Period "Weekly" with Installment Frequency 2, and "quarterly" is "Monthly" with 3. On import, the mapped `period` and `frequency` pair is looked up in the same map to recover the WeGive frequency; a pair that does not match leaves the frequency unset.

<Note>
  On export the frequency map's built-in values are used. The map is not overridable per integration on export.
</Note>

### **npsp\_\_Status\_\_c (Recurring Donation Status)**

| WeGive Plan State | Salesforce Status |
| :- | :- |
| Not paused, not deleted, `ends_at` empty or in the future | Active |
| `paused_at` set | Paused |
| `deleted_at` set, or `ends_at` before today | Closed |

Closed takes precedence over Paused. This logic is hard-coded.

### **npsp\_\_PaymentMethod\_\_c (Payment Method)**

| WeGive Payment Method Type | Salesforce Payment Method |
| :- | :- |
| card | `card_payment_method_name` |
| bank | `bank_payment_method_name` |
| paypal | `paypal_payment_method_name` |
| anything else | empty |

Blank settings write nothing. There are no other built-in values. These are the same settings used for `npe01__Payment_Method__c` on Payments, so both objects carry the same vocabulary.

***

## **Important Notes**

### **Enhanced Recurring Donations Required**

This integration requires NPSP's **Enhanced Recurring Donations**. Standard Recurring Donations are not supported.

### **Amount with Fee Coverage**

If the donor has opted to cover processing fees, the exported amount is `(amount + fee_amount) / 100`.

**Example:** A \$100 monthly gift covering a \$3 fee exports as `npe03__Amount__c = 103.00`; without fee coverage it exports as `100.00`.

### **Day of Month and Start Date**

Both `npsp__Day_of_Month__c` and `npsp__StartDate__c` are derived from the plan's `start_date`. If `start_date` is empty the plan's `created_at` is used, and if that is empty, the current date.

### **Card and ACH Last 4**

`npsp__CardLast4__c` and `npsp__ACH_Last_4__c` are both populated with the payment method's last four digits regardless of whether the payment method is a card or a bank account. They are empty when the plan has no payment method.

### **Organization vs Contact**

Default behavior:

* `npe03__Contact__c` is always set to the donor's Contact ID (`salesforce_id`), for individuals and companies alike
* `npe03__Organization__c` is set to the donor's Account ID only for company donors

With `push_rd_household_for_individuals` enabled:

* Individual donors also get `npe03__Organization__c` set to their household Account ID, so NPSP can roll up household totals
* Company donors get `npe03__Contact__c` set to the company's primary individual (the donor's linked primary individual, or the Account's `npe01__One2OneContact__c` looked up from Salesforce)

On import, donor resolution uses the mapped Contact and Account IDs together with the Account's Record Type (see Pulling Data from Salesforce).

### **Create vs Update**

The same payload is sent on create and update. There are no create-only fields on the Recurring Donation.

***

## **Recurring Donation Matching & Create/Update Logic**

### **Step 1: Check for Existing Salesforce Recurring Donation ID**

* **If `salesforce_id` exists:** UPDATE the existing Recurring Donation
* **If not:** acquire a per-plan lock and INSERT a new Recurring Donation, then store its ID

### **Step 2: Allocations**

When `enable_fund_allocations` is on AND `push_fund_allocations` is on, the plan's fund allocations are synced to `npsp__Allocation__c` records on the Recurring Donation after the push (see the GAU & Allocation page). When either is off, no allocation is created for the Recurring Donation.

<Warning>
  Recurring Donations are never matched by attributes, only by stored Salesforce ID. If a plan loses its `salesforce_id` in WeGive it will create a duplicate Recurring Donation on the next push.
</Warning>

***

## **Related Opportunity Creation**

### **How Recurring Donations Create Opportunities**

When a transaction is processed for a recurring donation in WeGive, the integration creates an Opportunity linked to the parent Recurring Donation:

* **Recurring Donation** = the ongoing commitment/schedule
* **Opportunity** = each individual donation/installment

### **Opportunity Field Mappings for Recurring Donations**

| Salesforce Field | Value/Mapping | Notes |
| :- | :- | :- |
| npe03\_\_Recurring\_Donation\_\_c | `scheduled_donation.salesforce_id` | Links the Opportunity to its parent |
| Type | 'Recurring' | Set on create and update |
| Amount | `(amount - fee) / 100` | Net amount in dollars |
| CloseDate | transaction date | `Y-m-d` in the organization timezone |
| StageName | Configured `stage_*` setting for the status | See the Opportunity page |
| AccountId / ContactId | donor's Account / Contact ID | Create only |
| CampaignId | `campaign.salesforce_id` | From the transaction's campaign |

**Key Behaviors:**

1. **Plan pushed first:** If the plan has no Salesforce ID when its installment pushes, the plan is pushed inline first.
2. **Matching existing Opportunities:** Before creating a new Opportunity, the integration looks for an Opportunity on the same Recurring Donation with `StageName = 'Pledged'` (the literal NPSP value, regardless of your `stage_pending` setting). If found, it is adopted and updated instead. With `uses_payments`, an existing Payment on that Opportunity is adopted too.
3. **Payment creation:** If `uses_payments` is enabled, a Payment is created or updated on the Opportunity.
4. **Allocations on installments:** NPSP copies the Recurring Donation's GAU Allocations onto each installment Opportunity. The integration adopts those records rather than creating its own, and never deletes them. See the GAU & Allocation page.

### **Example Scenario**

1. Donor sets up a \$100/month recurring donation in WeGive; a Recurring Donation is created with `npsp__Status__c = 'Active'`
2. Each month a transaction processes; the integration creates or updates an Opportunity with `Type = 'Recurring'`, the configured success StageName, and `npe03__Recurring_Donation__c` pointing at the plan
3. If payments are enabled, a Payment record is also created

## Related Documentation

* [Data Mapping Overview](./overview) - object index and cross-cutting data conventions
* [Opportunity & Payment](./opportunity) - installment Opportunities and Payments
* [GAU & Allocation](./gau) - fund allocations on Recurring Donations and installments

*Verified against the integration source, September 2026.*


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