Skip to content

Gift Mapping

Gift Mapping

WeGive Transactions map to Virtuous Gifts. There are two push paths with slightly different payload shapes — a batch importer and a real-time single-gift path — plus a pull path. This page documents each exactly.

Which transactions are sent

Before any push, the integration decides whether a transaction should go to Virtuous:

Transaction statusSent?Amount sent
successAlwaysamount / 100
processingYes, unless payment type is unknown, or it’s ACH (bank) and send_processing_ach_as_success is offamount / 100
failed / refundedOnly if it already has a real Gift ID (not a btch_* batch ID)0 (zeroes out the existing gift)
anything elseNo

Push — batch path

The batch importer sends transactions to POST v2/Gift/Transactions in chunks of 100, with createImport: true. Each transaction becomes one entry:

Virtuous fieldSourceNotes
contact.idowner.virtuous_contact_idThe parent Contact
contactIndividualIdowner.virtuous_contact_individual_idOnly if set
amountamount / 1000 for failed/refunded
giftDatecreated_at in the destination’s timezoneY-m-d
descriptiondescription
notessupporter_inputted_notes
isTaxDeductibleis_tax_deductible
isPrivateanonymous
designationsfund / fund allocations[{ id: <project virtuous_id>, amountDesignated: <amount/100> }]
segmentId / segment / segmentCodecampaign’s Virtuous segment + its code
recurringGiftTransactionIdscheduledDonation.virtuous_idLinks to the recurring gift
customFields{ "wg_id": <transaction.id> }Object form (batch only)
giftTypesee below

After a successful batch send, each transaction’s virtuous_id is set to a synthetic batch ID like btch_WeGive_Import (not a real Gift ID).

Push — real-time single path

When real_time is enabled, individual transactions are sent to POST Gift (and updated with PUT Gift/{id}). The payload is similar but the shapes differ from the batch path:

Virtuous fieldSourceNotes
contactIdowner.virtuous_contact_id
contactIndividualIdowner.virtuous_contact_individual_idOnly if set
amountamount / 100
giftDatecreated_at in destination timezoneY-m-d
descriptiondescription
notessupporter_inputted_notes
isTaxDeductibleis_tax_deductible
isPrivateanonymous
giftDesignationsfund / fund allocations[{ projectId: <project virtuous_id>, amount: <amount/100> }]
segmentId / segmentcampaign’s Virtuous segment + code
recurringGiftPayments[{ id: <recurring gift id>, amount }]Only if linked to a scheduled donation
customFields[{ "name": "wg_id", "value": <transaction.id> }]Array form (real-time only)
giftTypesee below

On create, the returned Gift id is stored as the transaction’s virtuous_id. On update, the integration GETs the existing gift, overwrites the WeGive-owned fields (amount, description, segment, tax/private flags, notes, designations, custom fields), and PUTs it back. Designation updates use Virtuous’s diff protocol: every existing designation is marked Delete and the desired set is appended as Add, achieving replace semantics.

The two paths differ deliberately: batch uses designations with { id, amountDesignated } and a customFields object; real-time uses giftDesignations with { projectId, amount } and a customFields array.

Payment method (giftType)

The integration sets giftType for only two payment types. All other source types are sent without a giftType:

WeGive source_typeVirtuous giftType
cardCredit
bankEFT
anything else(not set)

Amounts

WeGive stores amounts in cents; Virtuous in dollars. Amounts are divided by 100 on push (and multiplied by 100 on pull). There are no separate Fee, CurrencyCode, or GiftStatus fields in the payload.

Pull (Virtuous → WeGive)

Gifts are pulled via POST Gift/Query/FullGift (1000 per page, filtered by the configured pull_by date). Each record is imported as a Transaction:

WeGive fieldSourceNotes
virtuous_idgift id
amountamount * 100Only set when not already present
descriptiondescription, else "Virtuous Import"
created_atgiftDateMidnight dates are stored as 17:00:00
statusexisting status, else success
is_tax_deductibleisTaxDeductible
anonymousisPrivate
supporter_inputted_notesnotes

Matching: a pulled gift is matched first by virtuous_id, then by the wg_id custom field (which links back to the original WeGive transaction); otherwise a new transaction is created.

Owner resolution: a company donor (by virtuous_contact_id, no individual) is tried first; otherwise the donor is matched by contactIndividualId, falling back to the Contact’s primary individual.

Designations: the owning fund is taken from the first giftDesignations entry’s projectId; the campaign from segmentId. When fund allocations are enabled, allocations are reconciled to WeGive from the gift’s giftDesignations (projectId → fund, amountDesignated * 100 → amount). Recurring-gift linkage is not restored on pull.

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