Overview
The Virtuous integration syncs Donately donor and donation data to Virtuous CRM Constituents and Gifts. It supports custom field mapping and recurring gift tracking.
Once connected, syncs are triggered automatically each time a donation is created, updated, or refunded in Donately.
What Gets Synced
- Constituent — Donor identity, address, and contact information
- Gift — Each donation, including recurring subscription details
Connecting
Virtuous does not offer a standard OAuth authorization screen. Instead, it uses password-based authentication (OAuth 2.0 Resource Owner Password Credentials) to exchange your Virtuous username and password for an access token, which Donately stores and uses for all subsequent syncs.
To connect Virtuous to Donately:
- In the Donately dashboard, go to Integrations and click Install on the Virtuous card.
- Enter the email address and password you use to sign in to Virtuous CRM.
- If your Virtuous account has multi-factor authentication enabled, you will be prompted for a one-time code after submitting your credentials. Enter the code Virtuous sends to your authenticator app or email and click Connect again.
- On success, you are returned to the Integrations page and the Virtuous card shows Active.
Configuration
Per-account settings live on the Virtuous card's Configure page in the Donately dashboard (/native-integrations/virtuous/configure). Three sections are configurable today:
| Section | Description |
|---|---|
| Project Linking | Designate gifts to a Virtuous Project based on the originating Donately form, campaign, fundraiser, or metadata |
| Missing Name Fallback | How to handle donors that have no first or last name (Virtuous rejects nameless contacts) |
| Custom Field Mapping | Override the default mappings between Donately fields and Virtuous properties |
Planned settings for future releases — not yet configurable from the dashboard:
| Setting | Description |
|---|---|
| default_fund_id | Virtuous fund or GL code applied to gifts when no specific designation is provided |
| default_gift_type | Gift type written to all gifts (e.g., Cash, Credit Card, Online) |
| default_donor_type | Constituent type applied to new constituents (e.g., Individual, Organization) |
Pre-installed Custom Fields
Donately's default field mappings rely on a small set of Gift custom fields existing in Virtuous so each gift can carry the Donately donation, transaction, subscription, campaign, fundraiser, and form identifiers alongside the standard amount/date/donor data. These fields are normally created once at integration installation time — either by the Donately team during onboarding or by your Virtuous admin from the list below.
/api/Gift/Transaction), Virtuous auto-creates any custom field it sees on approval, so nothing needs to be pre-installed. In direct mode (/api/Gift), Virtuous silently drops any custom field whose name isn't already defined under Setup → Custom Fields → Gift — the gift itself still saves, but the dropped fields won't appear on the gift in Virtuous.If you need to (re)create them by hand, add each one in Virtuous under Setup → Custom Fields → Gift as type Text:
| Field name | Purpose |
|---|---|
dntly_donation_id | Donately's unique donation ID — used by Gift matching to find existing gifts on resync |
dntly_transaction_id | Processor (Stripe/PayPal) transaction ID for the charge |
dntly_donation_status | Donation status (processed, refunded, failed, etc.) |
dntly_donation_processor | Payment processor that handled the gift (e.g. stripe, paypal) |
dntly_subscription_id | Donately subscription ID if the gift is part of a recurring plan |
dntly_subscription_frequency | Recurring frequency (monthly, yearly, etc.) |
dntly_donation_campaign | Title of the Donately campaign the gift belongs to |
dntly_donation_fundraiser | Title of the Donately fundraiser the gift belongs to |
dntly_form_id | Donately form ID the donor used |
You only need the fields you want surfaced on the gift in Virtuous — any field you skip will be omitted from gift payloads. dntly_donation_id is the one strict requirement, since gift matching uses it to avoid creating duplicate gifts on every resync.
Field Mapping
Default mappings from Donately fields to Virtuous properties. These can be overridden using custom field mapping.
Constituent
| Donately Field | Virtuous Field |
|---|---|
| unique_identifier | dntly_donor_id (custom) |
| first_name | first_name |
| last_name | last_name |
| phone_number | phone |
| street_address | address.street |
| street_address_2 | address.street2 |
| city | address.city |
| state | address.state |
| zip_code | address.postal_code |
| country | address.country |
Gift
| Donately Field | Virtuous Field |
|---|---|
| unique_identifier | dntly_donation_id (custom) |
| donation_name | description |
| donation_amount | amount |
| donation_date | date |
| currency | currency |
| anonymous | is_anonymous |
| on_behalf_of | tribute.name |
| comment | comment |
| transaction_id | dntly_transaction_id (custom) |
| processor | dntly_processor (custom) |
| donation_type | dntly_donation_type (custom) |
| status | dntly_status (custom) |
| subscription_frequency | dntly_subscription_frequency (custom) |
| subscription_id | dntly_subscription_id (custom) |
| campaign_title | dntly_campaign (custom) |
| fundraiser_title | dntly_fundraiser (custom) |
| form_id | dntly_form_id (custom) |
Record Matching
The integration uses the following strategies to find existing records before creating new ones. Strategies are tried in order — the first match wins.
Constituent Matching
| Strategy | Description |
|---|---|
| FindByPersonId | Primary match using the Donately donor ID stored in the dntly_donor_id custom field |
| FindByEmail | Fallback match by donor email address |
Gift Matching
| Strategy | Description |
|---|---|
| FindByDonationId | Match by Donately donation ID stored in the dntly_donation_id custom field |
Advanced
Project Linking
Each Virtuous gift can be designated to a Project — Virtuous's accounting object for funds, programs, or campaigns. Donately picks the Project for each gift by walking an ordered list of rules. Rules are evaluated top-to-bottom, and the first matching rule wins. A donation that doesn't match any rule will not be designated to a Project unless you add a fallback rule.
Each rule has a type and the fields specific to that type:
| Rule type | When it matches | Required fields |
|---|---|---|
form | Donation came through a specific Donately form | match_value (form unique_identifier), virtuous_project_id |
campaign | Donation belongs to a specific Donately campaign | match_value (campaign unique_identifier), virtuous_project_id |
fundraiser | Donation belongs to a specific Donately fundraiser | match_value (fundraiser unique_identifier), virtuous_project_id |
meta_data | A specific key/value pair appears in the donation's meta_data | meta_key, match_value, virtuous_project_id |
fallback_project | Always — catches everything that didn't match an earlier rule | virtuous_project_id |
Order rules from most specific to least specific. If you use a fallback_project rule, put it last so it only fires when nothing earlier matched.
Example settings.project_rules:
[
{ "type": "form", "match_value": "frm_abc123", "virtuous_project_id": 12345 },
{ "type": "meta_data", "meta_key": "appeal", "match_value": "endofyear", "virtuous_project_id": 67890 },
{ "type": "campaign", "match_value": "cmp_xyz789", "virtuous_project_id": 11111 },
{ "type": "fallback_project", "virtuous_project_id": 99999 }
]
The matched rule's virtuous_project_id is sent on the gift payload as projects: [{ projectId, amountDesignated }] with amountDesignated equal to the full gift amount (multi-project splits are not yet supported).
projects array directly after the gift payload is built from the field mappings. If a custom mapping override also writes to projects, the project rule wins — the rule's Project ID is what reaches Virtuous.Missing Name Fallback
Virtuous's contact API requires both firstName and lastName on every constituent — a create or update with either field blank is rejected with a 400. Donately, however, accepts donors with no name (anonymous donors, single-field donation forms, mobile flows that only ask for email). Without a fallback strategy those donors and their gifts would be silently dropped on every sync attempt.
Two strategies are available, configured via settings.missing_name_fallback:
| Value | Behavior |
|---|---|
email (default) | Split the donor's email at the last @ and use each side as a placeholder. jane@acme.org becomes firstName: "jane", lastName: "acme.org". The gift still syncs. Each fallback fires an account.integration.run.warning Event with warning_type: "NAME_FALLBACK_APPLIED" so the team can subscribe a Notification, patch the donor record in Donately, and re-run the integration to replace the placeholder. |
skip | Record an upsert_contact_skipped failure on the integration run. The gift is not synced. The run's failure summary fires account.integration.run.failure at the end. Pick this if a hard signal in the run history is more useful than a placeholder contact in Virtuous. |
Either way, if the donor has no name and no usable email (blank, or no @), the integration falls back to recording the skip — there's nothing to synthesize from.
Custom Field Mapping
You can override the default field mappings with a custom mapping. Custom mappings are defined as a JSON object where keys are Donately field names and values are the target Virtuous property names.
{
"Person": {
"email": "email",
"unique_identifier": "dntly_donor_id",
"phone_number": "phone"
},
"Donation": {
"donation_amount": "amount",
"unique_identifier": "dntly_donation_id"
}
}
Person— Maps donor fields to Virtuous Constituent propertiesDonation— Maps donation fields to Virtuous Gift properties
projects field here AND have a Project Linking rule that matches, the project rule wins — see the precedence note in that section.