Skip to content

RecurringGift Mapping

RecurringGift Mapping

WeGive Scheduled Donations map to Virtuous RecurringGift records.

Record Model

WeGive recordVirtuous recordCorrelation column
Scheduled DonationRecurringGiftvirtuous_id

Frequency Mapping

WeGive frequencyVirtuous frequency
weeklyWeekly
monthlyMonthly
quarterlyQuarterly
yearlyAnnually

Push (WeGive → Virtuous)

Before pushing, the integration first pushes any related records that lack a virtuous_id — the donor (Contact), the campaign (Segment), and the fund(s) (Projects).

Creating a recurring gift

POST RecurringGift
{
"startDate": <start_date, Y-m-d>,
"nextExpectedPaymentDate": <start_date, Y-m-d>,
"frequency": "Weekly" | "Monthly" | "Quarterly" | "Annually",
"amount": <charge_amount / 100>,
"isPrivate": <anonymous>,
"segmentId": <campaign.virtuous_id>,
"designations": [ { "projectId": <project>, "amountDesignated": <dollars> } ],
"contactId": <source.virtuous_contact_id>
}

The returned id is stored as the scheduled donation’s virtuous_id.

Amount and fees

The pushed amount is the charge_amount (base amount plus fees), not the base amount. The designations are built so their sum equals charge_amount:

  • When fee-splitting is on (or no fee fund is configured), the fee is distributed proportionally across the allocation designations, with rounding absorbed by the last row.
  • When a dedicated fee fund is configured, the fee is added as (or merged into) a separate designation for that fund.

This mirrors WeGive’s own recurring fee allocation so per-installment gifts and the recurring plan stay aligned in Virtuous.

Updating a recurring gift

When the scheduled donation already has a virtuous_id, the integration GETs the RecurringGift and overwrites the WeGive-owned fields, then PUTs it back:

  • nextExpectedPaymentDate, amount, isPrivate, segmentId
  • designations — replaced entirely (WeGive is the source of truth for allocations)

Pull (Virtuous → WeGive)

RecurringGifts are pulled via POST RecurringGift/Query (1000 per page, filtered by the configured pull_by date) and imported as Scheduled Donations, matched on virtuous_id:

WeGive fieldSourceNotes
virtuous_idrecurring gift id
start_datenextExpectedPaymentDate
created_atcreateDateTimeUtc
deleted_atcancelDateTimeUtcSet when the gift is cancelled in Virtuous
frequencyreverse of the frequency map above
anonymousisPrivate
amountamount * 100New records only
fee_amount0New records only

amount and fee_amount are set only on first import. On existing records, WeGive is the source of truth — Virtuous stores the fee-inflated charge_amount, so pulling it would create a feedback loop of growing fees.

Source resolution: a company donor (by virtuous_contact_id, no individual) is tried first; otherwise the Contact’s primary individual. Fund comes from the first designation’s projectId; campaign from segmentId. When fund allocations are enabled, allocations are synced to WeGive from the designations on first import only (amountDesignated * 100 → allocation amount).

This reflects the integration as implemented in app/Integrations/Virtuous.php.