The data model
The RecurringGift describes the schedule (frequency, amount, designations, start date, status). Each scheduled payment becomes its own Gift record, linked to the RecurringGift via therecurringGiftTransactionId field on the Gift Transaction submission.
Two important consequences of this model:
- The RecurringGift itself is not money. It’s a commitment record. The Gifts that get linked to it represent actual transactions.
- Cancelling a RecurringGift does not delete past Gifts. Cancellation stops new payments from being recorded against the schedule; the historical record of past payments remains.
RecurringGift endpoints
The endpoints available for managing RecurringGifts:| Endpoint | Use |
|---|---|
POST /api/RecurringGift | Create a new recurring gift schedule. |
GET /api/RecurringGift/{recurringGiftId} | Retrieve a single schedule by ID. |
GET /api/RecurringGift/ByContact/{contactId} | List all schedules for a specific Contact. |
PUT /api/RecurringGift/{recurringGiftId} | Update an existing schedule. |
PUT /api/RecurringGift/Cancel/{recurringGiftId} | Mark a schedule as cancelled. |
POST /api/RecurringGift/Query | Search schedules by structured filters. |
GET /api/RecurringGift/QueryOptions | Retrieve valid filter parameters. |
POST /api/RecurringGiftPayment/{recurringGiftId} | Record a manual payment against a schedule (rare for partners — most payments come via Gift Transactions). |
Creating a schedule
When a donor signs up for a recurring donation on your platform — a Stripe Subscription, a recurring scheduled donation in your platform’s database — create the corresponding RecurringGift in Virtuous.id — store it on your side alongside your platform’s commitment record.
Required and recommended fields
| Field | Required | Purpose |
|---|---|---|
contactId | Yes | The donor’s Virtuous Contact ID. Resolve via Create a Contact before creating the RecurringGift. |
transactionSource + transactionId | Strongly recommended | Idempotency key. As with Gifts, use a stable identifier from your platform — typically the recurring schedule’s own ID, not the first payment’s ID. |
startDate | Yes | When the schedule begins. Use the date of the donor’s first scheduled payment. |
amount | Yes | Per-payment amount. |
frequency | Yes | The cadence — typically Monthly, Quarterly, Annually. |
designations[] | Yes | At least one designation. Designation amounts must sum to amount. |
Frequency values
Pre-flight: the donor’s Contact must exist
POST /api/RecurringGift takes a contactId directly — there is no embedded contact data with matching, unlike POST /api/v2/Gift/Transaction. Resolve or create the donor’s Contact before creating the schedule:
JavaScript
POST /api/v2/Gift/Transaction (which has embedded contact matching) and create the RecurringGift after the Contact appears. The RecurringGift links forward to the next payment; the first payment is one-off in Virtuous.
Linking payments to a schedule
Each time the recurring schedule produces a payment — Stripe charges the subscription, your platform’s recurring billing fires — submit a Gift Transaction withrecurringGiftTransactionId set to the schedule’s transactionId:
JavaScript
transactionIdis the specific payment’s ID — different on every payment of the schedule.recurringGiftTransactionIdis the schedule’s ID — the same value on every payment.
recurringGiftTransactionId to associate the new Gift with the existing schedule. In the UI, the Gift appears in the schedule’s payment history; in reports, the donor’s recurring total reflects the new payment.
Updating a schedule
When the donor changes their recurring amount, switches their designation, or otherwise modifies the schedule, push the change to Virtuous withPUT /api/RecurringGift/{recurringGiftId}.
Follow the same GET-then-PUT pattern as Contact updates (see Update a Contact):
JavaScript
Common update scenarios
| Donor action | Fields to change |
|---|---|
| Increase or decrease amount | amount and proportional designations[].amountDesignated. |
| Change designation | Replace the designations[] array. Confirm the new amounts still sum to amount. |
| Change frequency | frequency. Some platforms also require an updated nextExpectedPaymentDate. |
| Donor’s payment method updated | No direct change — payment method is tracked by your platform, not by Virtuous. The schedule continues at the same amount and frequency. |
Cancelling a schedule
When a donor ends their recurring commitment — actively unsubscribes, cancels in your platform’s portal, or has their payment method fail past the dunning threshold — cancel the schedule in Virtuous:cURL
JavaScript
Cancel endpoint is the right path for cancellation. Do not use PUT /api/RecurringGift/{id} with status: "Cancelled" — the canonical lifecycle transition is via the Cancel endpoint, which sets the cancelDateTimeUtc field and triggers the appropriate audit log entry.
Handling failed payments
When a scheduled payment fails (card expired, insufficient funds, account closed), the partner platform typically enters a dunning state — retrying the payment over several days before giving up. The Virtuous-side handling depends on the eventual outcome:| Eventual outcome | Virtuous action |
|---|---|
| Payment succeeds on retry | Submit the successful payment as a Gift Transaction with recurringGiftTransactionId. Schedule continues. |
| Payment fails permanently | No Gift submission. Surface to the customer to follow up with the donor; if the donor doesn’t update payment info, cancel the schedule. |
| Donor updates payment method | The next successful payment is recorded normally. Schedule continues. |
Reconciliation
Periodic reconciliation between your platform’s recurring schedules and Virtuous catches drift:JavaScript
End-to-end checklist
Before deploying a recurring-donation integration to production, confirm:- RecurringGifts are created with stable
transactionSource+transactionId(the schedule’s ID, not a payment’s ID). - The donor’s Contact is resolved before RecurringGift creation.
- Each recurring payment Gift Transaction includes
recurringGiftTransactionIdlinking back to the schedule. - Schedule updates use
GET-then-PUTwith the full record. - Cancellations use
PUT /api/RecurringGift/Cancel/{id}, not status updates via standard PUT. - Failed payments are not submitted as Gifts.
- Monthly reconciliation runs and surfaces amount/status drift between your platform and Virtuous.
- If your platform supports pause/resume, the chosen Virtuous-side modeling (cancel/recreate vs. suppress-payments) is documented and agreed with the customer.
Where to go next
Stripe to Virtuous CRM
The Stripe-specific recipe that sources recurring schedules from Stripe Subscriptions.
Import Historical Gifts
Backfill historical recurring payment data — each payment as a Gift, linked to the recreated schedule.
Statuses and Lifecycle States
The RecurringGift status field and its lifecycle states.
Reconcile Failed Syncs
Handle drift in recurring schedule state between your platform and Virtuous.