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

# Project Mapping

> How WeGive funds map to Virtuous projects, in both directions

# Project Mapping

WeGive **Funds** map to Virtuous **Projects**. This mapping is intentionally minimal: push is create-only, and pull carries a handful of fields plus archival logic.

## Record Model

| WeGive record | Virtuous record | Correlation column |
| - | - | - |
| Fund | `Project` | `virtuous_id` |

## Push (WeGive → Virtuous)

Funds are pushed with `POST Project` **only when they don't yet have a `virtuous_id`**. Once a Fund is linked to a Virtuous Project, the integration does **not** update it — push is create-only.

```
POST Project
{
  "name": <fund.name>,
  "revenueAccountingCode": "<organization_id>:<fund_id>",
  "createDateTimeUtc": <fund.created_at, ISO 8601>,
  "InventoryStatus": "Unspecified",
  "Type": "Unspecified",
  "Location": "Unspecified"
}
```

| Virtuous field | Source |
| - | - |
| `name` | `fund.name` |
| `revenueAccountingCode` | `<organization_id>:<fund_id>` (used to correlate back to WeGive) |
| `createDateTimeUtc` | `fund.created_at` |
| `InventoryStatus` / `Type` / `Location` | constant `"Unspecified"` |

The returned `id` is stored as the Fund's `virtuous_id`. No goal, description, or project-type fields are sent.

## Pull (Virtuous → WeGive)

Projects are pulled via `POST Project/Query` (1000 per page, filtered by the configured `pull_by` date) and imported as Funds, matched on `virtuous_id`:

| WeGive field | Source |
| - | - |
| `virtuous_id` | project `id` |
| `name` | `onlineDisplayName`, falling back to `name` |
| `code` | `projectCode` |
| `created_at` | existing value, else `createDateTimeUtc` |

### Archival

A pulled Project is soft-deleted (archived) in WeGive when an archival rule is enabled and the matching Virtuous flag is false:

| Integration setting | Archives when |
| - | - |
| `archive_funds_when_inactive` | `isActive` is `false` |
| `archive_funds_when_not_public` | `isPublic` is `false` — **unless** `sync_not_public_funds` is enabled (see below), in which case a not-public project syncs as a confidential fund instead of being archived |
| `archive_funds_when_not_available_online` | `isAvailableOnline` is `false` |

If none of the enabled rules match, the Fund is restored (its `deleted_at` is cleared).

### Not-public funds: confidential mode instead of archival

When `sync_not_public_funds` is enabled, a Project with `isPublic: false` doesn't get archived by the `archive_funds_when_not_public` rule above — instead it syncs as a **confidential** WeGive fund:

* `name` is set to the real (internal) Virtuous project name, not the donor-facing one
* `masked_name` is set to the project's Online Display Name, if it has one
* `confidential` is set to `true`
* `hide_from_donors` is set to `true` whenever the project is also not available online, has no Online Display Name to mask with, or the integration's `not_public_fund_mode` isn't set to "visible masked"

<Note>
  A fund with no Online Display Name to mask behind fails closed — it becomes fully donor-inaccessible via WeGive's own fund-access gate, regardless of what `not_public_fund_mode` is set to.
</Note>

If a previously-confidential project later becomes publicly visible again, its `masked_name`/`confidential` flags clear automatically on the next pull — but `hide_from_donors` is left alone, since unhiding a fund is treated as a deliberate staff action, not something a sync should do on its own.

This reflects the integration as implemented in `app/Integrations/Virtuous.php`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.