The Contact hierarchy
A Contact in Virtuous represents a household or organization — not a person. The people, addresses, and contact methods inside a Contact are separate sub-resources.
The two sub-resources that trip up new partner integrations most often are ContactIndividual and ContactMethod:
- ContactIndividual is the person record. First name, last name, birth date, and gender live here — not on Contact.
- ContactMethod is the email or phone record. Email addresses live here — not on Contact or ContactIndividual as a top-level string.
Contact types
ThecontactType field on a Contact identifies what kind of record it is. Common values include Household, Organization, and Foundation, but the exact enum is configured per organization.
The CRM+ spec does not enumerate the valid
contactType values in the OpenAPI definition — the field is typed as string. To discover the values valid for a specific organization, call GET /api/Contact/Types. Cache the result; do not hardcode contact type strings in your integration.- Household Contacts represent a family unit. They have one or more ContactIndividuals (typically the adult householders), and gifts are attributed to the household even when one specific individual made the donation.
- Organization and Foundation Contacts represent institutions. They have ContactIndividuals for the relevant contact people (development director, program officer, etc.), and gifts are attributed to the institution.
Primary and secondary individuals
Within a household Contact, ContactIndividuals carryisPrimary and isSecondary flags. Exactly one individual is the primary; at most one is the secondary. Other individuals (children, extended family) are neither.
canBePrimary and canBeSecondary indicate whether an individual is eligible for those roles based on other organization settings.
Addresses
Each Contact has one or moreContactAddress records, exposed both as a top-level address field on the Contact (the primary address) and as a separate resource with its own ID for management.
Key fields:
A Contact can have multiple addresses (e.g., a seasonal/snowbird donor with a winter and summer address). The
isPrimary flag controls which address is used for mailings.
Tags, custom fields, and references
Three mechanisms let you attach additional information to a Contact:- Tags — simple labels (strings) used for ad-hoc grouping. A Contact can have any number of tags. Tags are organization-defined; see
GET /api/Contact/Tagsfor the list valid in the current organization. - Custom fields — typed, named fields configured by the organization. Used for structured data that doesn’t fit the standard schema (e.g., a wealth screening score, a communications preference). See the Custom Fields concept page.
- Contact references — external system identifiers stored on the Contact. Used to bridge your platform’s IDs with Virtuous IDs (e.g.,
source: "Stripe", id: "cus_abc123"). See Relationships and IDs.
contactReferences are particularly important for partner integrations — they enable looking up the Virtuous Contact by your platform’s ID via GET /api/Contact/ByReference/{referenceId}, removing the need to store the Virtuous Contact ID in your own database.
GET /api/Contact/ByReference/{referenceId} requires HMAC authentication rather than standard Bearer token auth. See Authentication for details — contact Virtuous support to obtain HMAC credentials if you need to use this lookup pattern.Statuses and lifecycle flags
Contacts have several boolean fields that affect how the record behaves in the system:
By default,
POST /api/Contact/Query excludes archived Contacts. Set includeArchived: true in the filter body to include them when reconciling deleted records or auditing historical data. See Statuses and Lifecycle States for the full lifecycle treatment.
Creating Contacts without producing duplicates
The single most important pattern in any Contact-write integration is never create blindly. The CRM+ API does not prevent duplicate Contacts on direct creation — if you POST a Contact for a donor who already exists in the system, you produce a second record with the same name and address as the first. Two strategies handle this correctly:Strategy 1: Use the Contact Transaction endpoint (recommended)
POST /api/Contact/Transaction submits a Contact for asynchronous import. The nightly batch run applies Virtuous’s matching algorithm — checking email, phone, address, name, and contactReferences against existing records — and creates a new Contact only if no match is found. If a match is found, the incoming data is merged into the existing record.
This is the path the Virtuous team explicitly recommends for partner integrations. It is described in detail on the Transactions concept page.
Strategy 2: Find-then-create
If you need synchronous Contact creation, the safe pattern is:GET /api/Contact/Find— attempt to locate an existing Contact by email or external reference (source + ID). Returns a single matched Contact if one exists. Acceptsemail,referenceSource, andreferenceIdas query parameters.POST /api/Contact/Query— ifFindreturns nothing, run a broader query (by name + postal code, for example) to catch fuzzy matches thatFinddid not.POST /api/Contact— create only if both lookups confirm no existing record matches.
Updating Contacts
The CRM+ API usesPUT /api/Contact/{contactId} for updates. The endpoint name suggests full-replacement PUT semantics, but in practice the live API behaves as PATCH — fields omitted from the request body are not cleared.
For the field-by-field update pattern and the safe GET-then-PUT workflow, see the Update a Contact workflow.
Created and modified timestamps
Every Contact carries metadata about when it was created and last modified:
The
modifiedDateTimeUtc field is the canonical anchor for incremental sync — query for Contacts with modifiedDateTimeUtc > {last_sync_run} to retrieve only records that have changed since your last run. See Query Contacts by Filters for the pattern.
Where to go next
Donations / Gifts
Gift records — what they contain and how they reference a Contact.
Transactions
The recommended Contact and Gift import pattern with built-in matching and deduplication.
Custom Fields
How nonprofits extend the Contact schema and how to read/write custom field values.
Relationships and IDs
Using
contactReferences to bridge your platform’s IDs with Virtuous Contact IDs.Create a Contact
A working example of the Contact create workflow with all required fields.
Query Contacts by Filters
Use
POST /api/Contact/Query for incremental sync and bulk retrieval.