> ## 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 Recurring Donation Mapping

> Detailed mapping between Neon CRM recurring donation objects and WeGive scheduled donation records

# Recurring Donation Object Mapping

This document details the real mapping used by `NeonIntegration::generateRecurringDonationParams()`. Recurring donation sync is **push-only** — there is no pull for recurring donations at all.

<Warning>
  **Frequency mapping is broken, not just narrow.** `recurringPeriod` is sent as `$scheduledDonation->iteration` — but `iteration` is a **live-computed count of successful charges this plan has been through** (`getIterationAttribute()`: `(int) $this->transactions()->success()->count()`), not a frequency value. This means `recurringPeriod` changes every single time the plan is charged (1, 2, 3...) and has **no relationship whatsoever** to the plan's actual cadence. WeGive's real frequency field (`ScheduledDonation::frequency` — monthly/weekly/quarterly/etc.) is never read or sent anywhere in this integration. This is a known issue — high priority, unlike the other Neon findings in this audit, since this produces actively wrong, changing data for every synced org today (not a dormant/deprecated-integration concern).
</Warning>

## Core Recurring Donation Mapping

| WeGive Field | Neon Field |
| - | - |
| `owner.neon_account_id` | `accountId` |
| `chargeAmount` (amount + fee if `cover_fees`, cents) | `amount` (÷100) |
| `start_date` | `nextDate` |
| `iteration` (charge count — **wrong value**, see warning above) | `recurringPeriod` |
| — | `recurringPeriodType` — always hardcoded `"LIFE"` |

<Warning>
  There is no `currency` field sent — no USD default, no currency key at all.
</Warning>

<Warning>
  Campaign, fund, purpose, and end date are all commented-out placeholders in `generateRecurringDonationParams()` — none are ever populated, despite Neon's API supporting all of them. A recurring donation's campaign attribution and any end-date/limited-term configuration never reaches Neon.
</Warning>

## Integration Fields

| WeGive Field (stored) | Neon Field |
| - | - |
| `neon_id` | correlation key for update vs. create |

## Custom Field Mapping

<Note>
  Unlike accounts and donations, there is no seeded default `NeonMappingRule` set for recurring donations in this integration — the hardcoded fields above are the entirety of what's sent, with no custom-mapping override path exercised in the code shown for this object type.
</Note>

## Synchronization Behavior

### Recurring Donation Creation/Update (Push)

1. If `neon_id` already set → `PUT /recurring/{neon_id}` (update); otherwise → `POST /recurring` (create)
2. There is no "verify donor account/payment method first" orchestration inside recurring-donation push itself — if the donor's `neon_account_id` isn't set, the push simply sends `accountId: null`
3. There is no distinct status/pause handling sent to Neon — a paused or ended WeGive plan doesn't push a corresponding status change to Neon; Neon simply keeps receiving whatever `nextDate`/`amount`/(broken) `recurringPeriod` the plan currently has whenever it's next pushed

<Warning>
  There is no automatic retry. A failed push simply fails — check Sentry for the specific Neon API error.
</Warning>

### No Pull

There is no pull for recurring donations — `SyncNeon.php` has no `pullRecurringDonation()`/`importRecurringDonations()` method. Recurring donations created or edited directly in Neon never flow back to WeGive.

## API Examples

### Creating a Recurring Donation (real payload shape)

```http theme={null}
POST /v2/recurring
{
  "accountId": "67890",
  "amount": 100.00,
  "nextDate": "2026-03-01",
  "recurringPeriod": 0,
  "recurringPeriodType": "LIFE"
}
```

<Note>
  `recurringPeriod: 0` here reflects a brand-new plan with zero successful charges yet — not a "period of zero" in any meaningful sense. After the plan's first successful charge, the next push of this same plan would send `recurringPeriod: 1`; after the second charge, `2`; and so on, unrelated to actual cadence.
</Note>

### Updating a Recurring Donation

```http theme={null}
PUT /v2/recurring/rec123
{
  "accountId": "67890",
  "amount": 75.00,
  "nextDate": "2026-03-15",
  "recurringPeriod": 3,
  "recurringPeriodType": "LIFE"
}
```

## Error Handling

<Warning>
  There is no automatic retry, amount validation, or schedule-feasibility validation performed by WeGive for recurring-donation push. A failed push simply fails.
</Warning>

## Best Practices

* Don't rely on Neon's `recurringPeriod` field for this integration's data to reflect a donor's actual giving cadence today — it doesn't, pending a known issue being fixed
* Understand there's no campaign/fund attribution on recurring donations reaching Neon at all — if campaign-level recurring revenue reporting matters, it isn't available via this path
* There's no pull, so any recurring-plan edit made directly in Neon won't flow back — make plan edits in WeGive

## Related Documentation

* [Donation Mapping](/external/onboarding/neon/data-mapping/donation) — for the per-charge transaction records this plan generates
* [Data Mapping Overview](/external/onboarding/neon/data-mapping/overview)


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