Skip to main content
CRM+ does not use a single unified “status” model. Different resources express lifecycle state in different ways: Contacts use boolean flags (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.
The defensive pattern: always read the lifecycle flags on records you process, and check them against the operation you’re about to perform.

Contact lifecycle

Contacts use three boolean flags:
FlagMeaningAPI behavior
isPrivateThe 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.
isArchivedThe 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:
EndpointUse
PUT /api/Contact/Archive/{contactId}Archive a Contact.
PUT /api/Contact/Unarchive/{contactId}Unarchive (restore) a Contact.
These are state transitions, not deletions — the Contact’s data is preserved. Use these rather than DELETE /api/Contact/{id} when you need a record removed from active use but kept on file.

Including archived records in queries

{
  "groups": [
    { "conditions": [
        { "parameter": "Last Modified Date", "operator": "Is After", "value": "2024-01-01T00:00:00Z" }
    ]}
  ],
  "includeArchived": true,
  "skip": 0,
  "take": 100
}
Most incremental syncs should include archived records when reconciling — otherwise an archived Contact that you also had on your side will appear deleted from your sync’s perspective on the next run, when in fact it was archived and is still tracked in Virtuous.

Handling deceased individuals

When a ContactIndividual has isDeceased: 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.
Partner integrations that drive communications (email platforms, direct mail integrations) should always check 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:
FlagControls
isActiveWhether the Project accepts new designations. Inactive Projects retain historical gifts but reject new ones.
isPublicWhether the Project is visible in organization-wide Project lists. Internal-only Projects are excluded from most reports.
isAvailableOnlineWhether the Project can be designated to from a public-facing donation form.
isTaxDeductibleWhether designations to this Project are tax-deductible. Affects receipt generation.
For partner integrations submitting gifts: filter to 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 separate PUT /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 single isArchived flag. Archived Campaigns are excluded from most listing endpoints by default but remain readable by campaignId.
{
  "campaignId": 22,
  "name": "Year-End Giving 2023",
  "isArchived": true
}
Campaigns are read-only through the CRM+ API. There is no API endpoint to archive a Campaign — the archive/unarchive state transitions happen in the Virtuous UI. See Funds, Campaigns, and Designations.
Time-based fields on Campaigns (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 explicit status field plus a cancelDateTimeUtc timestamp:
{
  "id": 4501,
  "contactId": 4821,
  "amount": 50.00,
  "frequency": "Monthly",
  "status": "Active",
  "startDate": "2024-01-15",
  "nextExpectedPaymentDate": "2025-02-15",
  "cancelDateTimeUtc": null
}
FieldDescription
statusThe schedule’s current state (active, paused, cancelled, failed).
startDateWhen the schedule began.
anticipatedEndDateWhen the schedule is expected to end (if defined).
nextExpectedPaymentDateWhen the next payment is anticipated. Null when the schedule is no longer active.
cancelDateTimeUtcTimestamp 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
curl -X PUT https://api.virtuoussoftware.com/api/RecurringGift/Cancel/4501 \
  -H "Authorization: Bearer YOUR_API_TOKEN"
Cancellation is a state transition: 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.
RecurringGift cancellation is a permanent state transition. Confirm with the donor and your customer before calling PUT /api/RecurringGift/Cancel/{id} — there is no “uncancel” endpoint, only the option to create a new recurring schedule from scratch.

Pledge lifecycle

Pledges have a parallel structure to RecurringGifts — an explicit status field plus dedicated state-transition endpoints:
{
  "id": 9112,
  "contactId": 4821,
  "amountPledged": 10000.00,
  "frequency": "Annual",
  "pledgeDate": "2024-01-15",
  "expectedFulfillmentDate": "2028-01-15",
  "status": "Active",
  "payments": [
    { "id": 1, "expectedPaymentDate": "2024-01-15", "expectedAmount": 2500.00, "giftId": 78001, "actualAmount": 2500.00 }
  ]
}
FieldDescription
statusThe pledge’s current state (active, written off, completed).
pledgeDateWhen the pledge was made.
expectedFulfillmentDateWhen 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
curl -X PUT https://api.virtuoussoftware.com/api/v2/Pledge/WriteOff/9112 \
  -H "Authorization: Bearer YOUR_API_TOKEN"
The write-off is a state transition similar to RecurringGift cancellation. The Pledge remains in the system with status: "Written Off" (or equivalent) and historical payment records are preserved.

Gift lifecycle

Gifts do not have an explicit status field. A Gift exists once recorded; its “state” is expressed through related records and metadata:
ConceptHow it’s expressed
Original giftA standard Gift record.
Refund or reversalA separate Gift created via POST /api/Gift/ReversingTransaction. The original Gift remains; the reversing transaction offsets it.
Pledge paymentA Gift with pledgeTransactionId set, linking it to a Pledge’s payment schedule.
Recurring gift paymentA 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.
There is no archived-Gift state in the spec. Gifts are immutable once posted and reversals are explicit accounting entries, not state changes.

Timestamps as a temporal lifecycle anchor

Every resource carries createDateTimeUtc and modifiedDateTimeUtc fields. These are the foundation of incremental sync workflows:
{
  "createDateTimeUtc": "2024-01-15T09:23:00Z",
  "createdByUser": "admin@example.org",
  "modifiedDateTimeUtc": "2024-12-15T14:30:00Z",
  "modifiedByUser": "admin@example.org"
}
The 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.
Build incremental syncs around modifiedDateTimeUtc. On each sync run, query for records with modifiedDateTimeUtc > {previous_run_timestamp}, and store the maximum modifiedDateTimeUtc from the response as the next run’s filter floor. This pattern catches lifecycle transitions for free — a Contact archived since the last run will appear in the result set with isArchived: true, and your integration can update its own state accordingly.
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 archived boolean on most resources and a separate refund flow for gifts.
  • Volunteer API uses active/inactive flags on most resources and a different convention for participation state.
The CRM+ patterns described on this page do not translate directly. See Raise and Volunteer for product-specific guidance (these pages will be added when their respective Concept sections are built out).

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.
Last modified on May 21, 2026