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 status | Sent? | Amount sent |
|---|---|---|
success | Always | amount / 100 |
processing | Yes, unless payment type is unknown, or it’s ACH (bank) and send_processing_ach_as_success is off | amount / 100 |
failed / refunded | Only if it already has a real Gift ID (not a btch_* batch ID) | 0 (zeroes out the existing gift) |
| anything else | No | — |
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 field | Source | Notes |
|---|---|---|
contact.id | owner.virtuous_contact_id | The parent Contact |
contactIndividualId | owner.virtuous_contact_individual_id | Only if set |
amount | amount / 100 | 0 for failed/refunded |
giftDate | created_at in the destination’s timezone | Y-m-d |
description | description | |
notes | supporter_inputted_notes | |
isTaxDeductible | is_tax_deductible | |
isPrivate | anonymous | |
designations | fund / fund allocations | [{ id: <project virtuous_id>, amountDesignated: <amount/100> }] |
segmentId / segment / segmentCode | campaign’s Virtuous segment + its code | |
recurringGiftTransactionId | scheduledDonation.virtuous_id | Links to the recurring gift |
customFields | { "wg_id": <transaction.id> } | Object form (batch only) |
giftType | see 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 field | Source | Notes |
|---|---|---|
contactId | owner.virtuous_contact_id | |
contactIndividualId | owner.virtuous_contact_individual_id | Only if set |
amount | amount / 100 | |
giftDate | created_at in destination timezone | Y-m-d |
description | description | |
notes | supporter_inputted_notes | |
isTaxDeductible | is_tax_deductible | |
isPrivate | anonymous | |
giftDesignations | fund / fund allocations | [{ projectId: <project virtuous_id>, amount: <amount/100> }] |
segmentId / segment | campaign’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) |
giftType | see 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
designationswith{ id, amountDesignated }and acustomFieldsobject; real-time usesgiftDesignationswith{ projectId, amount }and acustomFieldsarray.
Payment method (giftType)
The integration sets giftType for only two payment types. All other source types are sent without a giftType:
WeGive source_type | Virtuous giftType |
|---|---|
card | Credit |
bank | EFT |
| 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 field | Source | Notes |
|---|---|---|
virtuous_id | gift id | |
amount | amount * 100 | Only set when not already present |
description | description, else "Virtuous Import" | |
created_at | giftDate | Midnight dates are stored as 17:00:00 |
status | existing status, else success | |
is_tax_deductible | isTaxDeductible | |
anonymous | isPrivate | |
supporter_inputted_notes | notes |
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.