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

# Statuses & Lifecycle States

> The boolean flags and status fields that control how records behave across CRM+ — Contacts, Projects, Campaigns, RecurringGifts, and Pledges each have their own lifecycle conventions.

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:

| 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. |

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

```json theme={null}
{
  "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:

| 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.                                      |

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.

<Note>
  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.
</Note>

***

## Campaign lifecycle

Campaigns have a single `isArchived` flag. Archived Campaigns are excluded from most listing endpoints by default but remain readable by `campaignId`.

```json theme={null}
{
  "campaignId": 22,
  "name": "Year-End Giving 2023",
  "isArchived": true
}
```

<Warning>
  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](/crm/concepts/funds-campaigns-designations#campaigns-the-organizing-context).
</Warning>

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:

```json theme={null}
{
  "id": 4501,
  "contactId": 4821,
  "amount": 50.00,
  "frequency": "Monthly",
  "status": "Active",
  "startDate": "2024-01-15",
  "nextExpectedPaymentDate": "2025-02-15",
  "cancelDateTimeUtc": null
}
```

| 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.                 |

<Note>
  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?).
</Note>

### Cancelling a RecurringGift

CRM+ provides a dedicated cancellation endpoint:

```bash cURL theme={null}
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`.

<Warning>
  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.
</Warning>

***

## Pledge lifecycle

Pledges have a parallel structure to RecurringGifts — an explicit `status` field plus dedicated state-transition endpoints:

```json theme={null}
{
  "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 }
  ]
}
```

| 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. |

<Note>
  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.
</Note>

### 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:

```bash cURL theme={null}
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:

| 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](/crm/concepts/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:

```json theme={null}
{
  "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.

<Tip>
  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.
</Tip>

See [Query Contacts by Filters](/crm/workflows/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](/raise/concepts/lifecycle) and [Volunteer](/volunteer/concepts/lifecycle) for product-specific guidance (these pages will be added when their respective Concept sections are built out).

***

## Where to go next

<CardGroup cols={2}>
  <Card title="Query Contacts by Filters" icon="filter" href="/crm/workflows/query-contacts-by-filters">
    Build an incremental sync that picks up lifecycle changes automatically via `modifiedDateTimeUtc`.
  </Card>

  <Card title="Handle Duplicate Records" icon="copy" href="/crm/workflows/handle-duplicate-records">
    Including archived records in deduplication queries.
  </Card>

  <Card title="Sync Recurring Donor Updates" icon="arrows-rotate" href="/crm/recipes/sync-recurring-donor-updates">
    A worked recipe for keeping RecurringGift state synchronized with your platform.
  </Card>

  <Card title="Transactions" icon="arrow-down-to-line" href="/crm/concepts/transactions">
    The holding state for in-flight Contact and Gift submissions — a lifecycle of its own.
  </Card>
</CardGroup>
