> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wegive.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Neon CRM Integration Nuances

> Important behaviors, limitations, and considerations for the WeGive Neon CRM integration

# Integration Nuances

<Warning>
  This page previously contained a large amount of generic, non-code-verified filler (rate-limit throttling, exponential-backoff retry, webhook queuing, rollback tooling, escalation tiers, and more) that doesn't reflect what this integration actually does. It's been rewritten around verified behavior only.
</Warning>

## Account Creation and Duplicate Prevention

* **Account type**: determined by the WeGive donor's `type` field (`individual` or `company`) — sent to Neon as `IndividualAccount`/`CompanyAccount` accordingly
* **Duplicate prevention on push**: `neon_account_id` correlation only. There's no name+address secondary matching. `exportDonor()` does have one specific fallback: if creating a new account fails with Neon's error code `10012` (duplicate account), the integration searches Neon by email and links the existing account rather than creating a second one — but that's the only "duplicate resolution" logic that exists.
* **Duplicate prevention on pull**: email match against existing WeGive `User` records — a Neon account row missing an email is skipped entirely, not partially imported

## Transaction Synchronization

* **Create vs. update**: a transaction with both `neon_id` and `neon_payment_id` already set triggers an **update** call; otherwise it's treated as new and creates. There's no restriction against updating an existing Neon donation — the "payments not modified" framing in a prior version of this page was inaccurate.
* **Fee attribution**: `donorCoveredFee` is only sent as a non-zero value when the transaction has `cover_fees` set — otherwise `0`
* **Received date**: `payout.paid_at` maps to Neon's `receivedDate`, but only once the transaction has an associated payout (i.e., after it's actually been paid out, not at initial gift creation)

## Custom Field Mapping Behavior

<Warning>
  The `literal` mapping-rule flag is **not honored** by Neon's field-application code — every configured mapping is resolved as a JSONPath lookup against rendered WeGive data, regardless of the flag. This is a known issue, with a fix in review.
</Warning>

<Warning>
  **Field mappings are push-only in practice.** `NeonMappingRule` records are only consulted by the push (export) functions — `generateAccountParams()`/`generateDonationParams()`. Pull uses its own separate, hardcoded field list and never applies `NeonMappingRule` at all. A mapping rule configured with `level = 'import'` or `'both'` has no pull-side consumer. This is a known issue.
</Warning>

* **JSONPath resolution**: `$.path.to.field` syntax against a pre-rendered resource array (via `DonorWebhookResource`/`TransactionWebhookResource`), not a live model traversal
* **Array handling**: the mapping code takes the first element of whatever JSONPath returns (`->get(...)[0]`)
* **No type coercion or automatic conversion**: whatever value the JSONPath resolves to is sent as-is

## Campaign Integration

* **Active/inactive filtering on pull**: `importCampaigns()` explicitly skips any Neon campaign with `status == 'INACTIVE'`
* **Statistics are pull-only, computed by Neon, never by WeGive**: `total_donated`, `number_of_donations`, etc. are read from Neon's own `statistics` object — WeGive doesn't calculate these independently
* **No campaign hierarchy, event, or custom-field support**: campaign push sends name, dates, goal, status, and generated donation/checkout URLs only

## Address Management

<Warning>
  **Address sync (`syncAddress()`) is dead code — it never actually runs.** It's fully implemented but has no caller anywhere in the codebase. This is a known issue.
</Warning>

* **What would happen if it were wired up**: push-only, one-way WeGive → Neon, with mobile/office/home/fax phone fields included alongside address fields — but this is describing unreachable code, not current behavior
* **What actually syncs today**: the donor account push (`generateAccountParams()`) includes both mailing and — for individual donors only — billing address, whenever both exist, with no "only primary address" filtering. This inline copy has **no phone/fax fields** — meaning **no phone or fax data reaches Neon via this integration at all today**.
* **No format/state-code/zip validation performed by WeGive** in either path: whatever's stored in the WeGive address record would be sent as-is

## Payment Method Handling

<Warning>
  Only 3 WeGive payment source types are mapped: `card` → Credit Card Offline, `bank`/`donor` → Check, anything else → Check (fallback default). There is no distinct cash, stock/securities, in-kind, or "other methods" categorization — despite this being listed as flexible in prior documentation.
</Warning>

* **Card data**: only the last 4 digits (zero-padded to 4 characters if shorter), issuer (mapped to a single-letter code — Visa/MasterCard/Amex/Discover only), and expiration month/year are sent — full card numbers are never transmitted by this integration
* **Bank data**: institution name, account type (always `"Checking"`), and last 4 digits — same zero-padding logic as cards

## Error Handling

<Warning>
  There is no automatic retry, exponential backoff, or rollback tooling anywhere in this integration. Every Neon API call (`get`/`post`/`put`/`remove` on `NeonIntegration`) is a single `Http::timeout(120)` request. A failed call simply fails; the calling code either returns early/skips (donor/campaign/address sync) or throws an exception that propagates up (`pushTransaction`/`pushScheduledDonation` in `Neon.php` explicitly throw on a failed response).
</Warning>

* **Donor sync failure blocking donation sync**: if `syncDonation()` needs to create a Neon account first (donor has no `neon_account_id`) and that donor export fails to actually assign one, `syncDonation()` throws an explicit exception rather than silently skipping the donation
* **Webhook delivery failures**: WeGive is the receiver for the 4 supported webhooks (donor/donation create/update), not the sender — retry behavior for a failed webhook *delivery* is entirely Neon's responsibility, not something this integration configures or monitors

## Integration Limitations

* **No event/ticketing sync**
* **No volunteer tracking sync**
* **No communication/email history sync**
* **No file attachment sync**
* **No campaign hierarchy sync**
* **No address sync at all** — this is a known issue; phone/fax numbers never reach Neon via any path in this integration
* **Webhooks limited to donor and donation create/update** — no webhook exists for recurring donations, campaigns, or addresses
* **`track_donations`/`track_recurring_donations` toggles are dead** — this is a known issue; only `track_donors` (all donor-adjacent objects) and `track_campaigns` actually gate sync

## Troubleshooting

### Sync Failures

* Check Sentry (not a dedicated dashboard) for the specific integration log/error
* Verify both the Neon Organization ID and API key are correct
* If a donation isn't syncing, check whether the donor sync (triggered inline first) failed

### Data Looks Wrong in Neon

* **Payment method showing as Check unexpectedly**: expected for any source type other than `card` — see the payment method warning above
* **Custom field not populating on pull**: known gap — pull doesn't apply `NeonMappingRule` at all
* **A "literal" mapping rule not sending its fixed value**: known gap, in review

## Support and Escalation

* [Neon CRM API Documentation](https://developer.neoncrm.com/)
* WeGive Support: [support@wegive.com](mailto:support@wegive.com)

For specific field-level detail, see the [Data Mapping Overview](/external/onboarding/neon/data-mapping/overview).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.