Skip to main content

Account Object Mapping

This document details the real mapping between Neon CRM account objects and WeGive donor records, based on NeonIntegration::generateAccountParams() and SyncNeon::processAccount().
The donor account push and the (dead) address push (syncAddress()) would be two separate API calls to two separate Neon endpoints (/accounts and /addresses) if the address push were reachable — but it isn’t (known issue). Phone numbers and fax are not included in the account push’s own address array either, so no phone/fax data reaches Neon via this integration at all today.

Individual Account Mapping

Core Identity Fields

There is no computed name (firstName + " " + lastName) sent to Neon — name is a WeGive-internal display field, not part of the account push payload.

Contact Information

Phone/fax are not part of the account push at all, and there’s nowhere else they’d sync either — syncAddress() (the code that would send mobile_phone/home_phone/office_phone/fax) is dead code with no caller. See Address Mapping. This is a known issue. No phone or fax data reaches Neon via this integration today.

Communication Preferences

Neon’s API itself may expect "GIVEN"/"NOT_GIVEN"/"UNKNOWN" string enums for consent fields — WeGive sends its own raw boolean value for email/sms regardless. Whether Neon’s API coerces this correctly on its end isn’t something WeGive verifies.

Integration Fields

Login Block

Every individual account push includes a login block with username derived from the donor’s name (preg_replace('/\PL/u', '', $donorProfile->name) — strips all non-letter Unicode characters) and a hardcoded, identical password for every donor across every organization: 'WeGiveTest1234'. This is a known issue — low priority, since WeGive currently has no active customer on this integration, but a real finding if that ever changes.

Company Account Mapping

Core Identity Fields

There is no separate business-phone/business-email field distinct from the individual structure — company accounts reuse the same primaryContact object, with firstName sent as null. There’s also no “company classification”/industry field mapped.

Contact Information

Integration Fields

Company accounts don’t get a login block — the login key is only ever added conditionally, but in the actual code both account types share the same primaryContact structure and neither has explicit login suppression logic distinguishing individual from company; in practice the login block is present for both account types as generated (see the Login Block warning above).

Address Within the Account Push

The account push’s primaryContact.addresses array (this is the only address data that actually reaches Neon today — see the note above) includes, for the mailing address: For individual donors only, a second address entry is included if billingAddress exists, with the same field set (address lines, city, zip, state) — but no isPrimaryAddress flag is set on it, and no phone/fax fields are included on either entry.
There is no country/country.code field sent on either address entry within the account push — country mapping doesn’t exist in generateAccountParams().

Data Transformations

Name Processing

  • First/last names mapped separately for individuals — no computed display name sent
  • Company accounts map WeGive’s name field directly

Username Generation

  • preg_replace('/\PL/u', '', $donorProfile->name) — strips everything that isn’t a Unicode letter (spaces, digits, punctuation), keeping only letters. "John Smith" becomes "JohnSmith".

Email

  • Up to 3 emails sent (email_1/email_2/email_3) — no format validation is performed by WeGive before sending; any rejection would be a Neon-side API error
  • No “primary email” logic beyond email_1 mapping to email1 — there’s no separate designation

Communication Preferences

  • Sent as raw WeGive booleans (see the warning above) — not translated to Neon’s GIVEN/NOT_GIVEN/UNKNOWN enum strings

Synchronization Behavior

Account Creation/Update (Push)

  1. If neon_account_id/neon_id already set → PUT /accounts/{accountId} (update)
  2. Otherwise → POST /accounts (create)
  3. On create, if Neon returns error code 10012 (duplicate account), search Neon by email and link the existing account instead of retrying create
  4. There is no “last modified wins”/timestamp-based conflict resolution — push always sends WeGive’s current state; whichever side calls last simply overwrites what Neon has for the mapped fields

Import from Neon CRM (Pull)

  1. POST /accounts/search with a Account Last Modified Date > (last sync - 1 day) filter, plus Email 1 NOT_BLANK and First Name NOT_BLANK filters
  2. Match to an existing WeGive User by exact email
  3. No match → create a new User (random password, via Hash::make(Str::random())) + Donor + Login
  4. If a Donor matching the pulled account already exists, only neon_account_id and the mailing/billing address are refreshed — first/last name, email, and consent fields are not re-imported on subsequent pulls once the donor already exists
NeonMappingRule is not consulted anywhere in the pull path above — the field list is fully hardcoded in SyncNeon::processAccount(). Any custom account-level mapping rule with level = 'import'/'both' has no effect on pull. This is a known issue.

Custom Field Mapping

The default seeded NeonMappingRule records for accounts:
These are the same fields generateAccountParams() already hardcodes directly (see above) — the mapping rules are somewhat redundant with the hardcoded logic for the default set, and only matter for custom additions beyond these defaults. Any custom addition only applies on push, per the pull-mapping-rules gap noted above.

Adding Custom Mappings

There is no dashboard “level: bidirectional” behavior distinct from any other level for Neon in practice — regardless of what level is set to, a custom mapping rule is only ever applied on push. This is a known issue.

Error Handling

There is no automatic retry, duplicate-merge tooling, or field-validation pre-check beyond the single 10012-duplicate-error recovery path described above. A failed account push (validation error, missing required field, network failure) simply fails — check Sentry/the integration log for the specific Neon API error response.

Best Practices

Data Quality

  • Keep donor emails valid and populated — pull skips accounts without an email entirely
  • Understand that a donor’s first/last name and email won’t re-sync from Neon once a Donor already exists in WeGive — only the address refreshes on later pulls

Integration Management

  • Check Sentry (not the WeGive dashboard) for account sync failures — there’s no dedicated sync-status view for this integration
  • Be aware of this known issue if you’re considering re-enabling Neon donor-portal login features for any org

API Examples

Creating an Individual Account

This example reflects real payload shape — including the hardcoded literal password, and consent.email/consent.sms as raw booleans rather than Neon-style enum strings.

Searching Accounts (Pull)