Person Mapping
This document details the real mapping between WeGive donor records and Planning Center Person objects, including contact information. The mapping is narrower than a full CRM sync — no demographic fields, no household/family relationships, and matching is by correlation ID only.Core Person Mapping
Primary Fields
There is no
middle_name pulled back (it’s push-only), and no suffix, birthdate, anniversary, gender, created_at, or updated_at field mapping. Planning Center’s own status attribute is pulled and mapped to WeGive’s enabled flag (status !== 'inactive') — that’s the only other person-level field synced.Contact Information Mapping
Email Address Management
Fields synced: up to 3 emails, positional —email_1, email_2, email_3.
Push behavior:
- Only runs when a donor is first created, or on every update via
syncDonorContactInfo() - Fetches the person’s current Planning Center emails first; an email already present (exact string match) is skipped, not re-sent
- New emails are posted with
primary: trueonly foremail_1;email_2/email_3areprimary: false - Location is always the literal string
"Email"— there’s no “home”/“work”/“other” categorization - A 409 or 422 response (Planning Center considers the email a duplicate) is treated as success and skipped silently — not an error
email_1/email_2/email_3.
Phone Number Management
Fields synced: up to 2 phone numbers, positional —mobile_phone, office_phone. There is no home_phone/work_phone distinction anywhere in the code.
Push behavior:
- Same current-value-check-then-post pattern as email
primary: trueonly formobile_phone- Location is always the literal string
"Phone Number"— no “mobile”/“home”/“work” categorization - Same 409/422-is-a-silent-skip behavior as email
mobile_phone/office_phone.
Address Management
Fields synced: one mailing address only —address_1 (from street_line_1), city, state, zip. There’s no street_2, no country field mapped on push, and no address type/categorization beyond the fixed location string "Mailing Address".
Push behavior:
- Only syncs if the address has non-empty
address_1,city,state, andzip— an incomplete address is skipped entirely, not partially synced - Finds the existing address marked
primaryin Planning Center and PATCHes it if found, otherwise POSTs a new one - Always sent with
primary: true
addresses.0) into WeGive’s mailing address, with a country default of "US" if Planning Center doesn’t provide one.
Data Transformation
Person Creation Processing (Push)
- Acquire a per-donor Redis lock (600s) to prevent duplicate creation under concurrent pushes
- Create the Person with
first_name/last_nameonly ("FNU"/"LNU"placeholders if blank) - Store the returned
idasplanning_center_id - Call
syncDonorContactInfo()to add emails/phones/address
Person Update Processing (Push)
- PATCH the Person’s
first_name/last_namebyplanning_center_id - If the PATCH returns 404 (person deleted in Planning Center), clear the stale
planning_center_idand create a new Person instead — this is a full re-create, not a narrower “confirm and skip” check - Call
syncDonorContactInfo()again regardless
Person Import Processing (Pull)
- Match by
planning_center_id; create a new WeGive Donor if no match - Map
first_name/last_name/middle_name/statusand the positional email/phone arrays - Create or update the mailing address from
addresses.0
Matching and Deduplication
Person Matching Strategy
The only matching mechanism, on push and pull alike, isplanning_center_id. There is no email-based, name-based, or partial-name-plus-phone matching anywhere in this integration. A donor without a stored planning_center_id always results in a new Planning Center Person being created — even if a person with the same name or email already exists there.
This matters for duplicate-contact tickets: the fix isn’t “improve matching logic” (there is none to improve) — it’s ensuring the donor gets linked to the correct existing Planning Center person (via
planning_center_id) before its first push.Conflict Resolution
- Push: WeGive’s
first_name/last_namealways overwrite Planning Center’s on update; contact info is additive (existing values aren’t removed, missing values are added if not already present) - Pull: Planning Center’s data always overwrites WeGive’s mapped fields
Validation Rules
- Planning Center requires at least one of
first_name/last_name— WeGive satisfies this unconditionally via the"FNU"/"LNU"placeholders, so a blank-name donor never fails Person creation - Email/phone addition is skipped gracefully (not treated as an error) if Planning Center reports it as a duplicate
- An address with any of
address_1/city/state/zipempty is skipped entirely on push — there’s no partial-address sync
Known Gaps (not implemented)
- Demographic fields: birthdate, anniversary, gender
- Household/family relationships of any kind
- Suffix, nickname, or middle-name-on-pull
- More than 3 emails or 2 phone numbers
- More than one address, or any address type beyond “mailing”
- Communication/opt-out preference sync
- Any performance/caching optimization beyond the existing-value check before each push