isPrivate, isArchived), Projects use a mix of flags (isActive, isPublic, isAvailableOnline), RecurringGifts and Pledges have an explicit status string field, and Gifts have no explicit status at all — their lifecycle is expressed through related records (reversing transactions, pledge payments).
This page maps the lifecycle conventions for each resource and the patterns partner integrations should use to interpret and respect them.
Why lifecycle states matter
Lifecycle states drive three behaviors that any partner integration needs to handle correctly:- Query inclusion or exclusion. Most list endpoints exclude archived records by default. If your sync needs to find deleted records, you must opt in.
- Write acceptance or rejection. Designating a gift to an inactive Project, or creating a payment on a cancelled RecurringGift, will fail.
- Downstream behavior. A private Contact’s data may not be visible to your integration; a deceased ContactIndividual’s email should not receive automated communications.
Contact lifecycle
Contacts use three boolean flags:| Flag | Meaning | API behavior |
|---|---|---|
isPrivate | The Contact is hidden from most users — visible only to administrators. Used for high-profile donors or sensitive records. | The record returns through the API for credentials with sufficient permissions. For credentials with restricted permissions, the record may be filtered out or return 403. |
isArchived | The Contact has been removed from active workflows but retained for historical record. | Excluded from POST /api/Contact/Query by default. Include with "includeArchived": true in the filter body. |
isDeceased (on ContactIndividual) | The individual has died. Does not archive the Contact — the household record may still be active. | Returned in standard queries; your integration is responsible for suppressing communications. |
Archive and unarchive
CRM+ provides dedicated endpoints for archiving and unarchiving Contacts:| Endpoint | Use |
|---|---|
PUT /api/Contact/Archive/{contactId} | Archive a Contact. |
PUT /api/Contact/Unarchive/{contactId} | Unarchive (restore) a Contact. |
DELETE /api/Contact/{id} when you need a record removed from active use but kept on file.
Including archived records in queries
Handling deceased individuals
When a ContactIndividual hasisDeceased: true:
- Their associated emails and phones should be excluded from outbound communications.
- The household Contact remains active if other living individuals remain.
- The household name typically updates (the deceased individual’s name is removed from joint salutations) but this is managed in the Virtuous UI, not automatically.
isDeceased before sending. The flag is on the ContactIndividual, not the Contact — read it per-individual when iterating a household.
Project lifecycle
Projects expose four lifecycle flags. The combination of values determines whether a Project can accept new gift designations and whether it appears in various views:| Flag | Controls |
|---|---|
isActive | Whether the Project accepts new designations. Inactive Projects retain historical gifts but reject new ones. |
isPublic | Whether the Project is visible in organization-wide Project lists. Internal-only Projects are excluded from most reports. |
isAvailableOnline | Whether the Project can be designated to from a public-facing donation form. |
isTaxDeductible | Whether designations to this Project are tax-deductible. Affects receipt generation. |
isActive: true and isPublic: true when presenting Project options to your customer. These are the Projects safe to designate gifts toward.
Temporary inventory status changes
A separatePUT /api/Project/{projectId}/Status endpoint exists for temporarily changing a Project’s inventory status — for example, marking a Project as temporarily unavailable during a fulfillment pause. The endpoint accepts an inventoryStatus value and an optional durationMinutes for an automatic revert.
This is distinct from the long-term isActive flag — inventory status is a short-term override, while isActive is the persistent state. Most partner integrations do not need to call the Status endpoint; it is used for operational pauses rather than ongoing lifecycle management.
The valid
inventoryStatus values are not enumerated in the spec — the parameter is typed as string. Confirm with the Virtuous team before using this endpoint, or restrict your integration to reading the field rather than writing it.Campaign lifecycle
Campaigns have a singleisArchived flag. Archived Campaigns are excluded from most listing endpoints by default but remain readable by campaignId.
startDateTimeUtc, endDateTimeUtc) define the active window. A Campaign whose endDateTimeUtc has passed is still gettable and still associated with its historical gifts, but most workflows would not designate new gifts to a closed Campaign.
RecurringGift lifecycle
RecurringGifts have an explicitstatus field plus a cancelDateTimeUtc timestamp:
| Field | Description |
|---|---|
status | The schedule’s current state (active, paused, cancelled, failed). |
startDate | When the schedule began. |
anticipatedEndDate | When the schedule is expected to end (if defined). |
nextExpectedPaymentDate | When the next payment is anticipated. Null when the schedule is no longer active. |
cancelDateTimeUtc | Timestamp set when the schedule was cancelled. Null while active. |
The CRM+ spec does not enumerate the valid
status values for RecurringGift — the field is typed as string. Common values include Active, Cancelled, Paused, and Failed, but the authoritative list depends on the organization’s configuration and the platform version. Treat unknown status values defensively in your integration.⚠️ Human input required: Confirm the canonical enum of status values for RecurringGift, and update this page with the authoritative list and what each state means for downstream behavior (e.g., does Paused resume automatically, or require manual reactivation?).Cancelling a RecurringGift
CRM+ provides a dedicated cancellation endpoint:cURL
status becomes Cancelled and cancelDateTimeUtc is set to the current time. Cancellation is not reversible through the API — re-creating a recurring schedule for the donor requires a new POST /api/RecurringGift.
Pledge lifecycle
Pledges have a parallel structure to RecurringGifts — an explicitstatus field plus dedicated state-transition endpoints:
| Field | Description |
|---|---|
status | The pledge’s current state (active, written off, completed). |
pledgeDate | When the pledge was made. |
expectedFulfillmentDate | When the pledge is expected to be fully paid. |
payments[] | The schedule of expected payments and their fulfillment status. |
As with RecurringGift, the CRM+ spec does not enumerate the valid Pledge
status values. Common values include Active, Written Off, and Completed, but treat unknown values defensively. Confirm the canonical list with the Virtuous team.Writing off a Pledge
When a donor cannot fulfill a pledge, the pledge is written off rather than deleted. This preserves the historical commitment record while removing the pledge from active obligations:cURL
status: "Written Off" (or equivalent) and historical payment records are preserved.
Gift lifecycle
Gifts do not have an explicitstatus field. A Gift exists once recorded; its “state” is expressed through related records and metadata:
| Concept | How it’s expressed |
|---|---|
| Original gift | A standard Gift record. |
| Refund or reversal | A separate Gift created via POST /api/Gift/ReversingTransaction. The original Gift remains; the reversing transaction offsets it. |
| Pledge payment | A Gift with pledgeTransactionId set, linking it to a Pledge’s payment schedule. |
| Recurring gift payment | A Gift with recurringGiftTransactionId set, linking it to a RecurringGift schedule. |
| Pending (Transaction state) | Not yet a Gift — exists only as a holding-state Transaction until the nightly batch processes it. See Transactions. |
Timestamps as a temporal lifecycle anchor
Every resource carriescreateDateTimeUtc and modifiedDateTimeUtc fields. These are the foundation of incremental sync workflows:
modifiedDateTimeUtc field updates whenever any field on the record changes — including lifecycle flag changes. This means a single query filtered by modifiedDateTimeUtc > {last_sync} catches all changes regardless of whether they were content edits, status transitions, or archive/unarchive actions.
See Query Contacts by Filters for the full incremental-sync pattern.
Cross-API lifecycle conventions
If your integration spans multiple Virtuous products, note that lifecycle conventions differ:- Raise API uses its own
archivedboolean on most resources and a separate refund flow for gifts. - Volunteer API uses
active/inactiveflags on most resources and a different convention for participation state.
Where to go next
Query Contacts by Filters
Build an incremental sync that picks up lifecycle changes automatically via
modifiedDateTimeUtc.Handle Duplicate Records
Including archived records in deduplication queries.
Sync Recurring Donor Updates
A worked recipe for keeping RecurringGift state synchronized with your platform.
Transactions
The holding state for in-flight Contact and Gift submissions — a lifecycle of its own.