> ## 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 and Lifecycle States

> The lifecycle states across Donors, Gifts, RecurringGifts, and Campaigns in Raise — what each state means, the integer enums in the spec, and the patterns for handling test mode and GDPR deletions.

Several resources in Raise carry lifecycle state — flags or status enums that change as the record moves through its life. Partner integrations that read or sync Raise data need to handle these states correctly: a deleted donor under GDPR is different from an archived donor, a failed recurring gift is different from a cancelled one, a test-mode gift shouldn't appear in production reports.

This page is the catalog. Each resource's lifecycle states are documented with what the state means, where to find it on the record, and how partner integrations should handle it.

## Donor lifecycle states

Donors carry three boolean lifecycle flags on the `DonorModel`:

| Field           | Type    | What it means                                                                                                                                                             |
| --------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `isArchived`    | boolean | The donor has been archived — hidden from most views but the record persists with all history intact.                                                                     |
| `isGDPRDeleted` | boolean | The donor has been deleted under a GDPR right-to-erasure request. The record is retained as a tombstone (the `id` continues to exist) but personal data has been removed. |
| `isTestMode`    | boolean | The donor was created in test mode.                                                                                                                                       |

These three states are independent — a donor can be `isTestMode: true` and `isArchived: true` simultaneously, for example.

### `isArchived`

Archive is a soft-delete state. The donor's record continues to exist, all related Gifts continue to reference it, and the data is preserved for historical reporting. Archived donors are typically hidden from active-donor lists, prospecting workflows, and engagement metrics.

How to apply archive:

```bash cURL theme={null}
curl -X POST https://prod-api.raisedonors.com/api/Donor/12345/archive \
  -H "Authorization: Bearer YOUR_API_TOKEN"
```

Archive is reversible — flip `isArchived` back to `false` via a Donor update.

How partner integrations should handle:

* **Sync workflows**: exclude archived donors from default sync flows unless the customer explicitly wants archived data included.
* **Search and lookup**: hide archived donors from active-donor type-ahead suggestions.
* **Reporting**: filter to `isArchived: false` for "active donor" metrics.

### `isGDPRDeleted`

GDPR deletion is a hard delete of personal data with a retained tombstone. The donor's `id` continues to exist (so historical Gifts can still reference the donor) but the identifying fields (`firstName`, `lastName`, `email`, etc.) are removed or anonymized.

Unlike archive, GDPR deletion is not user-driven through the API — it's typically initiated through the customer's admin tools as part of a formal GDPR right-to-erasure request from the donor.

How partner integrations should handle:

* **Sync workflows**: when an existing local record's source donor flips to `isGDPRDeleted: true`, the local record should also be reduced to a tombstone with personal data removed.
* **New ingestion**: a donor that arrives as `isGDPRDeleted: true` should not be persisted with personal data — store only the `id` reference for accounting integrity.
* **Reporting**: GDPR-deleted donors typically don't appear in donor-facing reports but do count in aggregate gift totals.

See [Donors: GDPR right-to-erasure](/raise/concepts/donors#gdpr-right-to-erasure) for more detail.

### `isTestMode`

Test-mode donors are created when running donation flows in test mode — typically during integration development or QA work in a customer's organization. The records are real, but they're flagged as non-production data.

How partner integrations should handle:

* **Production reports and metrics**: filter to `isTestMode: false`.
* **Sync to downstream production systems**: skip records where `isTestMode: true`.
* **Development and test environments**: process test-mode records normally.

A common reporting pattern: every query that builds production-facing dashboards adds a `isTestMode: false` filter at the query layer.

***

## Gift lifecycle states

Gifts carry a status enum, several boolean flags, and a refund capability indicator.

### Gift status

The `status` field on `GiftModel` is an integer enum. The spec defines a `ChargeStatus` enum with 13 possible values (`0` through `12`) without labels:

```text theme={null}
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
```

The accompanying `statusText` field provides the human-readable display version. Partner integrations should:

* **Display**: use `statusText` directly — it's already in the user's locale.
* **Logic**: don't switch on integer values until the platform team publishes the enum-to-label mapping.

<Warning>
  **⚠️ Spec gap:** Gift `ChargeStatus` enum values (0–12) are not labeled in the OpenAPI spec. The 13 distinct values likely correspond to states like "Pending", "Approved", "Settled", "Declined", "Refunded", "Voided", and similar, but the mapping is not documented.

  Until the mapping is published, partner integrations relying on status checks should call `GET /api/Query/options/{queryType}` to discover the available status values for a Gift query — see [Pagination and Filtering: Discovering query options](/raise/pagination#discovering-query-options). The `statusText` field is the safe choice for any display surface.
</Warning>

### `canRefund`

```text theme={null}
"canRefund": true | false
```

Indicates whether the Gift is eligible for refund through `POST /api/Gift/{id}/refund`. Reasons a Gift might have `canRefund: false`:

* The Gift has already been refunded.
* The Gift is too old for the gateway's refund window.
* The Gift's gateway doesn't support refunds.
* The Gift hasn't been settled yet (refund-before-settlement may not be supported by some gateways).

Partner integrations exposing a refund UI should check `canRefund` and hide or disable the refund action when it's `false`.

### `isAnonymous`

When the donor chose to give anonymously, this flag is `true`. The donor's identifying information is still on the Gift record (for accounting and stewardship purposes) but the customer's public-facing displays should omit identifying information.

### `isTestMode`

Same semantics as on Donors — `true` for test-mode gifts. Filter to `false` for production reports.

***

## RecurringGift lifecycle states

RecurringGifts have the most complex lifecycle of any Raise resource — they're long-lived schedules with their own state machine.

### RecurringGift status

`status` on `RecurringGiftModel` is an integer enum. The spec defines a `RecurringGiftStatus` enum with 6 possible values (`1` through `6`) without labels:

```text theme={null}
1, 2, 3, 4, 5, 6
```

The `statusText` field provides the display string.

<Warning>
  **⚠️ Spec gap:** RecurringGift status values (1–6) are not labeled in the OpenAPI spec. The 6 distinct values likely correspond to states like "Active", "Cancelled", "Paused", "Failed", "Completed", and similar, but the mapping is not documented.

  As with Gift status, use `statusText` for display and `GET /api/Query/options/{queryType}` for discovering valid filter values until the labels are published.
</Warning>

### `hasPaymentFailed`

```text theme={null}
"hasPaymentFailed": true | false
```

When `true`, the most recent payment attempt against the schedule failed — typically because the card expired, the card was declined, or there were insufficient funds. The schedule is still "active" in the sense that it hasn't been cancelled, but it isn't successfully charging.

This is one of the most actionable signals for partner integrations doing donor stewardship — see [Recurring Gifts: Detecting payment failures](/raise/concepts/recurring-gifts#detecting-payment-failures).

### `isLegacy`

```text theme={null}
"isLegacy": true | false
```

Marks schedules that were created in an older Raise implementation. Some operations may behave differently for legacy schedules — confirm specific behavior with the platform team when targeting legacy data.

### Frequency enum

While not strictly a "status", the `frequency` field on a RecurringGift is another integer enum that determines the schedule's cadence. The spec defines a `Frequency` enum with these values:

```text theme={null}
0, 1, 2, 4, 12, 26, 52, 100
```

The values are not labeled in the spec, but the numbers themselves hint at semantics — `12` likely corresponds to monthly (12 payments per year), `52` to weekly, `26` to biweekly, `4` to quarterly. Confirm the exact mapping against live data or the platform team before relying on specific values.

<Note>
  **⚠️ Spec gap:** The `Frequency` enum values are not labeled in the spec. The integer values pattern (0, 1, 2, 4, 12, 26, 52, 100) suggests they map to common cadence semantics — but the exact mapping is not in the spec. Use `formattedFrequency` (which is human-readable) for display. For programmatic logic, confirm the integer-to-cadence mapping against the live API.
</Note>

### `isTestMode`

Same as on Gifts and Donors — `true` for test-mode schedules.

***

## Campaign lifecycle states

Campaigns don't have an explicit lifecycle enum like Gifts and RecurringGifts. Their lifecycle is implied by date fields and a small number of flags.

### Implicit states from `startDate` / `endDate`

| State        | How to determine                                   |
| ------------ | -------------------------------------------------- |
| **Active**   | Current date is between `startDate` and `endDate`. |
| **Upcoming** | Current date is before `startDate`.                |
| **Closed**   | Current date is after `endDate`.                   |

These states are not stored as fields — they're derived from comparing the campaign's date range to the current date. Partner integrations that need an "active campaigns" filter compute this client-side rather than relying on a field.

### `canSync`

```text theme={null}
"canSync": true | false
```

Controls whether the campaign's data syncs to CRM+. Not a lifecycle state in the traditional sense, but a state that meaningfully affects how the campaign behaves. See [How Raise Data Flows to CRM+](/raise/concepts/data-flow-to-crm#controlling-sync-at-the-campaign-level-cansync).

### No `isArchived` field on Campaigns

Unlike Donors, the Raise spec doesn't expose an `isArchived` flag on Campaigns. Customers typically either:

* **Delete past campaigns** they no longer want surfaced (using `DELETE /api/Campaign/{id}`), or
* **Leave them in place** with the past `endDate` indicating the campaign is closed.

Partner integrations that need to distinguish "active" from "historical" campaigns rely on date comparisons rather than a flag.

***

## DonationForm states

Donation Forms have lifecycle states (drafted, published, archived) in the Raise product, but **none of these states are exposed through the current API**. The `GiftFormModel` embedded in Gifts has only:

| Field      | Type    | Lifecycle meaning                                                                                                                           |
| ---------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `isLegacy` | boolean | Distinguishes forms built on the legacy form engine from modern forms. Useful when partner integrations need to handle the two differently. |

There's no API-exposed indicator of whether a form is currently published, accepting submissions, or in a particular configuration state. Coordinate with the customer's admin team for form-state visibility.

See [Donation Forms](/raise/concepts/donation-forms) for the full picture of what the API does and doesn't expose about forms.

***

## Webhook subscription status

Webhook subscriptions have a status enum that determines whether they receive event deliveries. The spec defines a `Status` enum with 2 values (`1` and `2`) without labels:

```text theme={null}
1, 2
```

The two values likely correspond to "Active" and "Inactive" / "Disabled", but the mapping isn't in the spec.

<Note>
  **⚠️ Spec gap:** The Webhook `Status` enum values (1, 2) are not labeled in the spec. Confirm the active/inactive mapping against live data before designing webhook management workflows that rely on specific status integer values.
</Note>

See [Webhooks Overview](/raise/webhooks/overview) for the broader webhook surface.

***

## How test mode propagates across resources

The `isTestMode` flag is set at the resource level but follows a natural propagation pattern:

```mermaid theme={null}
graph LR
  Donate["POST /api/Raise/give<br/>isTestMode: true"]
  Donor["Donor<br/>(isTestMode: true)"]
  Gift["Gift<br/>(isTestMode: true)"]
  RG["RecurringGift<br/>(isTestMode: true)"]

  Donate --> Donor
  Donate --> Gift
  Donate -.->|if isRecurring| RG
```

A donation submitted with `isTestMode: true` produces a test-mode Donor (if a new one is created), a test-mode Gift, and a test-mode RecurringGift (if the donation is recurring). Once a donor exists as test-mode, subsequent gifts to that donor continue to be test-mode unless the donor is explicitly converted.

Partner integrations doing production reporting should filter to `isTestMode: false` on every resource that supports the flag. A common implementation: apply the filter at the API client layer so every query automatically excludes test-mode data unless explicitly opted in.

```javascript JavaScript theme={null}
async function listProductionDonors() {
  return queryRaise('POST /api/Donor/query', {
    skip: 0,
    take: 1000,
    groups: [
      {
        conditions: [
          { parameter: 'isTestMode', operator: 0 /* equals */, value: 'false' },
        ],
      },
    ],
  });
}
```

***

## Summary table

| Resource      | Field              | Type                                       | Purpose                                                 |
| ------------- | ------------------ | ------------------------------------------ | ------------------------------------------------------- |
| Donor         | `isArchived`       | boolean                                    | Soft-deleted, hidden from active views                  |
| Donor         | `isGDPRDeleted`    | boolean                                    | GDPR-erased, tombstone retained                         |
| Donor         | `isTestMode`       | boolean                                    | Test-mode data                                          |
| Gift          | `status`           | integer enum (0–12)                        | Charge status — use `statusText` for display            |
| Gift          | `statusText`       | string                                     | Display string for status                               |
| Gift          | `canRefund`        | boolean                                    | Refund eligibility                                      |
| Gift          | `isAnonymous`      | boolean                                    | Donor opted to be anonymous                             |
| Gift          | `isTestMode`       | boolean                                    | Test-mode data                                          |
| RecurringGift | `status`           | integer enum (1–6)                         | Schedule status — use `statusText` for display          |
| RecurringGift | `statusText`       | string                                     | Display string for status                               |
| RecurringGift | `hasPaymentFailed` | boolean                                    | Most recent charge failed                               |
| RecurringGift | `isLegacy`         | boolean                                    | Legacy schedule                                         |
| RecurringGift | `frequency`        | integer enum (0, 1, 2, 4, 12, 26, 52, 100) | Schedule cadence — use `formattedFrequency` for display |
| RecurringGift | `isTestMode`       | boolean                                    | Test-mode data                                          |
| Campaign      | `canSync`          | boolean                                    | CRM sync enabled                                        |
| Campaign      | (no `isArchived`)  | —                                          | Campaigns use date ranges, not archive flags            |
| DonationForm  | `isLegacy`         | boolean                                    | Built on legacy form engine                             |
| Webhook       | `status`           | integer enum (1, 2)                        | Subscription active/inactive                            |

***

## Where to go next

<CardGroup cols={2}>
  <Card title="Donors" icon="user" href="/raise/concepts/donors">
    Donor archive, merge, and GDPR-erasure operations in detail.
  </Card>

  <Card title="Recurring Gifts" icon="arrows-rotate" href="/raise/concepts/recurring-gifts">
    The full RecurringGift lifecycle including failure detection and recovery.
  </Card>

  <Card title="How Raise Data Flows to CRM+" icon="arrow-right-arrow-left" href="/raise/concepts/data-flow-to-crm">
    How lifecycle states like `isGDPRDeleted` and `isArchived` should propagate to downstream systems.
  </Card>

  <Card title="Pagination and Filtering" icon="filter" href="/raise/pagination">
    Using `Filter` and `groups[]/conditions[]` to query by lifecycle state.
  </Card>
</CardGroup>
