crmKey — determine whether the data tells a coherent story years later. Partner integrations are often the first place where these structural decisions show up as friction: a reporting integration that can’t find the gift it expects, a sync that produces duplicate donors, an analytics dashboard that can’t slice by the dimension the customer wants.
This page covers the modeling patterns that produce clean Raise data and the decisions to make explicitly during integration onboarding rather than discovering later.
The audience is partner integration leads helping a customer think through their Raise setup, and partner engineering teams whose integrations depend on the data shape being predictable.
The five modeling decisions that matter most
In rough order of impact on integration design:| Decision | Why it matters |
|---|---|
| How donors are matched and identified | Affects every downstream sync, deduplication, and donor-level reporting |
| Campaign / Segment / Form structure | Determines whether per-channel attribution is meaningful or messy |
| Project (designation) taxonomy | Drives the reporting axis for revenue tracking |
| Custom field placement and naming | Adds dimensions partner integrations can use; gets messy if treated as a junk drawer |
| CRM key seeding | Whether cross-product reconciliation is easy or hard |
Decision 1: donor matching
The most consequential modeling decision: how donors are identified and matched across submissions.Email is the de facto primary key
Raise’s donor-matching algorithm — embedded inPOST /api/Raise/give and in donor lookups — primarily matches on email. The implications:
- Donors with the same email collapse into one record. When a donor gives twice and uses the same email both times, both gifts attach to one Donor.
- Donors who change emails effectively become new donors. A donor who gave as
bruce@wayne.examplelast year and gives asbruce.wayne@updated.examplethis year will produce two distinct Donor records unless the integration explicitly reconciles them. - Donors who use a household email shared with a partner can produce ambiguous matches. “Bruce and Selina Wayne” giving from a shared email may produce one donor record that represents both — or different records if the form captures both names.
Practices that handle these cases well
For partner integrations interacting with donor records:| Practice | Description |
|---|---|
| Use email as the matching key for new donor submissions, matching the platform’s behavior | Avoids creating fragmented donor records |
| Run periodic deduplication checks to catch cases where the same person has multiple records | Especially valuable after email migrations or major account changes |
Use PUT /api/Donor/merge for confirmed duplicates | The platform’s native merge handles the data fold-down |
| Surface ambiguous matches for human review | When two records share characteristics but not email, the integration shouldn’t decide automatically |
| Handle the household case explicitly | Either treat shared-email gifts as one donor (default) or capture both names in the donor record and decide downstream |
Organization vs. individual donors
TheisOrganization flag distinguishes a corporate or foundation donor from an individual. Set it correctly at submission time:
JavaScript
organizationName is set, or an organization whose firstName is “Wayne” and lastName is “Foundation”) produces messy reporting and odd-looking thank-you emails.
Decision 2: Campaign and Segment structure
Campaigns and Segments are how the customer reports on revenue by appeal, channel, and time period. The structure determines whether reports answer the questions the customer actually asks.The hierarchy
- Campaign = the umbrella effort (e.g., “2025 Annual Appeal”, “Capital Campaign Phase 2”)
- Segment = the channel or wave within a campaign (e.g., “Email — Q1 2025”, “Direct Mail — March”)
- Form = the specific donation form (e.g., “Annual Appeal Online Form”, “Donor Renewal Form”)
- All gifts to the 2025 Annual Appeal (campaign level)
- All gifts driven by email in Q1 2025 (segment level)
- All gifts from a specific form (form level)
Practices for designing the hierarchy
| Practice | Description |
|---|---|
| Time-bound campaigns | Most campaigns have a start and end date. A “2025 Annual Appeal” closes when 2025 ends; “2026 Annual Appeal” begins anew. |
| Channel-based segments | Segments cleanly map to channels — email, direct mail, social, paid. This makes “channel performance” reports straightforward. |
| Sub-channel segments for major channels | If email is a primary channel, sub-divide by audience or appeal — “Email — Lapsed Donors Q1”, “Email — Major Donors Q1”. |
| Form per segment is acceptable when forms are channel-specific | A form embedded in an email blast can be a one-off segment; a general form serving multiple channels typically has one segment per channel using URL parameters. |
| Resist creating campaigns for everything | Campaigns are heavy; not every appeal needs its own. Use Segments for sub-divisions within an ongoing campaign. |
Anti-pattern: segments as Projects
A common mistake: using Segments as a substitute for Projects (designations). Segments are about how the donor came in; Projects are about what the donor funded. Don’t conflate them — a gift to the “General Fund” Project that arrived through the “Email Q1” Segment can answer both “how much did email drive?” and “how much went to general operations?” Separately, you get clean intersections.Working with the structure in partner integrations
When reading gifts for analytics, the attribution fields onGiftModel provide direct access:
JavaScript
Decision 3: Project taxonomy
Projects represent the funding destinations gifts designate to. The taxonomy — what Projects exist, how granular they are, how they evolve over time — determines what reports the customer can produce.Two opposing pressures
| Pressure | Symptom of going too far |
|---|---|
| Granularity (more Projects = finer reporting) | Hundreds of Projects, none with meaningful giving, hard to maintain |
| Aggregation (fewer Projects = simpler reporting) | One “General Fund” Project hides all the program-level revenue distinctions the customer needs |
Practices for Project taxonomy
| Practice | Description |
|---|---|
| Start with the customer’s existing program structure | Their accounting team or development office typically has a working program list. Map it 1:1 first. |
| Aggregate at the right level | If the customer reports on five programs internally, have five Projects (not 50 sub-programs). |
| Use one Project per restricted-giving destination | Restricted gifts need their own Projects for accounting compliance. |
| Keep “General Fund” or equivalent as a Project | The default destination for unrestricted gifts. |
| Don’t reuse Project IDs across reorganizations | When a program closes, archive the Project (don’t repurpose its ID for a new program). |
Project codes vs. names
Each Project has both a name (display) and a code (stable identifier used inprojectOverrideCode URL parameters and similar). The code should:
- Be short and code-like (
GOTHAM-OUTREACH, notGotham Outreach Program 2025). - Be stable across the Project’s lifetime — don’t rename it as the program evolves.
- Be unique across the organization.
projectOverrideCode=OLD-CODE breaks. Treat codes as part of the integration’s public contract.
Working with Projects in partner integrations
For analytics integrations, the project allocation is ingift.projects[]:
JavaScript
projects[] array on a 100.
Decision 4: Custom Fields
Custom Fields capture data that doesn’t fit the standard schema — donor preferences, gift-time questions, organizational metadata. Used well, they add dimensions to reporting and integration logic. Used poorly, they become a junk drawer of inconsistent values.Practices for Custom Field design
| Practice | Description |
|---|---|
| Treat Custom Fields like database schemas | Each field has a clear name, type, expected values, and purpose. Document these before creating fields. |
| Use Custom Fields on the right resource | A field about the donor (preferred communication method) belongs on the Donor; a field about the gift (event ticket type) belongs on the Gift. |
| Avoid free-text where an enum would work | A “Source” custom field with values like “Google Ads”, “google ads”, “Google ads”, “Google” produces messy reports. Use a constrained list. |
| Don’t create Custom Fields for fields the standard schema already supports | Don’t add a “Donor Phone” Custom Field — use the standard phone field and donorPhoneNumbers[] sub-resource. |
| Plan for evolution | Add a “deprecated” prefix or naming pattern for fields you’re phasing out so integrations know to ignore them. |
Working with Custom Fields in partner integrations
For reading Custom Field values on a Donor:cURL
cURL
Don’t use Custom Fields for everything
A common anti-pattern: treating Custom Fields as the universal extensibility mechanism for every integration’s needs. A partner integration that creates 50 Custom Fields on every customer’s account pollutes the customer’s data model — the customer sees these in their admin UI and has no idea what they’re for. For partner-internal state (sync metadata, processing flags), keep the data in your own database. Use Custom Fields only when:- The data is meaningful to the customer’s reporting or staff workflow.
- The customer would benefit from seeing the field in the Raise admin UI.
- The data belongs to the donor or gift, not to the integration.
Decision 5: CRM key seeding
For customers running Raise alongside CRM+ (or another external CRM), thecrmKey, crmSecondKey, and crmKeyUrls fields link Raise records to their counterparts in the external system. See How Raise Data Flows to CRM+.
The decision: when (if ever) should the partner integration seed these values explicitly versus letting the platform sync populate them?
When to seed crmKey explicitly
| Scenario | Approach |
|---|---|
| Partner is creating a Raise record from data that originated in CRM+ | Seed crmKey with the CRM+ Contact ID at creation time |
| Partner is creating a Raise record matched to an external CRM identifier the customer has in their hand | Seed crmKey with that identifier |
| Partner is creating a Raise record with no external counterpart yet | Don’t seed; let the platform sync populate it after the fact |
When not to seed
| Scenario | Why |
|---|---|
| The partner doesn’t know the corresponding CRM+ Contact ID | Don’t guess; let the sync find or create the right Contact |
| The customer’s CRM+ account is new or empty | Sync will populate crmKey once the Contact is created |
| The partner integration is read-only against Raise | No need to write linkage data |
Reading vs. writing crmKey
Reading crmKey is always useful — it tells the partner integration whether the record has been synced and which external Contact it links to. Writing crmKey only matters when the partner has authoritative knowledge of the linkage.
JavaScript
Test mode and production separation
Throughout the data model,isTestMode flags appear on submissions, donors, gifts, and recurring gifts. Treat test mode as a first-class data axis:
| Practice | Description |
|---|---|
Filter production reports to isTestMode: false | Test data should never appear in donor counts, revenue totals, or campaign performance |
| Tag test-mode usage clearly | Use distinct donor email domains (test@example.com, dev+timestamp@example.com) so test data is easy to identify |
| Run dedicated test-mode donations during development | Don’t develop against production data; use the test-payment-method generator |
| Clean up test-mode data periodically | The customer’s admin team can archive or delete accumulated test records |
isTestMode: true records out of downstream syncs unless explicitly running in a development environment. See Sync Raise Gifts to an External System: Test-mode in production.
Modeling pitfalls to avoid
A few specific anti-patterns that produce messy data:Pitfall 1: one Project for everything
Every gift designating to “General Fund” produces a single revenue line in reports. The customer can’t slice by program, can’t show donors what their gift funded specifically, can’t distinguish unrestricted from restricted giving. Add at minimum 5–10 Projects to cover the major programs.Pitfall 2: a Campaign per appeal
The opposite problem: every email blast is a new Campaign. Hundreds of Campaigns accumulate, most with a few gifts each, and the “all campaigns” view becomes unreadable. Use Segments for sub-divisions; reserve Campaigns for the major efforts.Pitfall 3: Custom Fields as a junk drawer
A customer accumulates 50+ Custom Fields over the years, most with inconsistent values, half of them used by integrations that no longer exist. Periodically audit Custom Fields and archive the ones that aren’t actively used.Pitfall 4: not setting isOrganization
A foundation giving $50,000 with isOrganization: false and firstName: "Wayne" lastName: "Foundation" looks weird in reports and breaks any logic that filters individual vs. organizational donors. Always set the flag correctly.
Pitfall 5: using free-text where a Project would do
“What did the donor designate to?” captured in a free-text Custom Field rather than as a Project allocation produces unstructured data. Use Projects for designation; Custom Fields for the things that genuinely don’t fit standard fields.Pitfall 6: keeping test-mode donors in production reports
A common reporting issue: the customer’s “all donors” count includes hundreds of test-mode records from development work. FilterisTestMode: false everywhere production-facing.
Onboarding a new customer
For partner integrations onboarding a new customer, walk through these decisions together:Audit the customer's existing data shape
How many Campaigns? Segments? Projects? Are the conventions consistent? Are there orphaned Custom Fields?
Document the canonical taxonomy
Write down what Campaigns and Projects exist, what they mean, and what new ones should be added. This becomes the integration’s contract.
Set the integration's expectations
“Our integration filters out test-mode gifts.” “Our integration assumes Projects have stable codes.” Make implicit expectations explicit.
Plan for Custom Field needs
If the integration needs Custom Fields, propose them with clear names and types — and confirm the customer wants them in their data model.
Coordinate on `crmKey` policy
If the customer also runs CRM+, agree on who’s responsible for what — the partner integration may or may not need to seed linkage data.
Where to go next
API Performance Tips
The performance practices that make integrations against this data model efficient.
Sync Architecture Patterns
How to design integrations that move data between Raise and external systems.
How Raise Data Flows to CRM+
The cross-product implications of the modeling decisions on this page.
The Raise Data Model
The resource-level reference for the data the modeling decisions apply to.