Virtuous

Full CRM sync — Constituents and Gifts.

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:

  1. In the Donately dashboard, go to Integrations and click Install on the Virtuous card.
  2. Enter the email address and password you use to sign in to Virtuous CRM.
  3. 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.
  4. On success, you are returned to the Integrations page and the Virtuous card shows Active.
Credentials are never stored. Donately forwards your Virtuous email and password to the Virtuous token endpoint and discards them as soon as the access token is received. Only the access token is persisted.

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:

SectionDescription
Project LinkingDesignate gifts to a Virtuous Project based on the originating Donately form, campaign, fundraiser, or metadata
Missing Name FallbackHow to handle donors that have no first or last name (Virtuous rejects nameless contacts)
Custom Field MappingOverride the default mappings between Donately fields and Virtuous properties
Gift Defaults Coming Soon

Planned settings for future releases — not yet configurable from the dashboard:

SettingDescription
default_fund_idVirtuous fund or GL code applied to gifts when no specific designation is provided
default_gift_typeGift type written to all gifts (e.g., Cash, Credit Card, Online)
default_donor_typeConstituent 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.

This only matters in direct sync mode. In queue mode (/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 namePurpose
dntly_donation_idDonately's unique donation ID — used by Gift matching to find existing gifts on resync
dntly_transaction_idProcessor (Stripe/PayPal) transaction ID for the charge
dntly_donation_statusDonation status (processed, refunded, failed, etc.)
dntly_donation_processorPayment processor that handled the gift (e.g. stripe, paypal)
dntly_subscription_idDonately subscription ID if the gift is part of a recurring plan
dntly_subscription_frequencyRecurring frequency (monthly, yearly, etc.)
dntly_donation_campaignTitle of the Donately campaign the gift belongs to
dntly_donation_fundraiserTitle of the Donately fundraiser the gift belongs to
dntly_form_idDonately 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 FieldVirtuous Field
unique_identifierdntly_donor_id (custom)
emailemail
first_namefirst_name
last_namelast_name
phone_numberphone
street_addressaddress.street
street_address_2address.street2
cityaddress.city
stateaddress.state
zip_codeaddress.postal_code
countryaddress.country

Gift

Donately FieldVirtuous Field
unique_identifierdntly_donation_id (custom)
donation_namedescription
donation_amountamount
donation_datedate
currencycurrency
anonymousis_anonymous
on_behalf_oftribute.name
commentcomment
transaction_iddntly_transaction_id (custom)
processordntly_processor (custom)
donation_typedntly_donation_type (custom)
statusdntly_status (custom)
subscription_frequencydntly_subscription_frequency (custom)
subscription_iddntly_subscription_id (custom)
campaign_titledntly_campaign (custom)
fundraiser_titledntly_fundraiser (custom)
form_iddntly_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

StrategyDescription
FindByPersonIdPrimary match using the Donately donor ID stored in the dntly_donor_id custom field
FindByEmailFallback match by donor email address

Gift Matching

StrategyDescription
FindByDonationIdMatch 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 typeWhen it matchesRequired fields
formDonation came through a specific Donately formmatch_value (form unique_identifier), virtuous_project_id
campaignDonation belongs to a specific Donately campaignmatch_value (campaign unique_identifier), virtuous_project_id
fundraiserDonation belongs to a specific Donately fundraisermatch_value (fundraiser unique_identifier), virtuous_project_id
meta_dataA specific key/value pair appears in the donation's meta_datameta_key, match_value, virtuous_project_id
fallback_projectAlways — catches everything that didn't match an earlier rulevirtuous_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).

Precedence: project linking sets the gift's 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:

ValueBehavior
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.
skipRecord 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 properties
  • Donation — Maps donation fields to Virtuous Gift properties
Custom mappings are merged with the defaults. You only need to specify the fields you want to override — any fields not listed in your custom mapping will use the default mapping.
If you map something to the gift's projects field here AND have a Project Linking rule that matches, the project rule wins — see the precedence note in that section.