Skip to main content
Most partner integrations look correct on day one and degrade slowly over months as data quality issues accumulate. The integrations that stay clean for years share a small set of data-modeling decisions made deliberately at the start. This page consolidates those decisions into a single reference — drawn from the Concepts and Workflows pages but distilled into actionable guidance. The audience is the engineering team designing a new integration, or the team responsible for an existing integration that’s starting to develop data quality issues. None of this is binding — every customer has their own preferences — but each recommendation reflects what tends to work across many partner integrations.

Choose Contact types deliberately

The contactType field — Household, Organization, or Foundation — determines how Virtuous treats the record throughout the platform. Get this right at creation time; changing it later is operationally awkward.
Contact typeUse for
HouseholdIndividuals and family units. The default for most donor records. Includes one or more ContactIndividuals (spouses, partners, family members).
OrganizationFor-profit companies, businesses, corporate sponsors, vendor partners. Has organizational data (Tax ID, industry) rather than individual data.
FoundationGrant-making foundations and family foundations. A specialized Organization with grant-tracking conventions.
The wrong choice causes downstream pain:
  • Creating an Organization as a Household means the organization shows up alongside individuals in donor reports, distorting per-household metrics.
  • Creating a Household as an Organization means the individual lacks ContactIndividuals to capture their name, birth date, or personal preferences.
  • Creating a Foundation as a generic Organization misses the foundation-specific reporting Virtuous provides.

How to detect Contact type from your source data

Your source platform usually has enough signal to distinguish:
SignalLikely Contact type
Source record has a business_name field populated, no individual first/last nameOrganization
Source records carry IRS Form 990 / Schedule I data, or have grant-tracking conventionsFoundation
Default for everything elseHousehold
For ambiguous cases (a sole proprietor giving from a business name with their personal email), default to Household and tag the record "Business-Donor" for the customer’s team to review and reclassify if needed.

Use a stable, predictable external reference

The referenceSource + referenceId pair is the single most important data-modeling decision for a partner integration. Every other matching, deduplication, and reconciliation pattern depends on it. Three rules for the reference:
RuleWhy
referenceSource is a fixed string per integration.Use the same value (e.g., "Stripe", "Mailchimp", your platform’s name) for every record your integration writes. Never vary by customer or environment.
referenceId is the source platform’s stable primary key.Use the Stripe Customer ID, the Mailchimp Subscriber Hash, your platform’s user ID. Not the email, not a generated UUID, not the order ID — the persistent identifier for the entity itself.
The pair is unique across all your integration’s writes.One donor on your platform should have exactly one (source, id) pair in Virtuous, even if they exist in multiple places on your platform.

What “stable” means in practice

A reference is stable if it doesn’t change when the entity’s other attributes change. Specifically:
  • Email is not stable — donors change their email.
  • Phone is not stable — donors change their phone.
  • Name is not stable — donors change their name (marriage, transition).
  • Address is not stable — donors move.
  • Stripe Customer ID is stable — once issued, it identifies the same customer for their lifetime on Stripe.
  • Your platform’s user ID is stable — assuming you use a real primary key, not a hash of mutable data.
The reference must survive every other field changing on the record.
A common partner integration mistake: using the donation’s order ID as the Contact’s referenceId. The order ID is unique per order, so the same donor giving twice produces two Contacts. Use the donor’s stable identifier (Customer ID, User ID) for Contact references; use the order or charge ID for Gift transactionId.

When the source doesn’t have a stable ID

Some source platforms (older systems, CSV imports, manual data entry) don’t expose a stable identifier. Two patterns:
  • Generate one once and persist it. On first encounter, generate a UUID and store it alongside the source record. Use the UUID as referenceId on every subsequent submission. This works for any source with a writable database on your side.
  • Use a deterministic hash. Hash the most stable subset of fields (e.g., lowercase(email) + lowercase(lastname)) to produce a reproducible ID. Less robust because the inputs can still change, but workable for read-only sources where you have no writable storage.

Project codes are the API’s primary key for designations

Projects are the destination for Gift designations. Their projectCode field is the partner-friendly API identifier — short, human-readable, stable. Three best practices:
PracticeWhy
Use upper-case alphanumeric codes with hyphens.CLEAN-WATER, GEN-FUND, ANNUAL-2024. Easy to type, easy to read in logs, doesn’t collide with anything else.
Avoid embedding the year unless the Project is genuinely year-specific.GEN-FUND is reusable; GEN-FUND-2024 becomes stale next year. Year-specific codes belong on Campaign-tied Projects (e.g., MARATHON-2024).
Document the mapping from your platform’s destinations to Virtuous Project codes at integration setup.Cache the mapping in your integration’s configuration. Validate against GET /api/Project/Query at startup and after any customer-initiated change.

Splitting designations: when to use multiple

A single Gift can be split across multiple Projects via the giftDesignations[] array. Use a split when:
  • The donor explicitly specifies multiple destinations.
  • The source platform records a multi-Project intent (e.g., “10% to admin, 90% to programs”).
  • The customer’s policy automatically allocates portions to specific funds (e.g., “5% of every gift to an endowment”).
Don’t use a split as a workaround for missing designations. If you don’t know which Project the gift belongs to, don’t split it across multiple defaults — flag it for the customer’s team to resolve.

Custom fields vs. tags: a decision tree

The two main ways to add structure beyond Virtuous’s built-in fields. They serve different purposes.
Use a tag whenUse a custom field when
The value is essentially boolean: “is this donor in segment X or not?”The value is structured data: a date, number, dropdown selection, or text.
Many records share the same value, and you want to filter by it.The value varies per record and represents a specific attribute.
The category is owned by the customer’s marketing/segmentation team.The category is owned by the customer’s operations or finance team.
The list of possible values is short and stable.The list of possible values changes frequently or is unbounded.

Examples

ConceptTag or custom fieldWhy
”Major Donor” classificationTagBoolean segmentation, owned by the team that uses it.
Donor’s preferred greetingCustom fieldPer-record data, structured, owned by communications.
Subscription source (Mailchimp, Constant Contact)Tag with prefixBoolean per source; useful for segmentation.
Last platform sync timestampCustom fieldTime-series data, per-record, used for diagnostics.
Subscriber engagement tierCustom fieldMulti-valued (Bronze/Silver/Gold), structured.

Tag naming conventions

When your integration writes tags, use a consistent prefix that identifies the source:
PrefixMeaning
MC:From Mailchimp
CC:From Constant Contact
Stripe:From Stripe
Auto:Automated tag added by any integration
No prefixCustomer-managed tags
The prefix lets the customer’s team visually distinguish “tags my staff applied” from “tags some integration applied” — and lets your integration code filter for “my tags” when synchronizing back to the source.

Capture, don’t infer, source-of-record data

A frequent partner integration mistake: re-deriving source-of-record fields from data the integration doesn’t authoritatively own. For example, computing a donor’s giving total in your integration’s database when Virtuous already calculates lifeToDateGiving. The rule: store what your platform owns; query what Virtuous owns.
Your platform ownsVirtuous owns
The donor’s identity on your platform (Stripe Customer ID, your User ID)The donor’s identity inside Virtuous (Contact ID)
The specific donation event metadata (Stripe charge ID, payment method)The Gift record, its designations, premiums, and tax-deductible status
Your platform’s tags, lists, segmentsThe Contact’s tags inside Virtuous
Your platform’s calculated metrics for your platform’s purposesVirtuous’s calculated metrics (giving totals, modification timestamps)
When the customer wants a report that mixes both — “donors on the Mailchimp Gold list who gave more than $1,000 last year” — pull the giving totals from Virtuous on demand rather than syncing them into your platform and risking staleness.

Designate ownership of each field per integration

In a multi-integration customer environment, multiple systems may write to the same Contact. Without explicit field ownership, two integrations will overwrite each other’s data. The pattern: document which fields each integration owns, both within your integration’s documentation and (ideally) in the customer’s runbook for managing their Virtuous setup. A typical breakdown for a fundraising-platform integration:
FieldOwned byNotes
name, firstName, lastNameVirtuous UI (the customer’s staff)Donor name corrections happen in Virtuous.
email, phone (primary)Source platformUpdates flow in from the source.
email, phone (additional)Virtuous UIThe customer’s staff curates these.
tags (with MC: prefix)Mailchimp integrationRound-tripped via the Mailchimp recipe.
tags (other)Virtuous UINot touched by integrations.
customFields["External Donor ID"]Source platformSet on creation, never modified.
customFields["Donor Segment Tier"]Virtuous UICustomer staff sets this during cultivation.
Your integration’s code enforces this — outbound writes only modify fields you own. Inbound reads (webhook handlers) apply changes only to fields the source platform owns. See Build a Two-Way Sync — Pattern 2: per-field ownership for the implementation.

Capture audit metadata at creation, not in narration

Several pieces of metadata are useful for diagnostics later: when did the integration first sync this record, which version of the integration code wrote it, what was the source event. The natural impulse is to put these in ContactNotes — but notes are designed for human-readable observations, not machine-readable diagnostics. Better practice: capture diagnostic metadata as custom fields, not notes.
JavaScript
{
  customFields: [
    { name: 'Integration Source', value: 'YourPlatform' },
    { name: 'Integration Version', value: '2.1.4' },
    { name: 'First Synced At', value: '2024-12-15T14:30:00Z' },
    { name: 'Source Event ID', value: 'evt_abc123' },
  ]
}
These fields are queryable, exportable, and don’t clutter the Notes view that the customer’s staff actually reads. Reserve ContactNotes for content that a human would want to see — interaction history, payment failures, manual flags.

Use Relationships sparingly and only when configured

Virtuous Relationships connect two Contacts (donor and spouse, parent and child, donor and recruiting fundraiser). They’re powerful but easy to misuse. Two rules:
RuleWhy
Only create Relationships your integration genuinely owns.Don’t auto-create “Spouse” relationships from address sharing — let the customer’s staff make those calls.
Only create Relationships whose type is configured in Virtuous.Discovered via GET /api/Relationship/Types. If the customer hasn’t configured the type, skip the relationship and log it.
Relationships are a “two-way edge in the social graph” — a wrong relationship is more confusing than no relationship. When in doubt, capture the linkage in a custom field instead and let the customer’s team formalize it as a Relationship if they want.

Plan for the customer’s reporting needs

The customer’s team will eventually want to build reports against the data your integration writes. Three things make their work easier:
  • Consistent tags and custom field values. “Major Donor” and “Major-donor” and “major_donor” are three different segments to Virtuous Query, even though they’re the same intent to a human.
  • Predictable Project codes. The customer should be able to write parameter: "Project", operator: "Is", value: "CLEAN-WATER" and know it returns the right gifts. Cryptic codes (P-2024-0317) frustrate this.
  • Useful originSegmentCode values. This field captures “how did this Contact first enter Virtuous?” — set it to a stable, descriptive value ("FUNDRAISING-PLATFORM", "STRIPE-DONATION", "MAILCHIMP-SIGNUP") so reports can split donors by acquisition channel.
Confirm the customer’s preferred values for these during onboarding. Document them in your integration’s configuration so they’re explicit, not buried in code.

Where to go next

API Performance Tips

How to keep your integration fast and within rate limits at scale.

Error Recovery Patterns

The resilience patterns that complement good data modeling.

Custom Fields

The reference page for custom field reads and writes.

Funds, Campaigns, and Designations

The deeper reference for the Project hierarchy and designation structure.
Last modified on May 21, 2026