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.| Resource | Cardinality | What it is |
|---|---|---|
| Contact | The top-level record | A household (e.g., “The Wayne Family”) or an organization (e.g., “Wayne Enterprises”). |
| ContactIndividual | Many per Contact | A person inside the Contact — first name, last name, birth date, gender, etc. |
| ContactAddress | Many per Contact | A postal address belonging to the Contact (one is the primary). |
| ContactMethod | Many per ContactIndividual | An email or phone number belonging to a specific ContactIndividual. |
- 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.
| Flag | Meaning |
|---|---|
isPrimary: true | The main contact for the household. Receipts, communications, and gift attributions default to this individual. |
isSecondary: true | The partner/spouse role. Some communications and joint-giving displays include this individual. |
| Neither flag set | Other household members (e.g., children). Stored but not the default recipient for communications. |
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:
| Field | Type | Description |
|---|---|---|
id | integer | The unique address ID. Required for updates. |
label | string | The address’s display label (e.g., “Home”, “Work”, “Seasonal”). |
address1, address2 | string | Street address lines. |
city, state, postal, country | string | Standard postal components. |
isPrimary | boolean | Whether this is the Contact’s primary address. |
canBePrimary | boolean | Whether this address is eligible to be primary (e.g., contains required fields). |
startMonth, startDay, endMonth, endDay | integer | Seasonal address date ranges. |
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:| Field | What it means |
|---|---|
isPrivate | The Contact is hidden from most users — visible only to administrators. Typical for high-profile donors or anonymous gifts. |
isArchived | The Contact has been archived. Excluded from most queries by default. |
isDeceased (on ContactIndividual) | The individual has died. The Contact may still be active if other individuals remain. |
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:| Field | Type | Description |
|---|---|---|
createDateTimeUtc | dateTime | ISO 8601 timestamp of when the Contact was created. UTC. |
createdByUser | string | Name of the user who created the record. |
modifiedDateTimeUtc | dateTime | ISO 8601 timestamp of the most recent modification. UTC. |
modifiedByUser | string | Name of the user who most recently modified the record. |
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.