Skip to content

Data Model Reference

All objects#

The complete packaged data model, generated straight from deployed metadata. For the visual map, see the interactive diagram.

Sourcebase/main/default/objectsGeneratorgenerate_data_model_docs.pyObjects27Relationships43

Generated file

Don't hand-edit this page — edit field/object descriptions in the metadata and rerun the generator.

Contents

Donors#

Account#

Account · standard · 7 fields · Donors

Relationships

Direction Field Related object Relationship type
← in Donor_Account__c on Event Attendee Event Attendee Lookup
← in Soft_Credit_Donor_Account__c on Opportunity Opportunity Lookup
← in Soft_Credit_Household_Account__c on Opportunity Opportunity Lookup
← in Donor_Account__c on Payment Method Payment Method Lookup
← in Donor_Account__c on Recurring Donation Recurring Donation Lookup

Fields

Field Label Type Notes
Gift_Count__c Gift Count Number Number of completed gifts from this donor. Updated automatically — do not edit directly.
Docs: Written by DonorSummaryRollupBatch via ICampaignService.rollupDonorSummary()/rollupDonorAccountSummary(), not by a trigger; a scheduled batch, not real-time DML, keeps this in sync, so a just-finalized donation will not be reflected here until the next run.
Last_Donation_Date__c Last Donation Date Date Date of this donor's most recent gift. Updated automatically.
Portal_Token_Expires_At__c Portal Token Expires At DateTime System-managed expiry for the donor portal link. Not intended for manual entry.
Portal_Token_Hash__c Portal Token Hash Text System-managed security value for the donor portal. Never edit or expose this field.
Docs: Read in SYSTEM_MODE only, by DonorPortalService; this field intentionally has no FLS grant in any permission set (see the field description); granting it would let a non-system context match a donor by their raw magic-link token.
Portal_Token_Issued_At__c Portal Token Issued At DateTime System-managed. Records when this donor's portal link was last issued, so repeated requests for a new link are ignored for a short cooldown period. Not intended for manual entry.
Stripe_Customer_Id__c Stripe Customer ID Text Stripe's identifier for this donor's customer record. Populated automatically by the payment integration.
Docs: Resolved by DonorResolutionService, which is the only sanctioned place to decide whether a given donor's Stripe customer profile lives on Contact or Account; callers should never branch on IsPersonAccount to pick between this field and its counterpart on the other object.
Total_Donated__c Total Donated Currency Lifetime total given by this donor. Updated automatically — do not edit directly.
Docs: Read by ContactsSelector/AccountsSelector and written by DonorSummaryRollupBatch, which runs the Contact arm and the Account arm as two chained batch jobs so both donor models in the dual-field strategy are refreshed by a single scheduled kickoff.

Contact#

Contact · standard · 7 fields · Donors

Relationships

Direction Field Related object Relationship type
← in Contact__c on Event Attendee Event Attendee Lookup
← in Soft_Credit_Contact__c on Opportunity Opportunity Lookup
← in Contact__c on Payment Method Payment Method Lookup
← in Contact__c on Recurring Donation Recurring Donation Lookup

Fields

Field Label Type Notes
Gift_Count__c Gift Count Number Number of completed gifts from this donor. Updated automatically — do not edit directly.
Docs: Written by DonorSummaryRollupBatch via ICampaignService.rollupDonorSummary()/rollupDonorAccountSummary(), not by a trigger; a scheduled batch, not real-time DML, keeps this in sync, so a just-finalized donation will not be reflected here until the next run.
Last_Donation_Date__c Last Donation Date Date Date of this donor's most recent gift. Updated automatically.
Portal_Token_Expires_At__c Portal Token Expires At DateTime System-managed expiry for the donor portal link. Not intended for manual entry.
Portal_Token_Hash__c Portal Token Hash Text System-managed security value for the donor portal. Never edit or expose this field.
Docs: Read in SYSTEM_MODE only, by DonorPortalService; this field intentionally has no FLS grant in any permission set (see the field description); granting it would let a non-system context match a donor by their raw magic-link token.
Portal_Token_Issued_At__c Portal Token Issued At DateTime System-managed. Records when this donor's portal link was last issued, so repeated requests for a new link are ignored for a short cooldown period. Not intended for manual entry.
Stripe_Customer_Id__c Stripe Customer ID Text Stripe's identifier for this donor's customer record. Populated automatically by the payment integration.
Docs: Resolved by DonorResolutionService, which is the only sanctioned place to decide whether a given donor's Stripe customer profile lives on Contact or Account; callers should never branch on IsPersonAccount to pick between this field and its counterpart on the other object.
Total_Donated__c Total Donated Currency Lifetime total given by this donor. Updated automatically — do not edit directly.
Docs: Read by ContactsSelector/AccountsSelector and written by DonorSummaryRollupBatch, which runs the Contact arm and the Account arm as two chained batch jobs so both donor models in the dual-field strategy are refreshed by a single scheduled kickoff.

Campaigns & Content#

Campaign#

Campaign · standard · 63 fields · Campaigns & Content

See the full reference page for this object.

Relationships

Direction Field Related object Relationship type
→ out Default_Designation__c Designation Lookup
→ out Design__c Campaign Design Lookup
→ out Payment_Account__c Payment Account Lookup
→ out Recurring_Migration_Campaign__c Campaign Lookup
← in Recurring_Migration_Campaign__c on Campaign Campaign Lookup
← in Campaign__c on Campaign Post Campaign Post Lookup
← in Campaign__c on Donation Staging Donation Staging Lookup
← in Campaign__c on Event Attendee Event Attendee Lookup
← in Campaign__c on Event Ticket Type Event Ticket Type MasterDetail
← in Campaign__c on Guest Request Guest Request Lookup
← in Campaign__c on Recurring Donation Recurring Donation Lookup

Fields

Field Label Type Notes
Accept_Donations_After_End_Date__c Accept Donations After End Date Checkbox Leave checked to keep accepting gifts after the end date passes — useful for an appeal whose date is a milestone rather than a deadline. Uncheck it for a campaign that must genuinely stop, such as a time-boxed match or an event that has already happened; donors then see a "no longer accepting donations" message instead of the form. Existing recurring gifts keep renewing either way.
Docs: Only meaningful alongside a non-blank EndDate — a campaign with no end date is never closed by this field. Deliberately independent of Status and of the Campaign_Closed__c formula: Status = 'Active' is the publicity gate (a non-Active campaign is invisible to guests entirely), Campaign_Closed__c is the console's display flag, and this field is the only one that decides whether an ended-but-still-Active campaign may take a payment. Enforced server-side in CampaignService.assertAcceptingDonations, called from DonationService.createDonorAndIntent and EventRegistrationController.requireActiveCampaign; surfaced to the guest UI as the acceptingDonations key on the campaign payload so the form renders a closed state instead of failing at charge time. Renewal of an ALREADY-ACTIVE recurring schedule is deliberately not gated — see RecurringRenewalBatch and Recurring_Behaviour_At_Goal__c / Recurring_Migration_Campaign__c, which own that policy.
Allow_Company_Match__c Allow Company Match Checkbox When on, donors can flag that their employer offers gift matching for this donation.
Allow_Donation_Addon__c Allow Donation Addon Checkbox When on, ticket buyers are offered the option to add an extra donation at checkout.
Allow_Recurring__c Allow Recurring Checkbox When on, donors can choose to make their gift recurring instead of one-time.
Allow_Tribute__c Allow Tribute Checkbox When on, donors can dedicate their gift "in honor of" or "in memory of" someone.
Allowed_Designation_Ids__c Allowed Designation Ids LongTextArea Only used when Designation Mode is Selectable — the set of funds donors may choose between on this campaign's donation page. Managed by the fund-scope picker, not edited directly.
Docs: Mirrors Campaign.FAQ_Ids__c's JSON-array-in-a-field pattern (CampaignFaqService) — deliberately not a junction object, per the locked design decision. A LongTextArea comfortably holds hundreds of 18-char Ids as a JSON array; no realistic campaign approaches that limit.
Amount_Impacts_JSON__c Amount Impacts (JSON) LongTextArea Advanced: JSON pairing each suggested amount with an impact line shown under its tile. Leave blank to skip impact hints.
Docs: The JSON keys are free-text amounts, not validated server-side against Suggested_Amounts__c — a key that does not match a suggested-amount tile is simply never shown, and there is no error surfaced to the admin if the two lists drift apart. Keep them in sync by hand when either field is edited.
Amount_Raised__c Amount Raised Currency Total raised toward this campaign's goal so far. Calculated automatically — do not edit directly.
Docs: Apex-maintained, not a native roll-up (Opportunity to Campaign is a standard lookup, not master-detail). Recomputed by CampaignService.rollupCampaignTotals() from a fresh SUM query every time it runs, so it is always the source of truth rather than an incremental counter; called from the Opportunities domain on insert, on a Closed Won status change, on any post-close Amount change, and on a refund. A goal-crossing detected mid-rollup enqueues CancelGoalReachedRecurringQueueable when Recurring_Behaviour_At_Goal__c is Cancel.
Campaign_Closed__c Closed Checkbox Automatically checked once this campaign's end date has passed. Drives the "Completed" tag in the Fundraisers console — you don't need to set this manually.
Docs: Deliberately EndDate-only, matching the console's prior "Ended" derivation (c.EndDate != null && c.EndDate < today) — Status stays a separate, admin-set field and is not part of this formula. See CampaignsSelector.selectFundraisers (adds this to the query) and FundraiserAdminController.GetFundraisers (reads it as the "Ended" status source).
Campaign_Stage_Icon__c Stage Icon Text A quick visual read of this campaign's stage: green means it's live and accepting donations, yellow means it's still a draft, red means it has ended. This is calculated automatically — there's nothing to set here.
Docs: Uses Salesforce's built-in /img/samples/color_{green,yellow,red}.gif sample images (verified current against Salesforce Developer Docs' "Sample Image Link Formulas" reference, checked 2026-08-04) — no static resource or upload needed. Same three-state logic as Campaign_Closed__c's own doc comment and FundraiserAdminController.GetFundraisers: Status = 'Active' -> Active (green); else Campaign_Closed__c -> Ended (red); else -> Draft (yellow). This is the native record-page counterpart to lexFundraisersConsole's client-side SVG stage icons — that component needs inline markup for styling control, this field exists so the same stage is visible on the standard Campaign layout without opening the console.
Campaign_Subtype__c Campaign Subtype Picklist What kind of fundraiser this is. Controls which public page layout and features are shown.
Docs: Read by CampaignService.getPublicExperienceConfig() to choose the guest shell composition, and by the campaign gallery for its badge/filter. Every value shares the single packaged Fundraising record type, so this picklist, not RecordTypeId, is the real behavioral switch — a maintainer adding a new fundraiser shape should add a value here, not a new record type.
Capacity_Mode__c Capacity Mode Picklist How ticket sales are capped for this event: no limit, per ticket tier, an overall event total, or both.
Column_Ratio_Override__c Column Ratio Override Picklist Leave blank unless this one campaign needs a different split from its design — for example a short appeal whose story does not need the wide column.
Docs: Replaces Form_Layout_Mode_Override__c and Form_Side_Override__c, which were the per-campaign half of the retired layout-mode pair. Blank means "use the design's ratio" — CampaignService reads it through the same campaign-over-design rule every other override on this page uses, so a blank never renders as a missing layout.
Contact_Email_Override__c Contact Email Override Email Only fill this in if this campaign is handled by someone other than your usual contact. Otherwise leave blank and the org default is used.
Docs: Named _Override__c to match Form_Title_Override__c and its siblings: the suffix is how the next reader knows a blank means "fall back to the org default", not "this campaign has no contact". Resolved in CampaignService, never read directly by the LWC.*
Contact_Phone_Override__c Contact Phone Override Phone Only fill this in if this campaign is handled by someone other than your usual contact. Otherwise leave blank and the org default is used.
Docs: Named _Override__c to match Form_Title_Override__c and its siblings: the suffix is how the next reader knows a blank means "fall back to the org default", not "this campaign has no contact". Resolved in CampaignService, never read directly by the LWC.*
Custom_Question_Ids__c Custom Question Ids LongTextArea Internal: ordered list of question template ids selected for this campaign. Managed by the Custom Questions picker, not edited directly.
Custom_Questions_Active__c Custom Questions Active Checkbox Uncheck to hide the Custom Questions step from the public donation form without losing the saved question selection.
Default_Amount__c Default Donation Amount Currency The gift amount pre-selected when a donor opens the donation form. Leave blank to have no amount selected by default.
Default_Designation__c Default Designation Lookup → Designation__c The fund this campaign supports. Its exact effect depends on the Designation Mode field above: the locked fund in Assigned mode, the pre-selected default in Selectable mode, or unused in All Active mode.
Docs: This field predates Designation_Mode__c/Allowed_Designation_Ids__c (2026-08) and is reused by the new mode-based resolution in CampaignDesignationService rather than replaced — see specs/04-data-model.md for the full three-mode contract. A blank value here in Assigned mode falls back to Settings__c.Default_Designation__c via SettingsService.defaultDesignationId(), then to an undesignated gift if that also fails to resolve — never a hardcoded fund.
Design__c Design Lookup → Campaign_Design__c Which saved design/theme to use for this campaign's public page. Leave blank to use the default design.
Docs: Resolved by CampaignService.resolveDesign()/selectDesignSystemMode(). The chosen design's theme JSON is normalized by CampaignService.normalizeThemeTokensJson() against the Theme_Token__mdt catalog before it ever reaches the guest page, so an admin cannot smuggle an arbitrary CSS var through a design record — only tokens present in that packaged catalog survive.
Designation_Mode__c Designation Mode Picklist How donors choose a fund on this campaign's donation page: lock every gift to one fund (default), let them pick from every active fund, or offer a scoped list you choose below.
Docs: Explicit picklist, not inferred from Allowed_Designation_Ids__c list length — this keeps admin intent stable while the admin is mid-edit of the fund list (e.g. temporarily down to one selected fund should not silently behave like Assigned mode). Default is Assigned — a new campaign locks to its single Default_Designation__c fund unless an admin opts into a broader mode; this field ships new alongside campaign designation scoping, so there is no pre-existing installed base to stay backward compatible with. This field's Assigned default is NOT what an API/Apex insert actually gets. Measured 2026-08-04 in pledgivo-dev-32376: a Campaign inserted with no RecordTypeId lands on the Fundraising record type, and that record type declares no picklist assignment for this field, so the platform supplies the first available value — All Active — overriding the Assigned default above. Only a caller that omits the field AND the record type is left with a blank. Campaigns created by the fundraiser wizard, the seed scripts, or any integration must therefore set this field EXPLICITLY rather than relying on either default. A record that does end up blank resolves to All Active in CampaignDesignationService — a deliberate choice so an unconfigured campaign is not silently narrowed to one fund, and it matches what the record type supplies. There is no validation rule enforcing this field; the fundraiser wizard's own save guards are the only place a blank mode is blocked.
Donor_Count__c Donor Count Number Number of people who have donated to this campaign. Calculated automatically.
Docs: Recomputed alongside Amount_Raised__c by CampaignService.rollupCampaignTotals(), which runs a grouped aggregate query rather than incrementing a counter, so a donor who gives twice is only counted once. Person Account orgs and standard orgs both feed the same count through DonorResolutionService, so this number is meaningful regardless of account model.
Donor_Covered_Fees_Enabled__c Donor Covered Fees Enabled Checkbox When on, donors can choose to cover the processing fee so the full gift amount reaches the campaign.
Docs: This flag is the ONLY gate on the feature — FeeCoverageService.isOffered() reads it, and every fee is recomputed server-side from Settings__c.Fee_Coverage_Percent__c / Fee_Coverage_Fixed__c, never from a browser-supplied number. The guest form sends only the donor's yes/no, so turning this off immediately stops any fee being added even if a stale page still shows the checkbox. The rate lives in Settings__c rather than here on purpose: it is a property of the payment processor, not of the appeal. Event ticket orders deliberately do not offer fee coverage regardless of this flag — a ticket already carries a fair-market-value split to disclose, and layering a second non-gift component on top makes the receipt materially harder to read.
Event_Address__c Event Address Text Street address where this event takes place.
Event_Capacity__c Event Capacity Number Maximum total attendees across all ticket types. Only used when capacity mode includes an event-wide limit.
Event_City__c Event City Text City where this event takes place.
Event_Country__c Event Country Text Country where this event takes place.
Event_State__c Event State Text State or province where this event takes place.
Event_Timezone__c Event Timezone Text Timezone the event runs in, e.g. "America/New_York".
Event_Zip__c Event Zip Text Postal/zip code where this event takes place.
FAQ_Ids__c FAQ Ids LongTextArea Internal: ordered list of FAQ template ids selected for this campaign. Managed by the FAQ picker, not edited directly.
FAQs_Active__c FAQs Active Checkbox Uncheck to hide the FAQ accordion from the public donation page without losing the saved FAQ selection.
Footer_Text_Override__c Public Page Footer Text Override Text Only fill this in if this campaign's pages need a different bottom line from the rest of your site.
Docs: Named _Override__c to match Contact_Email_Override__c and its siblings: the suffix is how the next reader knows a blank means "use the org-wide value from Setup", not "show nothing". Resolved in CampaignService.resolveIdentity, never read directly by an LWC.*
Form_Description_Override__c Form Description Override LongTextArea Custom description text for this campaign's donation form. Leave blank to use the design's default text.
Form_Disclaimer_Override__c Form Disclaimer Override LongTextArea Custom disclaimer text for this campaign's donation form. Leave blank to use the design's default text.
Form_Footer_Override__c Form Footer Override LongTextArea Custom footer text for this campaign's donation form. Leave blank to use the design's default text.
Form_Title_Override__c Form Title Override Text Custom title for this campaign's donation form. Leave blank to use the design's default title.
Goal_Amount__c Goal Amount Currency How much this campaign is aiming to raise. On a General or Open Donation campaign this also drives the raised-of-goal progress bar donors see on the donation page and in embedded widgets — unless the page design has Show Progress Bar switched off. A Ticketed Event campaign shows a tier picker rather than a bar, so there the goal is used for your own reporting only.
Docs: The bar's visibility rule lives in CampaignService.computeShowProgressBar (PROGRESS_BAR_SUBTYPES = {'General', 'Open_Donation'}) and is the single authority: pfHero, dfForm and the embed widget all render from the server-computed showProgressBar flag rather than re-deciding the subtype themselves. The help text spells the rule out because the field is on the layout for every subtype: before 2026-08-18 it promised "the public progress bar" unconditionally, which was false for an event. Open Donation was added to the set on 2026-08-18 (audit F-42), closing a three-way disagreement in which an open-ended appeal with a goal drew a bar on the donation form while its hero and its embed drew none, and unchecking Show Progress Bar removed it from two of the three.
Header_Image_URL__c Header_Image_URL Url Web address of the hero image shown at the top of this campaign's page. Leave blank to use the design's default image.
Match_Message__c Match Message Text Optional short line about a matching offer, shown as a chip on the public donation form (e.g. "Gifts matched 2x tonight"). Leave blank to hide.
Payment_Account__c Payment Account Lookup → Payment_Account__c Which of your configured Stripe accounts receives donations made to this fundraiser. Add more accounts from the Payments settings panel first. Every fundraiser needs one before it can go live.
Docs: Replaced the Campaign_Payment_Account__c junction object (removed 2026-08-04). The junction modelled a many-to-many that was never implemented: every consumer took cpas[0] and discarded the rest, and its Is_Default__c was written but never read. Campaigns are many-to-one on payment account by product decision — an org may hold several Stripe accounts, a campaign collects to one. deleteConstraint is Restrict, so a Payment_Account__c still in use by a campaign cannot be deleted; SetupController.DeletePaymentAccount raises the same condition as a friendly admin-facing error before the platform ever gets to enforce it.
Percent_Goal_Reached__c % of Goal Reached Percent Percentage of the goal reached so far. Calculated automatically.
Docs: The formula intentionally returns the raw fraction (0.5), not 0 to 100 — Percent-type fields render the stored decimal already multiplied by 100, so a formula that also multiplies by 100 would display 50 as 5000%. Do not add a 100 term if this formula is ever revised.*
Privacy_URL_Override__c Privacy URL Override Url Only fill this in if this campaign publishes a different privacy policy from the rest of your site. Otherwise leave blank.
Docs: Named _Override__c to match Contact_Email_Override__c and its siblings: the suffix is how the next reader knows a blank means "use the org-wide value from Setup", not "show nothing". Resolved in CampaignService.resolveIdentity, never read directly by an LWC.*
Public_URL__c Public URL Text The web address (or slug) for this campaign's public donation page.
Docs: Generated once from the campaign name by FundraiserAdminController.generateSlug() at creation time and only backfilled later if still blank — it is not regenerated when the campaign is renamed, so an old slug can outlive a name change. There is no unique-index enforcement on this field at the platform level; collision handling, if any, lives in the generation logic itself.
Recurring_Behaviour_At_Goal__c Recurring Behaviour At Goal Picklist What to do with active recurring gifts once this campaign hits its goal: keep charging or automatically cancel them.
Docs: Checked by CampaignService.rollupCampaignTotals() at the moment it detects a newly-crossed goal. When set to Cancel, that rollup enqueues CancelGoalReachedRecurringQueueable, which cancels active Recurring_Donation__c records in chunks of 200 with Transaction Finalizer auto-recovery (chunk size halves on a governor LimitException, up to 4 retries) rather than cancelling inline, so a large campaign never blocks the rollup that triggered it.
Recurring_Migration_Campaign__c Recurring Migration Campaign Lookup → Campaign Where to move active recurring gifts once this campaign closes. Leave blank to keep them here indefinitely.
Docs: The description above states the intended behavior, but as of this writing no batch or service actually re-attributes recurring donations when a campaign closes; CampaignsSelector reads this field but nothing writes Recurring_Donation__c.Campaign__c off of it. Treat it as reserved for a not-yet-built migration job, not as evidence that closing a campaign moves anything automatically.
Show_Contact__c Show Contact Checkbox Leave on so donors can reach a person before giving. Contact details fall back to the org defaults in Setup when this campaign leaves them blank.
Docs: The band renders below both columns, full width. It resolves its email and phone through the three-tier fallback Campaign override then Settings__c org default; with the toggle on and both tiers blank the band renders nothing rather than an empty shell.
Show_Documents__c Show Documents Checkbox Leave on to show donors your supporting files. The tab hides itself when there are no documents to show.
Docs: Two conditions gate the tab: this flag AND at least one published Campaign_Post__c of type Document. The emptiness check lives in the guest component, not here, so an admin can pre-enable the tab before uploading the first file.
Show_Story__c Show Story Checkbox Leave on to tell donors why you are asking. Turn off if this campaign needs nothing but a payment form.
Docs: Defaults to true so a campaign that has a story shows it without an extra click. Read by CampaignService into the guest payload as sections.story; the guest shell also requires non-empty story content, so the toggle can hide a story but never conjure one.
Show_Updates__c Show Updates Checkbox Leave on to report progress to donors. The tab hides itself when there are no updates to show.
Docs: Same two-condition gate as Show_Documents__c — this flag AND at least one published Campaign_Post__c of type Update.
Social_Facebook_URL_Override__c Social — Facebook URL Override Url Only fill this in if this campaign has its own Facebook page. Otherwise leave blank and your organization's page is shown.
Docs: Named _Override__c to match Contact_Email_Override__c and its siblings: the suffix is how the next reader knows a blank means "use the org-wide value from Setup", not "show nothing". Resolved in CampaignService.resolveIdentity, never read directly by an LWC.*
Social_Instagram_URL_Override__c Social — Instagram URL Override Url Only fill this in if this campaign has its own Instagram account. Otherwise leave blank and your organization's account is shown.
Docs: Named _Override__c to match Contact_Email_Override__c and its siblings: the suffix is how the next reader knows a blank means "use the org-wide value from Setup", not "show nothing". Resolved in CampaignService.resolveIdentity, never read directly by an LWC.*
Social_Share_Image_URL_Override__c Social Share Image URL Override Url Only fill this in if you want a different picture to appear when this campaign's page is shared on social media. Best at 1200x630 pixels.
Docs: Named _Override__c to match Contact_Email_Override__c and its siblings: the suffix is how the next reader knows a blank means "use the org-wide value from Setup", not "show nothing". Resolved in CampaignService.resolveIdentity, never read directly by an LWC.*
Social_Share_Text__c Social Share Text LongTextArea Suggested caption pre-filled when someone shares this campaign on social media.
Social_Twitter_URL_Override__c Social — X (Twitter) URL Override Url Only fill this in if this campaign has its own X (Twitter) account. Otherwise leave blank and your organization's account is shown.
Docs: Named _Override__c to match Contact_Email_Override__c and its siblings: the suffix is how the next reader knows a blank means "use the org-wide value from Setup", not "show nothing". Resolved in CampaignService.resolveIdentity, never read directly by an LWC.*
Story__c Story LongTextArea The narrative donors read on the public page — who you help, why it matters, what their gift does. Written in Markdown using the toolbar on the Story step of the fundraiser wizard: ## for a heading, bold, a bullet list, a pull quote, an image URL, a YouTube link. Type it as plain paragraphs and it still renders correctly.
Docs: Holds Markdown, never HTML. Every write from the fundraiser wizard passes through CampaignService.normalizeStoryMarkdown(), which strips HTML tags, blanks javascript:/data: URLs, drops malformed YouTube ids and throws above the byte ceiling. The public page renders it through uiMarkdownParser.js -> c-ui-markdown as a token tree of escaped LWC text bindings, so no author string ever reaches innerHTML. Stories written before Markdown (plain paragraphs, a leading ">" pull quote) are already valid Markdown and needed no migration. The length is the platform maximum and is one-way: a shipped field's length can be raised but never lowered, so 131072 is a permanent contract with every subscriber org.
Suggested_Amounts__c Suggested Amounts Text Comma-separated preset amounts shown as quick-pick buttons, e.g. "25,50,100,250".
Docs: Plain comma-separated text, not a structured list type, so any consumer must split and parse it defensively. Paired with Amount_Impacts_JSON__c, whose keys should match these values exactly for the impact hint to show — see the doc note there about the two lists not being validated against each other.
Tagline__c Tagline Text A short phrase shown below the campaign title on the public page.
Terms_URL_Override__c Terms URL Override Url Only fill this in if this campaign is governed by different terms from the rest of your site — a sponsored appeal with its own conditions, for example.
Docs: Named _Override__c to match Contact_Email_Override__c and its siblings: the suffix is how the next reader knows a blank means "use the org-wide value from Setup", not "show nothing". Resolved in CampaignService.resolveIdentity, never read directly by an LWC.*
Thank_You_CTA_Label__c Thank-You CTA Label Text Text shown on the thank-you CTA button (e.g. "See Our Impact"). Blank = use the org default.
Thank_You_CTA_URL__c Thank-You CTA URL Url Where the thank-you CTA button sends donors (e.g. your programs page). Blank = use the org default.
Docs: Sits in the middle of a precedence chain, not a standalone value: a page design's own CTA settings are checked first, then this campaign field, then the org-wide Settings default, and finally a hardcoded Continue fallback if nothing else is set. A blank value here does not mean no CTA — it means the next link in the chain decides.
Tickets_Sold__c Tickets Sold Summary Total tickets sold across all ticket types for this event. Calculated automatically.
Docs: The only native Roll-Up Summary field on Campaign — everything else on this object that looks like a rollup (Amount_Raised__c, Donor_Count__c) is Apex-maintained instead, because Opportunity to Campaign is a standard lookup, not master-detail, so a platform rollup is not available there. This one works natively because Event_Ticket_Type__c is Master-Detail to Campaign.
Venue_Name__c Venue Name Text Name of the location where this event takes place.

Campaign Design#

Campaign_Design__c · custom · 38 fields · ReadWrite · Campaigns & Content

A reusable branding/theme configuration (colors, fonts, layout, form copy defaults) that can be applied to one or more Campaigns' public donation pages. There is no standard-object equivalent for shareable page theming, so it is modeled as a custom object. If a Campaign has no Design assigned, the record with Is_Default__c checked is used as fallback. Admin-configurable design overrides only — never hardcode a themeable value in a public component.

Docs note

Theme_Tokens_JSON__c on this object is the only field whose overridable key set is governed by Theme_Token__mdt (see that object's doc note) — every other field here is a direct, always-present column read by CampaignService.buildPublicExperienceResult() and the setDesigns internal editor via SetupController. Records are shared across Campaigns many-to-one (one Design, many Campaigns), so editing a Design changes every Campaign that references it; there is intentionally no per-Campaign field override outside the JSON token layer.

Relationships

Direction Field Related object Relationship type
← in Design__c on Campaign Campaign Lookup

Fields

Field Label Type Notes
Accent_Color__c Accent Color Text Hex color (e.g. #0070D2) used for accent highlights on the public page.
Background_Color__c Background_Color Text Page background color as a hex code (e.g. #FFFFFF). Leave blank for the default white background.
Border_Color__c Border Color Text Hex color (e.g. #E0E0E0) used for borders and dividers on the public page.
Button_Color__c Button_Color Text Button color as a hex code (e.g. #0070D2). Leave blank to use the primary color.
Docs: See Primary_Color__c for the full fallback chain this field participates in (Button_Color -> Primary_Color, Button_Hover_Color -> derived shade of Button_Color, Button_Text_Color -> white); publicFundraisingExperience.js resolves all three together.
Button_Hover_Color__c Button Hover Color Text Button color on hover, as a hex code. Leave blank to use an automatic shade of the button color.
Button_Style__c Button Style Picklist No longer used. Public-page buttons are always filled; set their colour with Button Color and Button Text Color instead.
Docs: INERT. publicFundraisingExperience.js's resolveButtonTokens() is now a no-op that returns null, so this field emits no CSS and the primary CTA is always filled. Every consumer of the pf-btn CSS token group is a primary call to action, so Outline/Ghost could only strip the fill off the one button a donation page exists to get pressed, and on two of the packaged designs the flattened text failed WCAG AA. CampaignService still passes the value through to the guest payload and the field still has its picklist; both are retained purely so an installed subscriber's stored value does not break on upgrade. The per-field hex colours (Button_Color__c/Button_Hover_Color__c/Button_Text_Color__c) are unaffected and remain the way to restyle the button.
Button_Text_Color__c Button Text Color Text Button label text color as a hex code. Leave blank for white text.
Column_Ratio__c \* Column Ratio Picklist Pick how the public page splits. 60/40 gives your story the most room and is the usual choice. 40/60 moves the payment box to the left.
Docs: One value encodes both the proportion and which side the payment box sits on, so the payment box is always the narrower column at 60/40 and 40/60. This replaces the old Form_Layout_Mode__c and Form_Side_Default__c pair outright. Resolved into the guest payload as columnRatio by CampaignService, after Campaign.Column_Ratio_Override__c has had its say. Deliberately NOT a theme token: the grid template is derived from this field alone, so a page-design record cannot half-override it and leave the two columns disagreeing.
Content_Max_Width__c Content Max Width Picklist How wide the page content should be on large screens.
Docs: One of the fixed named-preset columns (alongside Radius_Preset__c, Spacing_Density__c, Button_Style__c, Shadow_Style__c) that publicFundraisingExperience.js reads directly to resolve layout CSS variables; not part of the open-ended, CMDT-governed Theme_Tokens_JSON__c override map on this same object.
Default_Hero_Image_URL__c Default_Hero_Image_URL Url Default hero image for campaigns using this design that don't set their own header image.
Docs: Read by both CampaignService.buildPublicExperienceResult() (guest donation page) and FundraiserAdminController (internal preview), and consumed client-side in publicFundraisingExperience.js and dfForm.js. A Campaign's own Header_Image_URL__c always wins when set; this field only fills the gap for campaigns on this design that never uploaded their own header image.
Description__c Description TextArea Internal notes for admins about this design. Not visible to donors.
Exit_Intent_Dismiss_Label__c Exit Intent Dismiss Label Text Text on the decline button, e.g. "No thanks". Keep it a genuine way out — a prompt with no clear exit reads as a trap and damages trust in the organization.
Docs: Dismissing sets the once-per-visit flag in dfForm, so the prompt does not reappear for the rest of the session regardless of how many times the donor's cursor leaves the page.
Exit_Intent_Enabled__c Exit Intent Enabled Checkbox When on, a donor who has chosen an amount but not yet paid sees a short "before you go" prompt if they move to close the tab. Shows at most once per visit, and never once payment has started.
Docs: Read by CampaignService.designToMap and honored in dfForm.js. The trigger is real pointer-based exit intent (cursor leaving through the top of the viewport), NOT the beforeunload event: browsers only ever render their own generic dialog from beforeunload and ignore any attempt to show custom UI, so a modal driven from that event cannot work. dfForm keeps a separate beforeunload handler for the mid-payment case, which is the one thing the native dialog is genuinely right for. Pointer-only by design: touch devices have no equivalent signal, and the substitutes (history traps, scroll heuristics) misfire often enough to be worse than not showing it.
Exit_Intent_Headline__c Exit Intent Headline Text Headline for the "before you go" prompt, e.g. "Before you go". Leave blank to use the default wording.
Docs: Rendered as plain text, never as HTML — the guest form must not become an injection surface for anyone with edit rights on a design record.
Exit_Intent_Message__c Exit Intent Message TextArea One or two sentences reminding the donor what their gift does. The amount they already chose is shown for them, so there is no need to repeat it here.
Docs: The donor's selected amount and frequency are rendered by dfForm from live form state, not merged into this text — copy written around a hardcoded amount goes stale the moment the donor changes their selection. Plain text only; see Exit_Intent_Headline__c.
Exit_Intent_Stay_Label__c Exit Intent Stay Label Text Text on the button that takes the donor back to finish their gift, e.g. "Complete my gift".
Focus_Ring_Color__c Focus Ring Color Text Hex color used for the keyboard focus outline on the public page (accessibility).
Font_Family__c Font Family Picklist The body text font for the public page.
Docs: Restricted picklist rather than a free-text font name/URL on purpose: allowing an admin to supply an arbitrary font stylesheet URL on a donor-facing guest page would be a CSS/content-injection risk, so only this pre-approved value set is selectable. The Google_ values are the only ones that trigger a webfont load; System and Modern render with no external request (Modern names Inter, which is deliberately NOT bundled - it falls through to the system UI face). Google_Bricolage/Google_Hanken are the two halves of the Ember default brand pairing referenced in the design brand spec, offered here as opt-in choices rather than as the field default. Widened to 38 faces on 2026-08-05, grouped by category in display order (sorted=false, so the order below IS the admin's dropdown order): grotesque sans, geometric sans, condensed sans, reading serif, slab, display serif, mono. Adding a face means three edits in lockstep - a FAMILIES line in scripts/build/fetch-public-fonts.sh, a value in BOTH this field and Heading_Font_Family__c, and a FONT_STACKS entry in publicFundraisingExperience.js + publicEventRegistration.js. Miss any one and the value renders a fallback face with no visible error.*
Form_Builder_JSON__c Form Builder JSON LongTextArea Advanced: the block layout that builds this design's donation form and page content. Edited via the Page Designs setup panel, not directly.
Docs: The v2 replacement for the deprecated Page_Blocks_JSON__c: block objects carry a type, a step bucket (1 Compose / 2 Payment / 3 Thank You), an enabled flag, and per-type settings. Some block types have a forced step regardless of what is stored (see PageCanvasBlocks); PageCanvasBlocks.buildUnified() is also the back-compat seam that merges in any pre-migration Page_Blocks_JSON__c content for orgs that have not yet been re-saved through the block editor.
Form_Description_Default__c Form Description Default LongTextArea Default form description text. A campaign can override this individually.
Form_Disclaimer_Default__c Form Disclaimer Default LongTextArea Default form disclaimer text. A campaign can override this individually.
Docs: Design-level default rather than always requiring a per-campaign entry so that shared legal/compliance disclaimer text can be authored once and reused across every campaign on this design; Campaign.Form_Disclaimer_Override__c exists specifically for the exceptional campaign that needs different wording, not as the primary authoring path.
Form_Footer_Default__c Form Footer Default LongTextArea Default form footer text. A campaign can override this individually.
Form_Title_Default__c Form Title Default Text Default form title. A campaign can override this individually.
Heading_Color__c Heading Color Text Hex color used for headings on the public page.
Heading_Font_Family__c Heading Font Family Picklist The heading font for the public page. Choose Inherit to match the body font.
Docs: This value set must stay identical to Font_Family__c's (plus the Inherit entry). Both are restricted picklists, so a theme or admin selecting a face present in one list but not the other fails to save rather than falling back. Widened to 38 faces on 2026-08-05 - see the doc comment on Font_Family__c for the three-file lockstep required to add one. Caveat specific to headings: Instrument Serif, DM Serif Display and IBM Plex Mono ship a 400 weight only, so a heading that asks for 600/700 gets a browser-synthesised faux bold. Avoid them in packaged theme heading slots (PostInstallHandler.themeSpecs() enforces this for the first two; the mono face is body/figure-oriented anyway). Every other face here is a variable font spanning at least 400-700.
Is_Active__c Is_Active Checkbox When off, this design can no longer be assigned to new campaigns.
Is_Default__c Is_Default Checkbox Makes this the fallback design for any campaign that doesn't have a design assigned. Only one design can be the default.
Docs: PostInstallHandler seeds exactly one packaged design with this set so a fresh org always has a usable default; SetupController.saveDesign() enforces single-default semantics by clearing the flag on every other design row whenever a save sets it true. CampaignDesignsSelector orders by this field descending so callers that need the org's default design can simply take the first row.
Link_Color__c Link Color Text Hex color used for links on the public page.
Muted_Text_Color__c Muted Text Color Text Hex color used for secondary/muted text on the public page.
Page_Blocks_JSON__c Page Blocks JSON LongTextArea Legacy field — page content blocks now live in Form Builder JSON. Kept only for backward compatibility.
Docs: Frozen, read-only from the editor's perspective: PageCanvasBlocks merges its content into the unified Form_Builder_JSON__c canvas at read time rather than the reverse, so a pre-migration design keeps rendering correctly without a one-time data migration job.
Primary_Color__c Primary_Color Text Main brand color as a hex code (e.g. #0070D2). Used for progress bars, links, and accents.
Docs: This is the root of the button color fallback chain: Button_Color__c falls back to this field when blank, Button_Hover_Color__c falls back to a shade derived from the resolved button color, and Button_Text_Color__c falls back to white. publicFundraisingExperience.js resolves the whole chain client-side via safeHex(); it is distinct from the CMDT-governed Theme_Tokens_JSON__c override map on the same object and from the email-only Settings__c.Brand_Primary_Color__c, which never touches the public page.
Radius_Preset__c Radius Preset Picklist How rounded corners look across the public page, from square to pill-shaped.
Docs: Not part of the Theme_Token__mdt catalog; unlike Theme_Tokens_JSON__c overrides, this and the other named style-preset fields (Spacing_Density__c, Button_Style__c, Shadow_Style__c, Content_Max_Width__c) are read directly off the Campaign_Design__c record by publicFundraisingExperience.js, not resolved through the CSS-variable allowlist mechanism.
Shadow_Style__c Shadow Style Picklist How much drop shadow cards have on the public page.
Show_Progress_Bar__c Show_Progress_Bar Checkbox When on, shows the goal progress bar. Not shown for Open Donation campaigns regardless of this setting.
Docs: The Open-Donation-campaigns exception is enforced by the campaign-type check in publicFundraisingExperience.js/CampaignService, not by this field itself; the checkbox stays true/editable even on designs assigned to Open Donation campaigns, it is simply not honored for that campaign type.
Spacing_Density__c Spacing Density Picklist How much whitespace is used between sections on the public page.
Surface_Color__c Surface Color Text Hex color used for card and panel backgrounds on the public page.
Text_Color__c Text Color Text Hex color used for body text on the public page.
Theme_Tokens_JSON__c Theme Tokens JSON LongTextArea Advanced: JSON overrides for specific design tokens not covered by the fields above. Only recognized token names are applied.
Docs: A second, independent customization mechanism from the named preset fields on this object (Radius_Preset__c, Button_Style__c, etc.): those are fixed, always-present columns, while this field is an open-ended override map validated at read time against the Theme_Token__mdt catalog. The legacy pre-rename token names from before the 2026-07-25 token-system redesign are still accepted here via CampaignService.THEME_TOKEN_ALIASES, a one-release backward-compat shim due for removal.

Campaign Post#

Campaign_Post__c · custom · 10 fields · ReadWrite · Campaigns & Content

A dated post attached to a fundraising campaign, shown in the Documents or Updates tab of the public page. Used to prove the ask (documents) and to report delivery afterwards (updates).

Docs note

One object serves both the Documents shelf and the Updates timeline on the public page. Type__c discriminates. They render differently but are authored, sorted, permissioned and queried identically, so two objects would duplicate every one of those concerns. Related to Campaign by Lookup rather than Master-Detail deliberately: a master-detail detail object takes no objectPermissions at all (2GP rejects the grant at package version create time), and without object access every field on it describes as inaccessible, so a USER_MODE query throws rather than returning rows. Lookup keeps a normal permission grant and a normal user-mode selector.

Relationships

Direction Field Related object Relationship type
→ out Campaign__c Campaign Lookup

Fields

Field Label Type Notes
Body__c Body LongTextArea Write the update donors will read. Leave blank for documents.
Campaign__c \* Campaign Lookup → Campaign Choose the campaign this update or document belongs to. It will appear in that campaign's public page tabs.
Docs: Required, so it is implicitly readable and editable and must be omitted from every permission set — the platform rejects fieldPermissions on a required field. A required Lookup must also declare a deleteConstraint or the field fails to deploy; Cascade is correct because a post exists only to render on its campaign's page and is meaningless once that campaign is gone.
File_Size__c File Size Number Do not fill this in by hand. It is taken from the attached file and refreshed whenever the post is saved.
Docs: Raw bytes, not a formatted label. CampaignPostService.formatSize turns it into "125 KB" or "2.4 MB" at read time, which keeps one rounding rule in one place and leaves the stored value free of a unit that would have to be reparsed. Stored rather than read live for the same reason as File_URL__c — the guest cannot query ContentVersion to ask how big the file is.
File_Type__c File Type Text Do not fill this in by hand. It is taken from the attached file and refreshed whenever the post is saved.
Docs: The uppercased ContentVersion.FileExtension, stored for the same reason as File_URL__c: the guest cannot query the file to ask. It is what the document card prints on its thumbnail, so a blank value shows as a generic FILE tag rather than breaking the card.
File_URL__c File URL Url Do not fill this in by hand. Attach the file to this record and the link fills itself in. If it is ever blank on a published document, save the record again to rebuild it.
Docs: This field exists because a site guest cannot read files. Querying ContentDocumentLink, ContentVersion or ContentDistribution requires the Query All Files permission, which a Guest licence cannot hold, so resolving the download link while the public page renders returns nothing at all. CampaignPostService therefore resolves it once, as an internal user, and stores the ContentDistribution public URL here; the guest read touches no file object. Written by CampaignPosts.onBeforeUpdate and by the ContentVersion trigger, and cleared again when the file behind it goes away.
Is_Published__c Is Published Checkbox Tick this when the post is ready for donors to see. Until then it stays private.
Docs: The only gate between a draft and a guest page. CampaignPostsSelector's guest method filters on it; the user-mode method deliberately does not, so an admin sees drafts.
Posted_Date__c Posted Date Date The date this update happened. Donors see it, so use the real date rather than the date you typed it up.
Sort_Order__c Sort Order Number Leave blank for normal date ordering. Enter a low number to pin a post to the top of its tab.
Docs: Applied before Posted_Date__c in CampaignPostsSelector's ORDER BY, NULLS LAST, so a blank value falls through to pure date ordering rather than sorting to the top.
Title__c \* Title Text A short, specific headline. For a document, name it the way a donor would ask for it.
Docs: Required, so it is omitted from all permission sets.
Type__c \* Type Picklist Choose Update for progress news, or Document for a file donors should be able to read (an audit, a budget, a venue map).
Docs: The discriminator. Document rows carry an attached file and render as a horizontal card shelf; Update rows carry body text and render as a dated timeline. Required, so it is omitted from all permission sets.

Content Template#

Content_Template__c · custom · 6 fields · ReadWrite · Campaigns & Content

Reusable library of donation-form content that admins define once and select onto any number of campaigns, distinguished by record type: Custom_Question (a question donors can be asked) or FAQ (a question-and-answer entry for the FAQ accordion). Each campaign's Custom_Question_Ids__c/FAQ_Ids__c field stores which templates it uses as a JSON list of these Ids, so editing a template updates every campaign that references it.

Docs note

The two record types share this one field set on purpose rather than being split into separate objects: a question needs a prompt, an answer format, and a required flag, while an FAQ entry needs a prompt (the question text) and an answer (the written response), so Prompt__c and Answer__c are reused across both with different meaning per record type. CustomQuestionService and CampaignFaqService are the read paths that resolve the ordered Selected_Question_Ids__c/Selected_FAQ_Ids__c JSON on the campaign-level selection records back into these template rows for the public donation page.

Relationships

Direction Field Related object Relationship type
← in Content_Template__c on Custom Answer Custom Answer Lookup

Fields

Field Label Type Notes
Answer__c Answer LongTextArea Answer text shown under this FAQ question (FAQ record type only).
Is_Active__c Active Checkbox Uncheck to remove this template from the picker without affecting campaigns already using it.
Is_Required__c Required Checkbox Require an answer before the donation form can be submitted (Custom Question record type only).
Picklist_Options__c Picklist Options LongTextArea Choices shown to the donor for this picklist question. Set by adding tags in the question editor — each tag becomes one selectable option (Picklist question type only).
Prompt__c Prompt LongTextArea The question text shown to the donor.
Docs: Deliberately reused across both record types rather than split into Question_Text__c and FAQ_Question__c: both CustomQuestionService and CampaignFaqService read this same field when resolving a campaign's Selected_Question_Ids__c / Selected_FAQ_Ids__c JSON list back into rendered content, so a shared name keeps both services' resolution code symmetric.
Question_Type__c Question Type Picklist How this question is rendered on the donation form (Custom Question record type only).
Docs: Rendered on the public donation form by the Custom Questions step block; Picklist is the only value with a dependent field (Picklist_Options__c); leaving it blank on a Picklist-typed template shows an empty choice list to donors.

Custom Answer#

Custom_Answer__c · custom · 4 fields · ControlledByParent · Campaigns & Content

Stores a single donor's answer to one custom question for one donation. Master-Detail to Opportunity so answers are removed along with their donation, and links to the specific reusable question template (Content_Template__c, Custom_Question record type) that was answered — this is the captured response, while the linked template is the question definition.

Docs note

Content_Template__c also hosts the unrelated FAQ record type used by CampaignFaqService — when reading this object's answers, filter/join on the Custom_Question record type rather than assuming every Content_Template__c row is a question.

Relationships

Direction Field Related object Relationship type
→ out Content_Template__c Content Template Lookup
→ out Opportunity__c Opportunity MasterDetail

Fields

Field Label Type Notes
Answer_Boolean__c Answer Boolean Checkbox Checkbox response — only populated for Checkbox-type questions.
Docs: The linked Content_Template__c's input-type field is what decides which of Answer_Boolean__c or Answer_Text__c a given answer row actually uses; this object carries both so the schema supports either question type without a polymorphic field.
Answer_Text__c Answer Text LongTextArea Text/Picklist response — only populated for non-Checkbox questions.
Content_Template__c Content Template Lookup → Content_Template__c The question template this response answers.
Docs: SetNull on delete rather than cascade or restrict, so retiring a question template from the library never deletes donors' historical answers — a Custom_Answer__c row can outlive the template it was captured against.
Opportunity__c Opportunity MasterDetail → Opportunity The donation this answer belongs to.

Donations & Payments#

Designation#

Designation__c · custom · 5 fields · ReadWrite · Donations & Payments

Represents a fund or program that donors can direct their gifts toward (e.g. "Scholarship Fund", "Building Campaign"). Exists because standard Salesforce objects have no native concept of a sub-campaign fund earmark. Donations are linked to one or more Designations via the Donation_Designation__c split junction, and recurring schedules via Recurring_Designation__c; can optionally mirror an NPSP General Accounting Unit.

Docs note

DesignationSplits is the shared allocator both one-time (DonationService) and recurring (RecurringDonationService) gifts use to parse a donor's fund choice from Options_JSON__c or a recurring schedule's stored Percentage__c rows — the actual split rows always land on Donation_Designation__c, never here; this object is just the fund catalog.

Relationships

Direction Field Related object Relationship type
← in Default_Designation__c on Campaign Campaign Lookup
← in Designation__c on Donation Designation Donation Designation Lookup
← in Designation__c on Recurring Designation Recurring Designation Lookup
← in Designation__c on Recurring Donation Recurring Donation Lookup

Fields

Field Label Type Notes
Description__c Description LongTextArea A short description of this fund shown to donors on the donation form.
GL_Code__c GL_Code Text The accounting system's ledger code for this fund.
Is_Active__c Is_Active Checkbox Uncheck to hide this fund from the donation form without deleting it.
Docs: Only filters what new donors can pick; existing Donation_Designation__c and Recurring_Designation__c rows pointing at an inactive fund keep working undisturbed, so deactivating a fund never breaks history or in-flight recurring schedules.
NPSP_GAU_Id__c NPSP_GAU_Id Text Internal reference to the matching NPSP fund record, if NPSP is installed.
Total_Raised__c Total_Raised Currency Total amount raised for this fund to date.
Docs: Recomputed by the DonationDesignations domain class on every insert/update/delete of a Donation_Designation__c split, net of refunds since it filters to Closed-Won splits only — the same domain-layer recompute path serves both one-time gifts and recurring installments, so there is no separate rollup for either gift type.

Donation Designation#

Donation_Designation__c · custom · 4 fields · ControlledByParent · Donations & Payments

Junction object between Opportunity and Designation__c that supports splitting a single donation across multiple funds or programs. Each row records the portion (amount and percentage) of one Opportunity allocated to one Designation__c; a donation split three ways has three of these rows, and Designation__c.Total_Raised__c rolls up from this object's Amount__c.

Docs note

The DonationDesignations domain class owns every write to this object and is the sole place Designation__c.Total_Raised__c gets recomputed — never write these rows directly from a service without going through the domain layer's UoW registration, or the fund rollup will silently drift.

Relationships

Direction Field Related object Relationship type
→ out Designation__c Designation Lookup
→ out Opportunity__c Opportunity MasterDetail

Fields

Field Label Type Notes
Amount__c Amount Currency The amount of the donation allocated to this fund.
Docs: DesignationSplits normalizes these amounts to sum EXACTLY to the Opportunity's charged total (remainder absorbed by the last split row), so a rounding drift in the donor's requested split can never leave the sum of a donation's Amount__c rows off from the actual charge.
Designation__c Designation Lookup → Designation__c The fund this portion of the donation supports.
Opportunity__c Opportunity MasterDetail → Opportunity The donation this fund split belongs to.
Docs: Populated for both one-time gifts (DonationService.createDesignationSplit at finalize) and recurring installments (RecurringDonationService.processRenewal at each charge) — there is no separate junction for recurring-installment splits; they land here too.
Percentage__c Percentage Percent What percentage of the donation goes to this fund.

Donation Staging#

Donation_Staging__c · custom · 24 fields · ReadWrite · Donations & Payments

Temporary staging record created by the Experience Cloud guest user during donation. Privileged async processing (Platform Event / Automated Process user) converts this into Contact + Opportunity.

Docs note

Deliberately breaks the platform's dual-field donor pattern: it carries raw Email__c/First_Name__c/Last_Name__c but no Contact__c or Donor_Account__c lookup, because donor resolution only happens at finalize time (DonorResolutionService, called from DonationService). Read by three converging paths: the guest browser poll (checkStagingPaymentStatus), StagingReconciliationBatch (runs on a schedule), and StripePaymentSweep (an orphan-PaymentIntent safety net matching on Correlation_Id__c). All three can race to finalize the same row, which is why Stripe_Intent_Id__c uniqueness is the idempotency backstop against creating two Opportunities for one PaymentIntent.

Relationships

Direction Field Related object Relationship type
→ out Campaign__c Campaign Lookup
→ out Opportunity__c Opportunity Lookup
→ out Payment_Account__c Payment Account Lookup
← in Donation_Staging__c on Transaction Log Transaction Log Lookup

Fields

Field Label Type Notes
Access_Token__c Access Token Text Internal capability token used to look up this checkout; not for manual entry.
Docs: The only credential the guest browser holds for its own staging row; every guest-context read (status polling, ticket-reservation checks) is scoped by this token rather than the record Id, since the Id alone is guessable and the guest user has no object-level access controls beyond FLS.
Amount__c Amount Currency The amount the donor is checking out with.
Campaign__c Campaign Lookup → Campaign The campaign this checkout belongs to.
Correlation_Id__c Correlation Id Text Internal id linking this checkout's log entries together; not for manual entry.
Docs: StripePaymentSweep cross-checks this against PaymentIntent.metadata.correlation_id to catch an orphan PaymentIntent whose staging row was lost or never updated — the last safety net behind the guest poll and StagingReconciliationBatch in this package's webhook-free confirmation design.
Currency_Code__c Currency Code Text Three-letter currency code for this checkout, e.g. USD.
Email__c \* Email Email The donor's email address.
Docs: Read by DonorResolutionService at finalize time to find-or-create the Contact/Person Account for this checkout; this row itself never gets a Contact__c/Donor_Account__c lookup, which is why the raw name/email fields exist here at all instead of following the usual dual-field donor pattern.
Error_Message__c Error Message Text The last error encountered processing this checkout, if any.
Expires_At__c Expires At DateTime When this unfinished checkout expires if not completed.
Docs: Set to a 30-minute hold for event-ticket stagings (EventRegistrationController) versus a much longer 72-hour window for donation stagings, since an unpaid ticket reservation blocks other buyers from that seat/capacity while a stalled donation checkout does not. StagingReconciliationBatch expires rows past this timestamp and, for ticket purchases, releases the held inventory.
Fee_Amount__c Fee Amount Currency Portion of the checkout amount that is the donor-covered processing fee, rather than the gift itself.
Docs: A typed field rather than another key in Options_JSON__c, deliberately: Options_JSON__c is donor-supplied form data echoed back verbatim, and a money value that decides what gets charged must not live in the same blob a guest can write. FeeCoverageService.computeFee is the only writer, called from DonationService.createDonorAndIntent, which recomputes the fee from the campaign flag and Settings__c rather than trusting any amount the browser sends. Amount__c is the GROSS (gift + this) — that is what the PaymentIntent charges and what Opportunity.Amount becomes, so subtracting this recovers the gift the donor chose.
Finalization_Error__c Finalization Error LongTextArea The last error encountered finalizing this checkout into a donation record.
First_Name__c First Name Text The donor's first name.
Frequency__c Frequency Picklist Whether this is a one-time or recurring checkout.
Docs: When not one_time, DonationService hands the finalized staging off to RecurringDonationService to create the Recurring_Donation__c schedule and its first installment, rather than a single Opportunity.
Idempotency_Key__c Idempotency Key Text Internal key preventing duplicate ticket checkouts; not for manual entry.
Last_Name__c Last Name Text The donor's last name.
Opportunity__c Opportunity Lookup → Opportunity The donation record created from this checkout, once completed.
Options_JSON__c Options JSON LongTextArea Internal checkout details; not for manual entry.
Docs: For Purchase_Type__c = Event_Ticket, EventPurchaseService reads and writes this JSON at every stage of the reservation/finalize/release lifecycle — it is the durable record of which tiers and quantities were reserved, so a reconcile pass can release the correct inventory without re-deriving it.
Payment_Account__c Payment Account Lookup → Payment_Account__c Which configured payment account processed this checkout.
Payment_Method_Id__c Payment Method Id Text Internal Stripe reference for the card used at checkout; not for manual entry.
Purchase_Type__c Purchase Type Text What kind of purchase this checkout is (donation or event ticket).
Docs: StagingReconciliationBatch and StripePaymentSweep both branch on this value to decide whether a terminal Failed/Expired row also needs EventPurchaseService.releaseReservation called before it is safe to close out.
Recurring_Activation_JSON__c Recurring Activation JSON LongTextArea Internal record of the donor's recurring authorization and the card they approved. Written automatically at checkout — never edit or clear it, or the recurring schedule cannot be created.
Docs: This field exists so the recurring schedule can be created in system context by StagingReconciliationBatch instead of inline in guest context. The guest user can never hold Create on Contact or Recurring_Donation__c (both carry fflib domain triggers that re-assert object CRUD), so the browser-facing step records the mandate here and creates nothing. The card snapshot is stored specifically to keep the reconciler callout-free: the batch's execute() is strictly callouts-then-DML, so a gateway.retrievePaymentMethod() at finalize time would throw "uncommitted work pending". See RecurringDonationService.ActivationSnapshot.
Reservation_Released__c Reservation Released Checkbox Whether the reserved tickets for this checkout have been released back to inventory.
Status__c Status Picklist The current stage of this checkout.
Stripe_Customer_Id__c Stripe Customer Id Text Internal Stripe reference for this checkout; not for manual entry.
Stripe_Intent_Id__c Stripe Intent Id Text Internal Stripe reference for this checkout's payment; not for manual entry.
Docs: StripePaymentSweep lists Stripe's recently-succeeded PaymentIntents and matches them back to this field to catch a staging row the guest poll and StagingReconciliationBatch both missed (e.g. the guest closed their browser before the poll ever ran).

Opportunity#

Opportunity · standard · 49 fields · Donations & Payments

Docs note

Container file only — it exists so the decomposed sub-components under this folder (fields, record types, business processes, and the Pending Company Matches list view) have the parent CustomObject the Metadata API requires; ListView in particular cannot be deployed without one. Deliberately empty: Opportunity is a standard object, and this package must not assert anything about its label, sharing model, or any other org-level setting.

See the full reference page for this object.

Relationships

Direction Field Related object Relationship type
→ out Payment_Account__c Payment Account Lookup
→ out Recurring_Donation__c Recurring Donation Lookup
→ out Soft_Credit_Contact__c Contact Lookup
→ out Soft_Credit_Donor_Account__c Account Lookup
→ out Soft_Credit_Household_Account__c Account Lookup
← in Opportunity__c on Custom Answer Custom Answer MasterDetail
← in Opportunity__c on Donation Designation Donation Designation MasterDetail
← in Opportunity__c on Donation Staging Donation Staging Lookup
← in Opportunity__c on Event Attendee Event Attendee Lookup
← in Opportunity__c on Guest Request Guest Request Lookup
← in Opportunity__c on Transaction Log Transaction Log Lookup

Fields

Field Label Type Notes
Balance_Transaction_Id__c Balance Transaction ID Text The processor's reference for this gift's settlement line. Set automatically; use it to find the transaction in your payment dashboard.
Docs: No caseSensitive setting: the platform only accepts it alongside unique=true, and uniqueness is deliberately off here (see below). Processor ids are case-sensitive in practice, so match on them exactly in any code that reads this field rather than assuming the index will do it. External ID but deliberately NOT unique, unlike Stripe_Payment_Intent_Id__c. That field's unique index is the idempotency backstop that stops two Opportunities being created for one payment; this one guards nothing, and a unique index here would turn any future re-stamp or backfill into a deploy-breaking constraint for no benefit. This is the join key a payout feature would need: the processor's balance transaction has no payout field of its own, so tying a gift to a bank deposit means listing the payout's balance transactions and matching on this id. Capturing it now is what keeps that feature a read of existing data rather than a re-fetch of every historical charge.
Check_Number__c Check Number Text Check number for this donation, if paid by check.
Company_Match_Amount__c Company Match Amount Currency The amount the employer sent to match this gift. Enter it when the match is received — record the employer's payment itself as its own donation, since this field is a reference figure only and is not counted in campaign totals.
Docs: No Apex writes this field — it exists purely for staff entry and reporting. It is excluded from every rollup on purpose (see Opportunities domain / CampaignRollupBatch, which sum Amount only). If a future release automates match intake, the employer's gift should still land as its own Opportunity and this field should stay a reference.
Company_Match_Requested_On__c Company Match Requested On DateTime When staff were first asked to chase this employer match. Set automatically — leave it alone; clearing it would cause a duplicate follow-up task on the next automation run.
Docs: The idempotency guard for CompanyMatchService.createFollowUps. Platform events are at-least-once, so DonationFinalized__e can be redelivered; the service only picks up Opportunities where this is null, and stamps it in the same UnitOfWork that registers the Task. Also doubles as the "how long has this match been sitting" clock for reporting, which is why it is a DateTime rather than a bare checkbox.
Company_Match_Status__c Company Match Status Picklist How far along the employer match is. Move it to Requested once the paperwork is filed with the employer, then Received when their gift arrives (record the amount in Company Match Amount) or Declined if they turn it down.
Docs: Stamped 'Pending' by DonationService.applyTributeAndMatch at finalize, at the same moment Company_Match__c is set — the two always move together, so a match flagged with no status means the row predates this field. Everything after Pending is a human judgement call and is left to staff; no Apex ever advances it. The "Pending Company Matches" list view and CompanyMatchService both key off Pending, so leaving a fulfilled match at Pending is what makes it keep showing up as outstanding work.
Company_Match__c Company Match Checkbox Check if the donor said their employer will match this gift.
Company_Name__c Company Name Text Donor's employer name, for company-matched gifts.
Currency_ISO_Code__c Currency ISO Code Text Three-letter currency code this gift was charged in, e.g. USD.
Docs: A plain custom Text field, not the platform's native CurrencyIsoCode — do not confuse the two. This exists because the package deliberately does not enable Salesforce multi-currency; an org that turns on native multi-currency separately is outside this package's tested scope.
Dispute_Amount__c Dispute Amount Currency Amount Stripe reports as disputed. Managed automatically — do not edit.
Dispute_Closed_Date__c Dispute Closed Date Date Date the dispute on this gift was resolved. Managed automatically.
Dispute_Evidence_Due_Date__c Dispute Evidence Due Date Date Deadline to respond to this dispute with evidence.
Dispute_Id__c Dispute ID Text Stripe's identifier for the dispute on this gift. Managed automatically.
Docs: "The reconciler" referenced in the description is RefundReconciliationBatch — the scheduled batch that polls Stripe for dispute state and writes this and the sibling Dispute_ fields; there is no dispute-specific webhook handler in StripeEventProcessor.*
Dispute_Opened_Date__c Dispute Opened Date Date Date this dispute was opened with Stripe.
Dispute_Reason__c Dispute Reason Text Reason the cardholder gave for disputing this charge.
Dispute_Status__c Dispute Status Picklist Current status of the dispute with Stripe, if any.
Donation_Type__c Donation Type Picklist What kind of donation or purchase this record represents.
Docs: Recurring_Installment is set once per charge by RecurringDonationService.applyRenewalOutcome() each time RecurringRenewalBatch runs — every installment is its own Opportunity linked back via Recurring_Donation__c, there is no single Opportunity that represents the whole schedule.
Donor_Covered_Fees__c Donor Covered Fees Checkbox Checked when the donor ticked "cover the processing fee" at checkout, so the amount on this gift includes that fee. Report on it to see how many of your donors opt in.
Docs: The take-up rate for fee coverage is the number an admin actually wants, and Fee_Amount__c > 0 cannot supply it: a gift small enough that the computed fee rounds to zero would look like a decline. Written alongside Fee_Amount__c by DonationService.processOneTimeStagingRecord and RecurringDonationService.processRenewal, from Donation_Staging__c.Fee_Amount__c and Recurring_Donation__c.Donor_Covered_Fees__c respectively. Event orders never set it — fee coverage is not offered at ticket checkout.
Event_Amount__c Event Amount Currency Portion of this gift that is not tax-deductible (e.g. the value of an event ticket).
Event_Deductible_Amount__c Event Deductible Amount Currency Tax-deductible portion of an event ticket order, for receipts.
Fee_Amount__c Fee Amount Currency Processing fee amount added by the donor, if fee coverage was selected. Included in the total Amount charged.
Docs: Written at finalize by DonationService.processOneTimeStagingRecord and RecurringDonationService.processRenewal from the value FeeCoverageService.computeFee() produced when the PaymentIntent was created; EventPurchaseService always writes 0 (ticket orders don't offer fee coverage). The gross-up is true gross-up, not gift × percent — the processor charges its percentage on the total it captures, so a $100 gift under 2.9% + $0.30 carries a $3.30 fee, not $3.20. See FeeCoverageService for the formula; it is the only place it exists. Reporting note: this is a component of Amount, not an addition to it. "Gift revenue" is Amount − Fee_Amount__c; Amount is what the card was charged. Not the same thing as the Stripe fee the org itself absorbs on a donation where the donor declined to cover it, which never appears on the Opportunity at all.
Funds_Available_On__c Funds Available On Date When this gift's money becomes available to pay out to your bank. Set automatically by the processor; it is normally a few days after the gift date.
Docs: Written once at finalize by SettlementStampHelper.applyTo from the balance transaction's available_on, which is an epoch-SECONDS timestamp converted in StripeGateway.applySettlement and read in GMT. GMT is deliberate: the processor states availability against its own clock, and re-reading it in the running user's time zone would shift the date by a day for anyone west of UTC without making it any more true. This is the availability date, not the payout date. Funds become available on this date; when they actually leave for the bank depends on the org's payout schedule, which the package does not read.
Net_Donation_Amount__c Net Donation Amount Currency Tax-deductible amount of this gift, shown on the donor's receipt.
Docs: Written at finalize by DonationService.processOneTimeStagingRecord, EventPurchaseService. processEventStagingRecord, and RecurringDonationService.processRenewal. A donor-covered processing fee (Fee_Amount__c) is deliberately NOT subtracted here. The charity received the whole charge and then paid its own processing cost out of it — the donor got nothing back — so the full amount stays deductible and subtracting the fee would under-report the deduction on every receipt that covers one. Only a genuine quid pro quo (the ticket fair-market value on event orders) reduces this figure. Do not "fix" the apparent inconsistency with Fee_Amount__c by subtracting it. ReceiptService still computes the deductible split itself from Event_Amount__c/ Event_Deductible_Amount__c rather than reading this field; the two agree by construction. Its only other consumer is lexOpportunityBreakdown.js (read-only display); remains editable on the Donation/Pledge/Grant layouts for manual correction.
Net_Settled_Amount__c Net Settled Amount Currency What your organization actually received for this gift after the processor's fee. Use this figure, not Amount, when reconciling against your bank statement.
Docs: Written once at finalize by SettlementStampHelper.applyTo, alongside Processor_Fee__c and from the same balance transaction. Taken from the processor's own net rather than computed as Amount − Processor_Fee__c: on a cross-currency charge the two are denominated in different currencies and the subtraction would silently produce a number that is not money in either of them. Not to be confused with Net_Donation_Amount__c despite the similar name — that is the TAX-DEDUCTIBLE portion (Amount less any goods or services the donor received) and exists for the receipt. This one is a CASH figure and exists for finance. They answer different questions and will usually disagree; neither is a correction of the other.
Payment_Account__c Payment Account Lookup → Payment_Account__c The configured payment account this gift was charged against.
Docs: Matters most in orgs with more than one Payment_Account__c (an org may configure several Stripe accounts and route each campaign to one via Campaign.Payment_Account__c) — a refund or dispute lookup that ignores this field and assumes a single default account will hit the wrong Stripe account's API keys.
Payment_Source__c Payment Source Picklist Payment method the donor used for this gift.
Payment_Status__c Payment Status Picklist Current payment state of this gift. Managed automatically by the payment integration.
Processor_Fee__c Processor Fee Currency The payment processor's cut of this charge, which your organization pays. Set automatically from the payment record; blank if the processor had not posted it yet.
Docs: Written once at finalize by SettlementStampHelper.applyTo, from the balance transaction expanded onto the PaymentIntent the reconciler was already fetching (see StripeGateway.applySettlement) — no extra callout on any payment path. Blank vs zero is load-bearing. The processor posts its balance transaction when the charge settles, so a gift finalized in the same instant the charge succeeded, or an ACH debit still processing, legitimately has no figure yet. Writing 0 in that case would report a free charge, so the helper leaves the field alone instead. Never treat blank as 0 in a rollup or a report — filter it out. Do NOT confuse with Fee_Amount__c: that is money the DONOR added on top and is a component of Amount; this is money the PROCESSOR removed and is not in Amount at all. A single gift can carry both, and the two are not expected to be equal even when the donor covered the fee — the gross-up is computed from the org's configured rates, the actual fee from what the processor really charged for that specific card.
Receipt_Number__c Receipt Number Text The unique number on this donor's tax receipt. Filled in automatically — when the payment succeeds for an online gift, or as soon as you mark a cheque or cash gift Closed Won. Do not type or change it, because the donor may already have a receipt quoting this number.
Docs: Case-insensitive uniqueness (caseSensitive is deliberately omitted, unlike Stripe_Payment_Intent_Id__c which sets it true). The package always generates upper-case numbers, but the prefix comes from an admin-editable setting, and "rcpt-2026-000001" must not be issuable alongside "RCPT-2026-000001" — a donor and an auditor would read those as the same receipt. Case-insensitive is the stricter choice for uniqueness, so it is the one a compliance field should take. This field replaced a fake receipt number that the guest receipt LWC derived from Receipt_Token__c (its last 8 characters). That was wrong twice over: it was not a serial number in any sense, and Receipt_Token__c is the bearer credential that authorizes access to the receipt, so the old label printed part of a secret onto a document donors email, print and hand to an accountant. Never source a user-visible reference from Receipt_Token__c. Two write paths, not one (the second added 2026-08-07, gap-audit §3.3). The three online paths — DonationService, EventPurchaseService, RecurringDonationService — stamp the number on the same Unit of Work that finalizes the gift. Everything else is numbered by Opportunities.stampOfflineReceiptNumber in the BEFORE trigger, which covers the cheque and cash gifts staff key in by hand; those used to print "—" on the year-end statement beside online gifts carrying a real serial, which made the org's numbering look holed. The trigger skips a gift that already has a number, so the online paths always win and no gift is ever numbered twice. Pledge record types are excluded — a Pledge is unpaid by definition. Deliberately NOT an Auto Number field. Auto Number stamps every Opportunity at insert, including pledges, abandoned checkouts and gifts that never complete, so the receipt series would be mostly holes; its format is frozen once packaged; and it cannot restart each tax year. See ReceiptNumberService for the counter that replaces it.
Receipt_Token__c Receipt Token Text Internal security token for the public receipt link. Do not share or expose this value.
Recurring_Donation__c Recurring Donation Lookup → Recurring_Donation__c The recurring donation schedule this installment belongs to, if any.
Docs: Set by RecurringDonationService.applyRenewalOutcome() when the daily RecurringRenewalBatch charges an installment off-session against the parent Recurring_Donation__c's stored Stripe_Customer_Id__c/Stripe_Payment_Method_Id__c. There is no Stripe Subscription object behind this relationship — the schedule is entirely self-managed on this package's side.
Refund_Date__c Refund Date Date Date this gift was last refunded, if applicable.
Refund_Reason__c Refund Reason LongTextArea Reason given for the most recent refund on this gift.
Refunded_Amount__c Refunded Amount Currency Total amount refunded to the donor so far. Managed automatically — never edit directly.
Soft_Credit_Contact__c Soft Credit Contact Lookup → Contact The person who should get credit for this gift even though someone else paid for it. Their giving history and recognition reports will include this gift; the gift itself still belongs to the donor who paid.
Docs: One of the standard Person Account dual-field pairs — this field and Soft_Credit_Donor_Account__c hold the SAME individual, populated according to which account model the org runs. Soft_Credit_Household_Account__c is deliberately NOT part of that pair: it holds a different party (the donor's household), so a tribute honoree and a household credit can sit on one gift without competing for the slot. Written by SoftCreditService off the DonationFinalized__e subscriber, and by staff directly on the record. Automation only ever fills it when it is blank — see Soft_Credit_Source__c for how a hand-entered value is protected.
Soft_Credit_Donor_Account__c Soft Credit Donor Account Lookup → Account The person who should get credit for this gift even though someone else paid for it. Use this field if your org uses Person Accounts; otherwise use Soft Credit Contact.
Docs: The Person Account half of the package-wide dual-lookup rule. Never populated at the same time as Soft_Credit_Contact__c — they are two spellings of one person, and which one is used is decided by DonorResolutionService.isPersonAccountOrg(), never by branching on IsPersonAccount at the call site. This is NOT the household field. Soft_Credit_Household_Account__c holds a different party and may be populated alongside either half of this pair.
Soft_Credit_Household_Account__c Soft Credit Household Lookup → Account The household or organization that should share credit for this gift alongside the individual donor. Leave blank if the gift is already recorded against that household.
Docs: Deliberately a THIRD field rather than a third value of the dual pair above. The pair (Soft_Credit_Contact__c / Soft_Credit_Donor_Account__c) holds one individual in two org shapes; this holds a different party entirely. Keeping them separate is what lets a tribute honoree and a household credit coexist on one gift instead of overwriting each other. Filled by SoftCreditService from the donor Contact's AccountId, and ONLY when that account differs from the donation's own AccountId — in the common household-account org the two are the same and this field would be a duplicate of a value already on the record.
Soft_Credit_Source__c Soft Credit Source Picklist Shows whether the soft credit was worked out from the gift's tribute details or entered by hand. Anything marked Manual is left alone by automation.
Docs: The provenance marker that makes "manual beats automation" enforceable. SoftCreditService writes 'Tribute Honoree' in the same save as the lookup it resolved; the Opportunities domain stamps 'Manual' when a soft-credit lookup changes without the source changing alongside it, which is exactly the shape of a person editing the record by hand. It describes the individual pair only. Soft_Credit_Household_Account__c has one write path (SoftCreditService) and so needs no provenance of its own.
Stripe_Payment_Intent_Id__c Stripe Payment Intent ID Text Stripe's identifier for the payment behind this gift. Set automatically.
Tribute_Ecard_Requested__c Tribute Ecard Requested Checkbox Leave unchecked unless the donor explicitly asked you to notify the honoree or their family. Unchecked means no e-card is ever sent, even if a notification email address is filled in — so untick this rather than clearing the address when a donor changes their mind.
Docs: Added as a plain record of the donor's choice, but nothing read it until the 2026-08-07 gap audit (docs/gap-audit-2026-08-07.md, finding 1.1) found the e-card sending on the presence of Tribute_Notification_Email__c alone. Two writers keep it honest: DonationService.applyTributeAndMatch maps the form's tributeEcardRequested onto it, and dfForm clears the address whenever the box is unticked.
Tribute_Message__c Tribute Message LongTextArea Personal message to include in the tribute notification, if any.
Tribute_Name__c Tribute Name Text Name of the person this gift honors or memorializes.
Tribute_Notification_Email__c Tribute Notification Email Email Email address to notify about this tribute gift, if applicable.
Tribute_Type__c Tribute Type Picklist Select whether this gift is a tribute, and of which kind.
URL_Parameters__c URL Parameters LongTextArea The full set of tracking parameters from the link the donor clicked, stored as JSON. Reference only — report on the UTM fields instead.
Docs: Set by DonationService.applyAttribution() (one-time and event checkout) and copied onto every renewal installment by RecurringDonationService, so an installment carries the attribution of the signup that created it rather than of the night the renewal ran. Source is Donation_Staging__c.Options_JSON__c -> attribution, normalized by AttributionCapture.
UTM_Campaign__c UTM Campaign Text The marketing appeal name from the link, e.g. year-end-2026. This is the marketer's label, not the Salesforce Campaign — filled in automatically.
Docs: Set by DonationService.applyAttribution() (one-time and event checkout) and copied onto every renewal installment by RecurringDonationService, so an installment carries the attribution of the signup that created it rather than of the night the renewal ran. Source is Donation_Staging__c.Options_JSON__c -> attribution, normalized by AttributionCapture.
UTM_Content__c UTM Content Text Which link or creative variant was clicked, for A/B tests. Filled in automatically — usually blank.
Docs: Set by DonationService.applyAttribution() (one-time and event checkout) and copied onto every renewal installment by RecurringDonationService, so an installment carries the attribution of the signup that created it rather than of the night the renewal ran. Source is Donation_Staging__c.Options_JSON__c -> attribution, normalized by AttributionCapture.
UTM_Medium__c UTM Medium Text The channel that carried the link, e.g. email, cpc or social. Filled in automatically — leave it alone.
Docs: Set by DonationService.applyAttribution() (one-time and event checkout) and copied onto every renewal installment by RecurringDonationService, so an installment carries the attribution of the signup that created it rather than of the night the renewal ran. Source is Donation_Staging__c.Options_JSON__c -> attribution, normalized by AttributionCapture.
UTM_Source__c UTM Source Text Where this gift came from, e.g. newsletter or facebook. Filled in automatically from the link the donor clicked — leave it alone.
Docs: Set by DonationService.applyAttribution() (one-time and event checkout) and copied onto every renewal installment by RecurringDonationService, so an installment carries the attribution of the signup that created it rather than of the night the renewal ran. Source is Donation_Staging__c.Options_JSON__c -> attribution, normalized by AttributionCapture.
UTM_Term__c UTM Term Text The paid-search keyword that led here, if any. Filled in automatically — usually blank.
Docs: Set by DonationService.applyAttribution() (one-time and event checkout) and copied onto every renewal installment by RecurringDonationService, so an installment carries the attribution of the signup that created it rather than of the night the renewal ran. Source is Donation_Staging__c.Options_JSON__c -> attribution, normalized by AttributionCapture.

OpportunityLineItem#

OpportunityLineItem · standard · 2 fields · Donations & Payments

Relationships

Direction Field Related object Relationship type
→ out Event_Ticket_Type__c Event Ticket Type Lookup
← in Opportunity_Line_Item__c on Event Attendee Event Attendee Lookup

Fields

Field Label Type Notes
Event_Amount__c Event Amount Currency Portion of this line that is not tax-deductible (value of goods/services received).
Docs: Summed up to the parent Opportunity.Event_Amount__c (all ticket lines on one order), and read by ReceiptService when building the tax receipt so the deductible amount printed for a donor excludes the value of what they received.
Event_Ticket_Type__c Event Ticket Type Lookup → Event_Ticket_Type__c The ticket type sold on this line.
Docs: Populated by EventPurchaseService at ticket-order finalization; EventAvailabilityService reads the inverse relationship (Line_Items) to compute remaining inventory per tier.

Payment Account#

Payment_Account__c · custom · 8 fields · ReadWrite · Donations & Payments

Stores one record per set of gateway credentials the org has configured (e.g. a Stripe account used for general donations, or a second Stripe account restricted to one program). Admins create and manage these from the setup console with no metadata deployment required; each account's secret key lives in its own Named Credential and is only referenced here by name. Each campaign points at exactly one of these through Campaign.Payment_Account__c, which is the account every gift on that campaign settles to.

Docs note

PaymentGatewayFactory is the join point between this object and the Apex that talks to the processor: it reads Gateway__c off the chosen account and looks the value up in PaymentGatewayRegistry, which returns the IPaymentGateway implementation used for the actual callout. Until 2026-08 that lookup went through a Payment_Gateway__mdt custom metadata type and a Type.forName() on a stored class name; the registry replaced it so an unshippable gateway value can't be configured and the class reference is checked by the compiler. Deactivating a Payment_Account__c (Is_Active__c = false) rather than deleting it is what keeps historical Opportunity/refund references intact.

Relationships

Direction Field Related object Relationship type
← in Payment_Account__c on Campaign Campaign Lookup
← in Payment_Account__c on Donation Staging Donation Staging Lookup
← in Payment_Account__c on Opportunity Opportunity Lookup
← in Payment_Account__c on Payment Method Payment Method Lookup
← in Payment_Account__c on Recurring Donation Recurring Donation Lookup

Fields

Field Label Type Notes
Admin_Named_Credential__c \* Admin Named Credential Text Named Credential API name holding the full secret key. Grant it to staff who issue refunds and to whoever owns the scheduled jobs — never to your site guest user. May name the same credential as the public field if you prefer to run one key.
Docs: Added 2026-08-11 to split one credential into two roles. Before this field, every Stripe callout ran on Named_Credential__c, and the setup guide told the admin to grant that one credential to the Experience Cloud guest user - so an anonymous visitor's principal could issue refunds. Named_Credential__c is now the restricted, charge-only key the guest holds; this field is the full key nothing guest-reachable ever names. Required for the same reason Named_Credential__c is: blank resolves the endpoint to 'callout:null', which fails when a refund is attempted rather than when the account is saved. AdminCredentialBackfillBatch fills it from Named_Credential__c on upgrade so no pre-existing account becomes unsavable; those accounts land in shared mode, which the setup guide reports as amber rather than green.
Display_Label__c \* Display_Label Text Label donors see on the payment method picker.
Docs: Required: the Settings console has always rejected a blank label, and every list view, lookup and payment-method picker shows this value rather than Name (which the console copies from it and never exposes). A record without one is unusable in the UI that creates it.
Gateway__c Gateway Picklist The payment processor these credentials belong to. Only processors this app supports are listed.
Docs: The value set here is kept identical to the keys of PaymentGatewayRegistry, which holds the IPaymentGateway class for each gateway. PaymentGatewayFactory reads this field and asks the registry for the implementation, so adding a gateway means adding BOTH a registry entry and a value here. PaymentGatewayRegistry_Test.test_registryMatchesGatewayPicklist fails the build if the two ever drift apart.
Is_Active__c Is_Active Checkbox Only active accounts appear on donation forms and campaign assignment lists.
Mode__c Mode Picklist Test = sandbox keys, no real money moves. Live = real donations. Derived from the publishable key when you save, so it always matches the keys you entered.
Docs: Set on save by SetupController from IPaymentGateway.classifyPublishableKey(), never typed by an admin — a hand-entered value could disagree with the keys actually stored, which is exactly the confusion this field exists to prevent. Kept gateway-agnostic through the interface rather than being a formula on Stripe's pk_test_/pk_live_ prefix, so a second gateway with a different key format needs no data-model change. Unknown is the honest answer for a key shape the gateway does not recognise; it is not treated as Live anywhere.
Named_Credential__c \* Public Named Credential Text Named Credential API name holding a restricted, charge-only key. Your site guest user is granted this one, so scope the key to Customers, PaymentIntents, SetupIntents and PaymentMethods, plus READ on Charges and Balance transactions — and no Refunds, no Disputes. Those two read scopes are what let the app confirm each gift, record the processor fee, and notice a refund issued in the gateway dashboard. Never store the secret itself here.
Docs: The Charges and Balance transactions READ scopes are not optional and must not be trimmed back: StripeGateway.retrievePaymentIntent asks the gateway to expand latest_charge.balance_transaction on every payment-intent read, so the request touches a charge and a balance transaction as well as the intent. A key scoped without them either fails the request outright or returns it without the expansion - and the second outcome is silent, leaving every donation with a null processor fee and net, and leaving RefundReconciliationBatch reading latest_charge.amount_refunded as zero, so a refund issued in the gateway dashboard is never detected. That read is reached from the guest donation and event-registration status polls as well as from StagingReconciliationBatch and RefundReconciliationBatch, so the same scope covers both the public and the back-office use. StripeGateway builds its donor-facing callout endpoints as 'callout:' + Named_Credential__c. Back-office endpoints use Admin_Named_Credential__c instead - see that field's comment for why the pair was split on 2026-08-11. There is no package-level fallback credential, because the package stopped shipping Named Credentials in 2026-08 (a packaged credential can carry no secret, so it installed as a dead shell an admin still had to fill in). Every account names credentials the subscriber org owns, which is also what lets two accounts point at two different processor accounts. Required: blank makes the endpoint 'callout:null', which fails at the moment a donor pays rather than at the moment the account is saved.
Payment_Sweep_Cursor__c Payment Sweep Cursor DateTime Set automatically by the payment recovery job. Leave blank; clearing it just makes the next run re-check a wider window.
Docs: Per-account rather than org-wide because the sweep REPAIRS staging rows: re-scanning a window already cleared for this account is wasted callouts, and a shared cursor would let one account's progress mark another account's window as swept. This is the field that makes StripePaymentSweep safe to run across every active Payment_Account__c — before it existed the sweep used PaymentGatewayFactory.getDefault() and only ever saw the alphabetically-first account, leaving abandoned checkouts on every other account charged at the processor and unrepaired in Salesforce. Contrast Settings__c.Refund_Reconcile_Cursor__c / Dispute_Reconcile_Cursor__c, which stay org-wide on purpose: listing refunds is idempotent, so a shared cursor there costs only a cheap re-list.
Publishable_Key__c \* Publishable_Key Text Client-side publishable key. Safe to expose to the browser; not the secret key.
Docs: Required: the browser-side payment element cannot initialise without it, so an account missing this value renders a checkout that can never take a payment. It is also what Mode__c is derived from at save time (IPaymentGateway.classifyPublishableKey).

Payment Method#

Payment_Method__c · custom · 16 fields · ReadWrite · Donations & Payments

Stores a donor's saved payment method as a reference to a tokenized record in the payment gateway (e.g. a Stripe PaymentMethod/Customer ID), so the donor can reuse it for future one-time gifts or recurring installments without re-entering card details. Only non-sensitive display data (card brand, last 4 digits, expiry month/year) and gateway reference IDs are stored — the platform never stores the full card number, CVV, or bank account number.

Docs note

Populated only by PaymentMethodService.saveWalletCard, called from exactly three places in RecurringDonationService (recurring signup, donor-portal card change, reinstate-with-a-new-card) — a one-time gift never writes this object, since Stripe's setup_future_usage is only requested for recurring signups. Rows are insert-only: a Fingerprint__c match is never refreshed in place, because the guest license can never receive object-level Edit on a custom object, only Insert.

See the full reference page for this object.

Relationships

Direction Field Related object Relationship type
→ out Contact__c Contact Lookup
→ out Donor_Account__c Account Lookup
→ out Payment_Account__c Payment Account Lookup
← in Payment_Method__c on Recurring Donation Recurring Donation Lookup

Fields

Field Label Type Notes
Bank_Name__c Bank_Name Text Bank name shown for ACH payment methods. Not populated for cards.
Brand__c Brand Text Card network (Visa, Mastercard, Amex, Discover). Blank for ACH.
Contact__c Contact Lookup → Contact Owning contact in standard (non-Person Account) orgs.
Donor_Account__c Donor Account Lookup → Account Owning Person Account in Person Account orgs.
Exp_Month__c Exp_Month Number Card expiry month, for display only. Blank for ACH.
Exp_Year__c Exp_Year Number Card expiry year, for display only. Blank for ACH.
Fingerprint__c Fingerprint Text Gateway fingerprint used to detect duplicate payment methods.
Docs: PaymentMethodService.saveWalletCard matches on this before inserting; a hit returns the existing row untouched with no DML at all rather than a duplicate, since the guest context all 3 callers run in can never get object-level Edit on this object to update it.
Is_Default__c Is_Default Checkbox Checked if this is the donor's default payment method.
Is_Valid__c Is_Valid Checkbox Unchecked when the gateway reports this method as no longer usable.
Docs: PaymentMethodsSelector.selectByDonor filters WHERE Is_Valid__c = true so an invalid method never surfaces as a choice in the donor-facing picker, while selectByDonorSystem (used by the guest/portal write paths) reads all rows so Fingerprint__c dedup still finds them.
Last4__c Last4 Text Last 4 digits shown to identify the method. Never the full card/account number.
Payment_Account__c Payment Account Lookup → Payment_Account__c Which of your configured Stripe accounts holds this saved card. Set automatically when the donor saves the card and must not be changed by hand — the card only works with the account listed here.
Docs: Added 2026-08-07 to close a live renewal bug. Before this field the renewal batch re-derived the Stripe account from Campaign.Payment_Account__c on every run, so re-pointing a campaign at a second Stripe account made every in-flight schedule charge account-A tokens against account B; Stripe answers resource_missing and the batch fed that into the dunning cadence as though the donor's card had declined. deleteConstraint is deliberately omitted (SetNull), matching Opportunity.Payment_Account__c and Donation_Staging__c.Payment_Account__c: this is a historical transactional reference, not a config link. The hard block on deleting an in-use account lives on Campaign.Payment_Account__c (Restrict) plus SetupController.DeletePaymentAccount's friendly guard, and every wallet row is reachable through a campaign, so nothing is lost by not repeating it here.
Removed_Date__c Removed Date DateTime The date and time the donor removed this card themselves from their online account. Blank means the card is still available to them. Useful when a donor asks why a card stopped being offered, or when reconciling a gift that was charged shortly before the card was removed.
Docs: Companion to Removed__c — set in the same SYSTEM_MODE update so the two never disagree. Nothing queries on it; it exists so support can answer "when did this card disappear from my account?" without reading field history, which is not enabled on this object.
Removed__c Removed Checkbox Ticked when the donor removed this card themselves from their online account. The card stays on file so past gifts and any refunds still trace back to it, but it is no longer offered when someone gives again or when a recurring gift is set up. Do not tick or untick this yourself — ask the donor to remove or re-add the card from their donor portal instead.
Docs: A soft remove, for three reasons: the Guest License can never hold object Delete; historic Donation__c rows need Stripe_Payment_Method_Id__c to stay resolvable for refund traceability; and DonorPortalService.removeWalletCard refuses removal outright while an Active, Paused or Failed Recurring_Donation__c still points at the card, so a removed row can never be one a live schedule depends on (Failed blocks too — a dunning donor who removed the card would have nothing left to fix). Removal makes NO Stripe detach callout — the site-context guard added to the gateway refuses back-office Stripe calls from a site context by design, and the donor's click path must not carry a callout. PaymentMethodsSelector filters Removed__c = true out of the two wallet-listing methods (selectValidByDonor, selectValidByDonorSystem). selectByDonorSystem is deliberately NOT filtered — it is the dedup query, and Stripe_Payment_Method_Id__c is unique, so hiding a removed row there would make re-adding the same card die on the uniqueness constraint instead of reinstating it. selectById / selectDetailById stay unfiltered so back-office lookups still resolve historic rows.
Stripe_Customer_Id__c Stripe Customer ID Text Stripe Customer reference ID (cus_...). Not a secret.
Stripe_Payment_Method_Id__c Stripe Payment Method ID Text Stripe PaymentMethod reference ID (pm_...). Not a secret.
Docs: The billing-critical copy of this id actually lives on Recurring_Donation__c, stamped directly by the caller of PaymentMethodService.saveWalletCard — this wallet-row copy is display/dedup metadata only, which is what makes the insert-only (never-updated) row safe under the guest-context restriction.
Type__c Type Picklist Card or US Bank Account (ACH).

Receipt Sequence#

Receipt_Sequence__c · custom · 2 fields · ReadWrite · Donations & Payments

Holds the last receipt number issued for each tax year, so official donation receipts can carry the unique serial number tax authorities require. One row per tax year, created automatically the first time a gift is receipted in that year. This is machine-maintained bookkeeping, not a record staff work with — editing a row by hand will cause the next receipt to reuse a number that has already been sent to a donor.

Docs note

Deliberately has NO tab and is not searchable or reportable: it is an internal counter, and every way of surfacing it to an admin is a way for someone to "tidy up" a row and cause duplicate receipt numbers. Field history IS on, because if a number ever does get reused the only useful question is who changed the counter and when.

Written exclusively by ReceiptNumberService.assign(), which reads the row with FOR UPDATE and bumps Last_Number__c inside the same Unit of Work that saves the receipted gift. Reading the current value is safe from anywhere; writing it from anything other than that service is not. See ReceiptNumberService for why the lock is cheap here (every finalize path is batch Apex, so there is no synchronous high-concurrency writer to contend with).

Relationships

No lookup/master-detail relationships to or from this object.

Fields

Field Label Type Notes
Last_Number__c Last Number Issued Number The last receipt number issued this tax year. Do not edit — lowering it makes the next receipt reuse a number a donor has already been sent, and tax authorities require every receipt number to be unique.
Docs: Gaps in this sequence are expected and acceptable. A finalize transaction that rolls back after allocating (a failed save, a lock timeout, a batch chunk error) leaves its number permanently unused. The requirement receipts must meet is that each number is UNIQUE, not that the run is unbroken, so the package trades gaplessness for not serialising every gift behind a single row.
Tax_Year__c Tax Year Number The tax year whose receipt numbers this row counts. Set automatically; do not change it — the receipts already issued for this year cannot be renumbered.

Transaction Log#

Transaction_Log__c · custom · 8 fields · ReadWrite · Donations & Payments

Immutable audit log of payment-gateway API calls the platform makes (create/retrieve PaymentIntent, charge off-session, refund, list-recent-succeeded). There is no inbound webhook in this package, so nothing is ever received here — every row records an outbound callout this org initiated and its outcome, used to reconcile finalized donations and diagnose failed charges.

Docs note

Currently written only by three schedulable/batch classes: StagingReconciliationBatch, StripePaymentSweep, and ReconciliationHeartbeat — there is no synchronous write path from the guest checkout controllers themselves. Event_Type__c mixes gateway-native event names with the reconciler's own internal vocabulary, so filter on it carefully when reporting.

Relationships

Direction Field Related object Relationship type
→ out Donation_Staging__c Donation Staging Lookup
→ out Opportunity__c Opportunity Lookup
→ out Recurring_Donation__c Recurring Donation Lookup

Fields

Field Label Type Notes
Donation_Staging__c Donation Staging Lookup → Donation_Staging__c Related staging record, if this callout occurred before donation finalize.
Error__c Error LongTextArea Error detail when this callout or its processing failed.
Event_Type__c Event Type Text The API call/outcome this row records, e.g. payment_intent.succeeded.
Docs: StagingReconciliationBatch writes its own internal vocabulary here too (RECONCILE, RECONCILE_FINALIZED, RECONCILE_EXPIRED, RECONCILE_ERROR, etc.), not just gateway-native event names — so this field mixes "what gateway call happened" with "what the reconciler decided to do about it."
Opportunity__c Opportunity Lookup → Opportunity Related donation Opportunity, if applicable.
Payload__c Payload LongTextArea Raw JSON response from the gateway callout, for troubleshooting.
Processed_At__c Processed At DateTime When this record was successfully processed. Blank until complete.
Recurring_Donation__c Recurring Donation Lookup → Recurring_Donation__c Related recurring donation, if this callout was for a renewal charge.
Docs: Distinguishes a renewal-charge row from the Donation_Staging__c-linked rows written during guest checkout (StagingReconciliationBatch, StripePaymentSweep, ReconciliationHeartbeat) — those three are currently the only writers of this object, so this lookup is populated only if/when a renewal-charge path is wired to log here the same way.
Status__c Status Picklist Processing state of this callout (Received, Processing, Processed, Failed, Duplicate, Rejected).

Events & Ticketing#

Event Attendee#

Event_Attendee__c · custom · 10 fields · Private · Events & Ticketing

Tracks each individual attendee registered against a purchased event ticket line, whether or not they have a Contact/Account record in Salesforce. A ticket tier configured for more than one attendee per ticket (e.g. a table) produces multiple Event Attendee records against the same order line, and each row carries its own check-in status so staff can track arrivals at the door.

Docs note

Modeled after the proven Blackthorn events pattern of tracking attendees independently of Contact/Lead so a ticket buyer can register guests who have never interacted with the org before (e.g. a table of coworkers). Rows are created when a ticket order finalizes, one per seat per Event_Ticket_Type__c.Attendees_Per_Ticket__c.

Relationships

Direction Field Related object Relationship type
→ out Campaign__c Campaign Lookup
→ out Contact__c Contact Lookup
→ out Donor_Account__c Account Lookup
→ out Opportunity_Line_Item__c OpportunityLineItem Lookup
→ out Opportunity__c Opportunity Lookup
→ out Ticket_Type__c Product2 Lookup

Fields

Field Label Type Notes
Campaign__c Campaign Lookup → Campaign The event this person is attending.
Check_In_Status__c Check-In Status Picklist Update as the attendee checks in at the event, or mark them as a no-show or cancellation.
Docs: Written by EventCheckInController from the door-staff check-in UI; EventTicketsController and FundraiserDetailController both read it to show live check-in progress on the admin event console.
Contact__c Contact Lookup → Contact Linked Contact record, if this attendee matches a known donor.
Donor_Account__c Donor Account Lookup → Account Linked Person Account record, if this attendee matches a known donor (Person Account orgs only).
Email__c Email Email Attendee's email address, as entered at checkout.
First_Name__c First Name Text Attendee's first name.
Last_Name__c Last Name Text Attendee's last name.
Opportunity_Line_Item__c Opportunity Line Item Lookup → OpportunityLineItem The ticket order line this attendee's seat belongs to.
Opportunity__c Opportunity Lookup → Opportunity The order (donation) this attendee's ticket was purchased on.
Ticket_Type__c Ticket Type Lookup → Product2 The ticket-type template (e.g. General Admission) this attendee purchased.

Event Ticket Type#

Event_Ticket_Type__c · custom · 13 fields · ControlledByParent · Events & Ticketing

Defines one purchasable ticket tier for a specific event (Campaign), such as "General Admission" or "VIP Table," including its price, capacity, sale window, and tax-deductible split. Master-Detail to Campaign so a tier is removed along with its event and its sales roll up into the event's Tickets_Sold__c total; optionally linked to a reusable Product2 ticket-type template that seeds its defaults.

Docs note

EventTicketTypesSelector takes a raw-SOQL FOR UPDATE row lock on the selected tiers during reservation (WITH SYSTEM_MODE, since the caller is the guest-context EventPurchaseService and fflib's QueryFactory builder can't express FOR UPDATE) — this is what prevents two concurrent checkouts from both reserving the last seat in a limited-capacity tier.

Relationships

Direction Field Related object Relationship type
→ out Campaign__c Campaign MasterDetail
→ out Product2__c Product2 Lookup
← in Event_Ticket_Type__c on OpportunityLineItem OpportunityLineItem Lookup

Fields

Field Label Type Notes
Attendees_Per_Ticket__c Attendees Per Ticket Number How many people one ticket covers, e.g. 8 for a table of 8.
Docs: A tier with this set greater than 1 produces N Event_Attendee__c rows against the same OpportunityLineItem when the order finalizes, one per seat, so each attendee can be checked in individually at the door even though only one ticket line was purchased.
Campaign__c Campaign MasterDetail → Campaign The event this ticket tier is sold for.
Capacity__c Capacity Number Leave blank for unlimited capacity.
Docs: Only enforced by EventRegistrationController.createTicketIntent when the parent Campaign's Capacity_Mode__c is Per_Tier or Both; under Event_Total mode this per-tier cap is ignored and only Campaign.Event_Capacity__c governs how many seats can be sold across all tiers combined.
Deductible_Percentage__c Deductible Percentage Percent % of the ticket price that is tax-deductible. Required while this org calculates deductions by percentage — enter 0 if none of the ticket price is deductible.
Docs: Required-by-validation-rule, not required-by-field: Deductible_Pct_Required_In_Pct_Mode enforces it only while Settings__c.Event_Deductible_Mode__c is "Percentage", and only on insert or on an edit to this field, so the sold-seat counter the public checkout writes cannot be blocked by it. A field-level required flag would instead force every Fair-Market-Value org to fill a column it never uses. Added 2026-08-19 under audit F-81, after a blank value here silently produced receipts claiming the whole ticket price as tax-deductible.
Description__c Description TextArea Say what the buyer gets for this price (for example "Two premium seats, sparkling reception, and your names in the programme"). Leave blank if the tier name says it all.
Docs: Plain TextArea, not Rich Text, on purpose: this string is rendered into a guest Experience Cloud page by publicEventRegistration, and plain text cannot carry markup that would need sanitising before it reaches an unauthenticated visitor. Line breaks are preserved by CSS (white-space) rather than by HTML in the value.
Fair_Market_Value__c Fair Market Value Currency Dollar value of what the ticket buyer receives (e.g. dinner) — reduces the tax-deductible amount.
Highlight_Label__c Highlight Label Text Type a short badge such as "Most popular" to draw attention to this tier. Leave blank for every tier you do not want to single out — highlight only one per event, or the badge stops meaning anything.
Docs: One highlight per event is an editing convention enforced server-side by EventTicketAdminController.demoteOtherHighlights (badging one tier clears the label on the event's others), not a platform constraint — data loaded through the API or Data Loader bypasses that method entirely and can badge several tiers, and publicEventRegistration renders whatever it is given rather than silently picking a winner. Deliberately free text rather than a picklist so an org can say "Sells out first" or "Table of 10" without a package upgrade; it is rendered as text content, never as markup.
Is_Active__c Is Active Checkbox Uncheck to hide this ticket tier from new purchases without deleting past sales history.
Price__c Price Currency Price per ticket for this event (editable independent of the ticket-type template).
Product2__c Product Lookup → Product2 The reusable ticket-type template this tier was based on, if any.
Quantity_Sold__c Quantity Sold Number Tickets sold so far for this tier (updated automatically as purchases complete).
Docs: Counts sold-OR-reserved seats, not just paid ones: EventRegistrationController.createTicketIntent increments this under the tier's FOR UPDATE row lock at reservation time (before payment confirms), so two concurrent checkouts can never both reserve the same last seat; a reservation that never completes is decremented back by EventPurchaseService.releaseReservation.
Sale_End_Date__c Sale End Date DateTime When sales for this tier close. Leave blank to sell until sold out or the event ends.
Sale_Start_Date__c Sale Start Date DateTime When this tier goes on sale. Leave blank to make it available immediately.

Product2#

Product2 · standard · 4 fields · Events & Ticketing

Relationships

Direction Field Related object Relationship type
← in Ticket_Type__c on Event Attendee Event Attendee Lookup
← in Product2__c on Event Ticket Type Event Ticket Type Lookup

Fields

Field Label Type Notes
Attendees_Per_Ticket__c Attendees Per Ticket Number Number of attendees included per ticket (1 for an individual ticket).
Docs: EventPurchaseService reads the per-event Event_Ticket_Type__c copy of this value (not this template field) to multiply ticket quantity into attendee headcount when generating Event_Attendee__c records — this Product2 field only prefills a new tier via EventTicketAdminController, it is never read directly at purchase time.
Deductible_Percentage__c Deductible Percentage Percent Default % of this ticket's price that is tax-deductible.
Fair_Market_Value__c Fair Market Value Currency Default non-deductible value of goods/services for this ticket type.
Docs: Only used at purchase time when Settings__c.Event_Deductible_Mode__c is set to a mode other than Percentage — EventPurchaseService clamps the resulting deductible amount to [0, totalPrice] either way, so a stale or oversized FMV template value can never push the donation's deductible portion negative or above the price paid.
Suggested_Price__c Suggested Price Currency Default price shown when this ticket template is reused for a new event.
Docs: Read only by EventTicketAdminController's prefill path when an admin picks this product for a new event tier; changing it after tiers already exist has no retroactive effect on those tiers' own Price__c.

Recurring Giving#

Recurring Designation#

Recurring_Designation__c · custom · 4 fields · ControlledByParent · Recurring Giving

Junction object between Recurring_Donation__c and Designation__c that stores the fund-allocation template a donor set on a recurring schedule. Each renewal installment copies these rows into Donation_Designation__c on the new Opportunity (allocated by Percentage__c against the captured amount), so recurring gifts carry multi-fund splits at parity with one-time gifts. Populated at schedule creation by RecurringDonationService.

Docs note

DesignationSplits.allocateByPercentage is the actual renewal-time allocator (called from RecurringRenewalBatch/RecurringDonationService.processRenewal) — it distributes the captured installment amount across these Percentage__c rows, putting any rounding remainder on the last row so the split always sums exactly to the charged amount.

Relationships

Direction Field Related object Relationship type
→ out Designation__c Designation Lookup
→ out Recurring_Donation__c Recurring Donation MasterDetail

Fields

Field Label Type Notes
Amount__c Amount Currency The amount of each recurring installment allocated to this fund.
Designation__c Designation Lookup → Designation__c The fund this portion of the recurring gift supports.
Percentage__c Percentage Percent What percentage of each recurring installment goes to this fund.
Docs: A blank value here does not error at renewal — DesignationSplits.allocateByPercentage falls back to an even share across the schedule's fund rows when Percentage__c is null.
Recurring_Donation__c Recurring Donation MasterDetail → Recurring_Donation__c The recurring gift this fund split belongs to.
Docs: Selected via RecurringDesignationsSelector when a renewal runs; do not confuse this parent lookup with Donation_Designation__c.Opportunity__c on the per-installment split rows these templates generate — the two junctions look alike but sit on different objects.

Recurring Donation#

Recurring_Donation__c · custom · 39 fields · ReadWrite · Recurring Giving

Represents a donor's ongoing recurring giving schedule (subscription profile) supporting a campaign. This package self-manages the billing schedule: a daily scheduler charges each installment off-session against a saved Stripe payment method, so there is no native Stripe subscription object involved. Related to the donor via the dual Contact/Person-Account lookup pattern and to Designation__c/Recurring_Designation__c for fund allocation; each state change or payment attempt is logged to a child Recurring_Donation_History__c record.

Docs note

The description's "each state change or payment attempt is logged to a child Recurring_Donation_History__c record" is aspirational, not current behavior — see the doc note on Recurring_Donation_History__c: only the initial Created event is written automatically today. Self-managed (Phase B) means there is no Stripe Subscription object anywhere in this flow; RecurringRenewalBatch drives every charge off-session via IPaymentGateway.chargeOffSession against Stripe_Customer_Id__c/Stripe_Payment_Method_Id__c, and RecurringDonationService. processRenewal is the dunning state machine interpreting each charge outcome. When the optional NPSP extension is detected, this object maps to npe03__Recurring_Donation__c rather than replacing it.

See the full reference page for this object.

Relationships

Direction Field Related object Relationship type
→ out Campaign__c Campaign Lookup
→ out Contact__c Contact Lookup
→ out Designation__c Designation Lookup
→ out Donor_Account__c Account Lookup
→ out Payment_Account__c Payment Account Lookup
→ out Payment_Method__c Payment Method Lookup
← in Recurring_Donation__c on Opportunity Opportunity Lookup
← in Recurring_Donation__c on Recurring Designation Recurring Designation MasterDetail
← in Recurring_Donation__c on Recurring Donation History Recurring Donation History MasterDetail
← in Recurring_Donation__c on Transaction Log Transaction Log Lookup

Fields

Field Label Type Notes
Amount__c Amount Currency The amount charged to the donor's card each billing cycle.
Campaign__c Campaign Lookup → Campaign The campaign this recurring gift supports.
Docs: Also the field CancelGoalReachedRecurringQueueable filters on when a campaign's goal is newly crossed with Recurring_Behaviour_At_Goal__c set to Cancel — every Active schedule pointing at that campaign is a candidate for bulk auto-cancellation, chunked at 200 with Transaction Finalizer retry.
Cancellation_Reason__c Cancellation Reason Text Why this recurring donation was cancelled, if known.
Docs: Also populated by system-initiated cancellations, not just donor/admin ones — CancelGoalReachedRecurringQueueable calls RecurringDonationService.cancelSubscriptions() with a fixed reason string when Campaign.Recurring_Behaviour_At_Goal__c is Cancel and the campaign's goal is newly crossed, so a blank-looking manual cancel and an automatic goal-triggered one are both represented here, distinguishable only by the reason text.
Contact__c Contact Lookup → Contact The donor for this recurring gift (standard Account orgs).
Currency_ISO_Code__c Currency ISO Code Text Three-letter currency code for this recurring gift, e.g. USD.
Designation__c Designation Lookup → Designation__c The fund or program this recurring gift supports, if not split across multiple funds.
Donor_Account__c Donor Account Lookup → Account The donor for this recurring gift (Person Account orgs).
Donor_Covered_Fees__c Donor Covered Fees Checkbox Checked when the donor agreed to cover the processing fee. Each renewal then charges their gift amount plus the current fee, so the amount taken is slightly more than the Amount field. Uncheck it to bill only the gift amount from the next renewal onward.
Docs: Amount__c on a schedule is always the GIFT, never the grossed-up charge — this flag is what tells RecurringRenewalBatch to add a fee on top at charge time. Storing the gross in Amount__c instead would have been simpler but wrong twice over: the designation splits that every installment inherits would allocate the fee to a fund, and the fee would freeze at whatever the rate was on signup day, so a later Settings__c rate change would never reach existing donors. Recomputing per cycle from FeeCoverageService.computeFee also means the donor's coverage does not silently lapse after month 1, which is what happens if the fee is only ever applied to the signup charge.
End_Date__c End Date Date The date after which this recurring gift should stop being charged. Leave blank for a gift that continues until the donor stops it.
Docs: Unlike Installment_Cap__c (enforced after a charge), this one is enforced BEFORE any charge: it is part of RecurringDonationsSelector.dueCondition(), so a schedule past its end date is never even selected, and RecurringRenewalBatch.isStillDue() re-checks it against a freshly read row. Nothing marks such a schedule Completed on the day it expires — it simply stops being due, and processRenewal marks it Completed the next time it would otherwise have renewed.
Failed_At__c Failed At DateTime When this recurring gift stopped retrying and moved to Failed. Used to auto-cancel it after the configured grace period — do not edit directly.
Failed_Payment_Count__c Failed Payment Count Number Number of renewal charges in a row that have failed.
Frequency__c Frequency Picklist How often the donor is charged for this recurring gift.
Installment_Cap__c Installment Cap Number Set this to end the gift automatically after a fixed number of payments — enter 12 for a "$50 a month for a year" pledge. Leave blank for a gift that continues until the donor stops it.
Docs: The cap is enforced AFTER a charge, not before one: RecurringDonationService.processRenewal increments Installments_Paid__c on a successful capture and, when the incremented count reaches this cap, flips Status__c to Completed and clears Next_Payment_Date__c. That is why the due-selector needs no field-to-field comparison (SOQL cannot express one) — a completed schedule simply stops matching the Status__c = 'Active' predicate. A cap lowered below the count already paid therefore ends the schedule at the NEXT successful charge, not retroactively.
Installments_Paid__c Installments Paid Number A running count of payments successfully collected on this recurring gift. The system maintains this — do not edit it.
Docs: Maintained by Apex, not a roll-up summary: Recurring_Donation__c ↔ Opportunity is a lookup, not a master-detail, so the platform cannot roll up the installment count. Gift #1 is counted at signup (RecurringDonationService.createScheduleFromStaging sets it to 1) and each later successful renewal increments it in processRenewal's success branch. A schedule created before this field existed starts at 0 and counts forward from its next renewal, so a cap applied to a long-running legacy schedule grants it a full fresh term — deliberate, since the historical count cannot be reconstructed for schedules whose installments predate the field.
Last_Dunning_Email_Sent_At__c Last Dunning Email Sent At DateTime When a payment-failure email was last sent to the donor.
Last_Error__c Last Error LongTextArea The last error encountered trying to charge this recurring gift.
Last_Payment_Amount__c Last Payment Amount Currency The amount of the most recent successful payment.
Last_Payment_Date__c Last Payment Date Date The date the most recent payment was successfully charged.
Mandate_Accepted__c Mandate Accepted Checkbox Whether the donor has agreed to the recurring-charge authorization.
Docs: Actively enforced, not just a record of consent — RecurringDonationsSelector's active-batch query requires Mandate_Accepted__c = true, so a schedule created without it set is silently excluded from every renewal charge, not just flagged for audit.
Mandate_Timestamp__c Mandate Timestamp DateTime When the donor agreed to the recurring-charge authorization.
Mandate_Version__c Mandate Version Text Which version of the authorization terms the donor accepted.
NPSP_Recurring_Donation_Id__c NPSP Recurring Donation Id Text Internal reference to the mirrored NPSP recurring donation record; not for manual entry.
Docs: Part of the optional NPSP extension, detected at runtime via Schema.describeSObjects() rather than a hard package dependency — the core package works whether or not NPSP is installed. When present, this schedule maps to npe03__Recurring_Donation__c as a mirror, not a replacement; Designation__c/Recurring_Designation__c map to NPSP's GAU allocations separately.
Next_Payment_Date__c Next Payment Date Date The next date this recurring gift is scheduled to be charged.
Docs: Frozen on the missed date once a charge declines, not advanced day to day. A decline sets Next_Retry_Date__c and leaves this field alone; RecurringDonationsSelector's due-schedule query is gated so the normal branch only fires when Next_Retry_Date__c is null, otherwise a stale Next_Payment_Date__c would re-select the schedule every run and defeat the dunning cadence. Only a successful charge advances this field, via advanceScheduleDate.
Next_Retry_Date__c Next Retry Date Date The next date a failed payment will automatically be retried.
Docs: Computed as today plus SettingsService.retryIntervalDays(), the same flat interval for every attempt — see the doc note on Retry_Attempt__c about the stale fixed 1/3/5-day cadence described elsewhere.
Paused_Until__c Paused Until Date The date this recurring gift will automatically resume, if paused.
Payment_Account__c Payment Account Lookup → Payment_Account__c Which of your configured Stripe accounts renewals for this schedule are charged to. Set automatically when the schedule starts — changing the fundraiser's payment account later does not, and must not, move an existing schedule.
Docs: Second rung of the renewal resolution chain: Payment_Method__r.Payment_Account__c, then this field, then Campaign.Payment_Account__c for rows predating both (see RecurringRenewalBatch). Stamped from Donation_Staging__c.Payment_Account__c, which is already in hand at RecurringDonationService.createScheduleFromStaging — the account was decided at intent time, so this is a snapshot of a known fact rather than a fresh resolution. deleteConstraint omitted (SetNull) for the same reason as Payment_Method__c.Payment_Account__c.
Payment_Method__c Payment Method Lookup → Payment_Method__c The saved payment method record associated with this recurring gift, if tracked.
Docs: Kept in sync automatically, not just set once at signup. RecurringDonationService. updatePaymentMethod calls PaymentMethodService.saveWalletCard and registers the returned Payment_Method__c on this schedule via uow.registerRelationship, so a card swap updates both this lookup and Stripe_Payment_Method_Id__c together in the same unit of work.
Re_Auth_Link_Sent_At__c Re-Auth Link Sent At DateTime When a re-authentication email was last sent to the donor.
Re_Auth_Required__c Re-Auth Required Checkbox Whether the donor needs to re-authenticate their card before the next charge can go through.
Docs: Set by RecurringDonationService.processRenewal's requiresAction branch and excluded from the renewal batch's selection query (RecurringDonationsSelector) alongside Mandate_Accepted__c and Status__c — a schedule needing re-auth is simply skipped by the batch, not retried, until a donor completes 3-D Secure and this is cleared.
Retry_Attempt__c Retry Attempt Number How many retry attempts have been made after a failed payment.
Docs: The description above states a fixed 1/3/5-day retry schedule; that is stale. The current implementation in RecurringDonationService.processRenewal uses a flat interval read from SettingsService.retryIntervalDays(), capped by SettingsService.maxFailedPaymentRetries(), not a hardcoded per-attempt day count. Trust the Apex over this description until it is corrected.
Status__c Status Picklist The current state of this recurring gift.
Docs: Pending is the picklist's UI default, but RecurringDonations.onBeforeInsert (setDefaults) assigns Active to any record inserted with a null Status__c, so a schedule created through normal Apex flows practically never lands on Pending — that value only shows up if something explicitly sets it.
Stripe_Customer_Id__c Stripe Customer Id Text Internal Stripe reference for this donor's saved payment method; not for manual entry.
Stripe_Payment_Method_Id__c Stripe Payment Method Id Text Internal Stripe reference for the saved card charged each cycle; not for manual entry.
URL_Parameters__c URL Parameters LongTextArea The full set of tracking parameters from the link the donor clicked, stored as JSON. Reference only — report on the UTM fields instead.
Docs: Set once at signup by RecurringDonationService from Donation_Staging__c.Options_JSON__c -> attribution, and is the template every renewal Opportunity copies its UTM fields from. Deliberately never refreshed — it records how the donor was acquired, not how they last interacted.
UTM_Campaign__c UTM Campaign Text The marketing appeal name from the link, e.g. year-end-2026. This is the marketer's label, not the Salesforce Campaign — filled in automatically.
Docs: Set once at signup by RecurringDonationService from Donation_Staging__c.Options_JSON__c -> attribution, and is the template every renewal Opportunity copies its UTM fields from. Deliberately never refreshed — it records how the donor was acquired, not how they last interacted.
UTM_Content__c UTM Content Text Which link or creative variant was clicked, for A/B tests. Filled in automatically — usually blank.
Docs: Set once at signup by RecurringDonationService from Donation_Staging__c.Options_JSON__c -> attribution, and is the template every renewal Opportunity copies its UTM fields from. Deliberately never refreshed — it records how the donor was acquired, not how they last interacted.
UTM_Medium__c UTM Medium Text The channel that carried the link, e.g. email, cpc or social. Filled in automatically — leave it alone.
Docs: Set once at signup by RecurringDonationService from Donation_Staging__c.Options_JSON__c -> attribution, and is the template every renewal Opportunity copies its UTM fields from. Deliberately never refreshed — it records how the donor was acquired, not how they last interacted.
UTM_Source__c UTM Source Text Where this gift came from, e.g. newsletter or facebook. Filled in automatically from the link the donor clicked — leave it alone.
Docs: Set once at signup by RecurringDonationService from Donation_Staging__c.Options_JSON__c -> attribution, and is the template every renewal Opportunity copies its UTM fields from. Deliberately never refreshed — it records how the donor was acquired, not how they last interacted.
UTM_Term__c UTM Term Text The paid-search keyword that led here, if any. Filled in automatically — usually blank.
Docs: Set once at signup by RecurringDonationService from Donation_Staging__c.Options_JSON__c -> attribution, and is the template every renewal Opportunity copies its UTM fields from. Deliberately never refreshed — it records how the donor was acquired, not how they last interacted.

Recurring Donation History#

Recurring_Donation_History__c · custom · 4 fields · ControlledByParent · Recurring Giving

Master-detail child of Recurring_Donation__c that records an immutable audit trail of every state change and payment event on a recurring schedule (created, paused, resumed, cancelled, updated, and each successful or failed renewal charge). Written by the renewal batch and schedule-management services so admins can see the full payment history without inspecting Stripe directly.

Docs note

The description above states the intended design, but as of this writing RecurringDonations.onAfterInsert (createHistoryRecords) is the ONLY place a row is written automatically, and it only ever writes a single Created event at schedule creation. RecurringDonationService's pause/resume/cancel/reinstate methods and processRenewal's success/failure handling all mutate Recurring_Donation__c directly without appending a History row. Do not rely on this object for a full audit trail of status changes or renewal outcomes — check Status__c, Failed_Payment_Count__c, Last_Error__c, and Last_Payment_Date__c on the parent instead.

Relationships

Direction Field Related object Relationship type
→ out Recurring_Donation__c Recurring Donation MasterDetail

Fields

Field Label Type Notes
Amount__c Amount Currency The amount involved in this event, if applicable.
Event_Date__c Event Date DateTime When this event occurred.
Event_Type__c Event Type Picklist What kind of event this history entry records.
Docs: Set by package automation today: Created (RecurringDonations.onAfterInsert) and the four self-service/term values added 2026-08-06 — Amount_Changed, Designation_Changed, Skipped and Completed, all written by RecurringDonationService. Paused, Resumed, Cancelled, Payment_Succeeded and Payment_Failed remain vocabulary only: those transitions mutate Recurring_Donation__c directly without appending a history row.
Recurring_Donation__c Recurring Donation MasterDetail → Recurring_Donation__c The recurring gift this event happened on.
Docs: Master-Detail, so unlike the Contact__c/Donor_Account__c dual-lookup pattern used elsewhere in the data model, this relationship has no Person-Account-specific branching — it always points at the Recurring_Donation__c regardless of the org's account model.

Platform & Settings#

Donation Finalized#

DonationFinalized__e · event · 7 fields · Platform & Settings

Internal decoupling seam published after a donation successfully finalizes, so optional downstream processes (e.g. NPSP allocation sync) can react asynchronously without the finalize path depending on them. Publishes only after the finalize transaction commits (PublishAfterCommit); a publish failure is logged and never blocks the donation. Subscribed to by DonationFinalizedSubscriber.

Docs note

Kept as a deliberate decoupling seam rather than a package boundary — the trigger dispatches to INpspSyncService.syncAllocations (Opportunity Ids only), a self-guarding no-op when the feature is inactive. New subscribers hang off this event rather than being wired into the donation-finalize path directly.

Relationships

No lookup/master-detail relationships to or from this object.

Fields

Field Label Type Notes
Account_Id__c Account Id Text 18-character Account Id of the donor. Platform events store Ids as text, not lookups.
Amount__c Amount Number Donation amount at the moment it finalized.
Campaign_Id__c Campaign Id Text 18-character Campaign Id, if the donation was tied to a campaign.
Contact_Id__c Contact Id Text 18-character Contact Id of the donor, when the org uses standard Contacts.
Docs: The built-in subscriber (NpspSyncService) does not read this field — it works from Opportunity_Id__c alone and re-queries donor identity itself when needed. It is carried on the event for other, more general-purpose subscribers that may be added later.
Opportunity_Id__c Opportunity Id Text 18-character Id of the Opportunity created for this donation.
Docs: The only field DonationFinalizedSubscriber.trigger itself reads — it builds the finalizedOppIds set passed to NpspSyncService.syncAllocations from this field alone.
Options_JSON__c Options JSON LongTextArea Extra JSON context passed through to subscribers (e.g. attribution data captured from the donation URL). Not interpreted by the base package.
Docs: No built-in subscriber reads this today. dfForm captures an opaque attributionRef off the donation URL and DonationService stashes it here verbatim, so a future attribution subscriber has the context without the finalize path needing to know what it means.
Staging_Id__c Staging Id Text 18-character Id of the staging record this donation was finalized from.

Fundraising Settings#

Settings__c · custom · 97 fields · Platform & Settings

Package configuration settings

Docs note

Tier 1 of the three-tier settings model documented in specs/04-data-model.md. Tier 2 is Payment_Account__c (one record per set of connected processor credentials — it replaced the Payment_Gateway__mdt CMDT tier in 2026-08), Tier 3 is Named Credentials (secrets never live here). As a Hierarchy Custom Setting it resolves Org Defaults, falling back per profile or user only if a future release needs per-role overrides; today every read goes through the Org Default row via SettingsService, which exposes one typed accessor per field or field group rather than callers reading Settings__c.getOrgDefaults() directly, so a field rename or a default-value change only needs one code change.

See the full reference page for this object.

Relationships

No lookup/master-detail relationships to or from this object.

Fields

Field Label Type Notes
Allow_Company_Match__c Allow_Company_Match Checkbox Default corporate-matching prompt setting for new campaigns. Individual campaigns can override this.
Allow_Tribute__c Allow_Tribute Checkbox Turn this off to switch tribute gifts off everywhere at once — no campaign will show the "in honor of / in memory of" section and no honoree e-cards will be sent. Leave it on to let each campaign decide for itself.
Docs: This was a write-once seed for new campaigns until 2026-08-07, when it became a real master switch (gap-audit-2026-08-07 §2.1 — an org had no way to stop honoree e-cards without editing every campaign). Because the field defaults to FALSE, that change would have turned tributes off on upgrade for orgs that never touched it, so PostInstallHandler. adoptTributeMasterSwitch flips it to true when any campaign is visibly using tributes. Enforced in CampaignService (masks the campaign payload) and DonationService (refuses the capture); see SettingsService.tributeEnabled for why both are needed.
Allowed_Embed_Origins__c Allowed Embed Origins TextArea One origin per line or comma-separated, e.g. https://www.acme.org. Include scheme; no trailing slash.
Docs: Parsed by SettingsService.allowedEmbedOrigins() into a normalized (lower-cased, no trailing slash, scheme+host only) set, used as the CORS allowlist by the guest EmbedConfigResource. An empty value means no external origin may load the embed widget, not that all are allowed.
Auto_Receipt_Enabled__c Auto_Receipt_Enabled Checkbox When on, a receipt email is sent automatically as soon as a donation closes. Turn off to send receipts manually.
Brand_Primary_Color__c Email Brand Color (hex) Text Hex color (e.g. #25465f) used for the email letterhead, footer bar, and default buttons. Leave blank to use the built-in slate.
Docs: Hex color used only for transactional email chrome (letterhead, footer, buttons) via SettingsService.emailBrandColor(); a separate concern from Campaign_Design__c.Primary_Color__c, which brands the public donation page. Falls back to the Ember brand coral when blank.
Company_Match_Followup_Days__c Company Match Follow-up Days Number How many days staff have to chase an employer match before the follow-up task is due. Leave blank to use 3 days.
Docs: 3 days rather than same-day because a match request usually needs the donor's employer portal details, which staff rarely have on the day the gift lands; a task due the moment it is created reads as overdue immediately and trains people to ignore the list.
Company_Match_Task_Owner_Id__c Company Match Task Owner Id Text The 18-character Salesforce User Id of the person who chases employer matches. Leave blank and each follow-up task goes to the owner of the campaign the gift came in on, which is the right answer for most organizations.
Docs: A Text field rather than a lookup because Custom Settings cannot hold relationship fields — this is the platform's constraint, not a design preference, and it is why CompanyMatchService validates the Id against an active User before using it instead of trusting the stored value. Storing a User Id in config is not the "no hardcoded Ids" violation the security checklist targets: the value is admin-entered per org, never shipped in source.
Contact_Email__c Contact Email Email The address donors should write to when they have a question before giving.
Docs: Read through SettingsService.contactEmail(), never Settings__c.getInstance() directly. Deliberately separate from Reply_To_Email__c: that one is a transactional sender identity, this one is published on a public page for humans to write to.
Contact_Hours__c Contact Hours Text When someone will actually answer. Setting expectations here reduces unanswered calls.
Docs: Free text rather than structured hours on purpose — orgs describe availability in wildly different ways and no parser consumes this; it is printed verbatim in the contact band. No per-campaign override exists: hours are an org fact.
Data_Retention_Days__c Data Retention (Days) Number How many days of diagnostic logs, Stripe call logs, and completed checkout records to keep before they are automatically and permanently deleted, e.g. 90. Lower this to reduce data storage; raise it to keep history longer.
Debug_Logging__c Debug_Logging Checkbox Turns on extra System.debug() output for troubleshooting. Does not control what is saved to the Log object — see Log Level for that.
Docs: Legacy flag: controls only the AppLogger System.debug() console echo. It does NOT gate Log__c persistence any more; that is Log_Level__c's job. Keep both in mind when a Log__c record is expected but missing: check Log_Level__c, not this field.
Default_Campaign_Type__c Default_Campaign_Type Text Pick the Campaign Type new fundraisers should be created with — this is the field standard Salesforce campaign reports group by. Many organizations add their own "Fundraising" or "Appeal" value first. Leave blank to keep whatever default your org already set.
Docs: Deliberately Text, not a picklist: Campaign.Type is a STANDARD picklist whose values are org-specific, and a packaged picklist could not offer the value a subscriber actually uses. The Settings console renders the org's live Campaign.Type entries with the getPicklistValues wire adapter instead, and SettingsService.defaultCampaignType() re-checks the stored value against the live describe before it is applied, so a value later removed from the picklist degrades to "leave the org default alone" rather than failing the create. Seeded to 'Other' on fresh install by PostInstallHandler (never on upgrade). Applied by FundraiserAdminController.CreateFundraiser only — an existing campaign's Type is never rewritten by an edit.
Default_Currency__c Default_Currency Text Three-letter ISO currency code, e.g. USD, EUR, GBP.
Docs: Single source of truth for BOTH the currency symbol shown to donors and the ISO code Stripe is charged in (SettingsService.currencyCodeForGateway() lower-cases it for the Stripe API). Before this field existed, the two could disagree; an org set to INR could be shown a rupee symbol in one place and charged in USD by the PaymentIntent. Blank reads as USD.
Default_Designation__c Default Designation Text 18-character Id of the fund/designation to credit when a donor does not choose one. Leave blank for no default.
Docs: Stored as text, not a Lookup, because Hierarchy Custom Settings cannot hold relationship fields, so SettingsService.defaultDesignationId() casts it to Id at read time and swallows a blank/malformed value back to null rather than throwing.
Default_Hero_Image_URL__c Default Hero Image URL Url Internal cache of the packaged default hero image's public link. Do not edit directly.
Docs: Idempotency guard as much as a cache — ensureDefaultImagesSeeded() only creates the ContentVersion/ContentDistribution when this is blank, so a package upgrade re-running the post-install script does not stack a duplicate file per upgrade.
Default_Org_Logo_Content_Document_Id__c Default Org Logo Content Document Id Text Internal cache of the default logo's ContentDocument Id. Do not edit directly.
Default_Org_Logo_URL__c Default Org Logo URL Url Internal cache of the default logo's public link. Do not edit directly.
Default_Social_Share_Image_URL__c Default Social Share Image URL Url Internal cache of the packaged default social share image's public link. Do not edit directly.
Docs: Same idempotency role as Default_Hero_Image_URL__c. Note the downstream field it feeds, Campaign_Design__c.Social_Share_Image_URL__c, is not yet rendered into an og:image tag by anything in this package — seeding it gives admins a sensible default in the Designs panel and a value a future social-preview feature can read.
Default_Suggested_Amounts__c Default_Suggested_Amounts Text Comma-separated list of suggested amounts, e.g. "25,50,100,250".
Docs: Read by SettingsService.defaultSuggestedAmounts(); returns null (not a hardcoded list) when unset so the donation form's fallback chain can tell "no org default configured" apart from "use this"; the chain is campaign-level Suggested_Amounts__c, then this field, then a client-side literal fallback.
Default_Thank_You_CTA_Label__c Default Thank-You CTA Label Text Fallback text shown on the thank-you CTA button (e.g. "See Our Impact"), used whenever a campaign leaves its own CTA label blank.
Default_Thank_You_CTA_URL__c Default Thank-You CTA URL Url Fallback destination for the thank-you CTA button, used whenever a campaign leaves its own CTA URL blank.
Dispute_Reconcile_Cursor__c Dispute Reconcile Cursor DateTime Machine-managed. Do not edit — used internally by the reconciliation batch to avoid re-scanning processed disputes.
Docs: Machine-managed high-water mark read and advanced only by RefundReconciliationBatch's dispute-polling pass; a separate cursor from Refund_Reconcile_Cursor__c because disputes and refunds are polled from Stripe as two independent event streams within the same batch job.
Donation_Page_Path__c Donation Page Path Text Only fill this in if you built your Donate page at a URL other than /donate. Enter the path exactly as set in Experience Builder's Page Properties (e.g. /give). Leave blank if you used the default /donate — campaign, fundraiser, and embed links are built using this value.
Donation_Sync_Campaign_Members__c Donation_Sync_Campaign_Members Checkbox When on, a donor who gives to a campaign is added as a Campaign Member on that campaign. Off by default — leave it off if you already add campaign members with your own automation.
Docs: The donation-side counterpart of Event_Sync_Campaign_Members__c, deliberately a SEPARATE switch: an org that wants event attendance in campaign reporting does not necessarily want every donor added too, and the two write paths run at different times (event orders sync inline in EventPurchaseService; donations sync off the DonationFinalized__e subscriber via CampaignMemberSyncService). Read through SettingsService.donationSyncCampaignMembers().
Donor_Portal_Confirmation_Message__c Donor Portal Confirmation Message TextArea Copy shown after a donor submits their email, explaining the link (if sent) and how long it lasts. Leave blank to use the default wording.
Donor_Portal_Confirmation_Title__c Donor Portal Confirmation Title Text Heading shown after a donor submits their email for a portal link. Leave blank to use the default wording.
Donor_Portal_Empty_History_Message__c Donor Portal Empty History Message Text Message shown when a donor has no gift history. Leave blank to use the default wording.
Donor_Portal_Empty_Recurring_Message__c Donor Portal Empty Recurring Message Text Message shown when a donor has no active recurring gifts. Leave blank to use the default wording.
Donor_Portal_Empty_Wallet_Message__c Donor Portal Empty Wallet Message Text Message shown when a donor has no saved cards. Leave blank to use the default wording.
Donor_Portal_Hide_History__c Donor Portal Hide History Checkbox Leave unchecked to show the gift-history panel on the donor portal. Check to hide it.
Docs: One of four Donor_Portal_Hide_ visibility toggles (History, Recurring, Summary, Wallet), all stored inverted for the same backfill-safety reason as Refunds_Disabled__c. These fields, and the Donor_Portal_ copy fields on this object, were migrated from Donor_Portal_Config__mdt on 2026-07-31 because that CMDT type shipped with zero packaged records and required a package upgrade to change; as Settings__c fields they are editable from the setup console like any other org preference.
Donor_Portal_Hide_Recurring__c Donor Portal Hide Recurring Checkbox Leave unchecked to show the recurring-gifts panel on the donor portal. Check to hide it.
Donor_Portal_Hide_Summary__c Donor Portal Hide Summary Checkbox Leave unchecked to show the giving-summary panel on the donor portal. Check to hide it.
Donor_Portal_Hide_Wallet__c Donor Portal Hide Wallet Checkbox Leave unchecked to show the saved-cards panel on the donor portal. Check to hide it.
Donor_Portal_Path__c Donor Portal Path Text Path appended to the Experience Site URL for donor-portal links in emails (default /donor).
Donor_Portal_Request_Subtitle__c Donor Portal Request Subtitle TextArea Copy shown below the heading explaining how the magic-link email works. Leave blank to use the default wording.
Donor_Portal_Request_Title__c Donor Portal Request Title Text Heading shown above the email field when a donor requests portal access. Leave blank to use the default wording.
Donor_Refund_Requests_Enabled__c Donor Refund Requests Enabled Checkbox Check this to let donors ask for a refund from their donor portal. Nothing is refunded automatically — each request lands in the Guest Requests tab for a person to approve and process. Leave unchecked if you would rather donors phone or email you.
Docs: Read by SettingsService.donorRefundRequestsEnabled(). Deliberately NOT inverted, unlike Refunds_Disabled__c: a Checkbox backfills to false on an upgraded org row, and false must mean "off" here so an existing org never starts inviting refund requests it did not ask for. Edited in the Refunds panel of the settings console (setRefunds), not the donor-portal panel, because it is bounded by the same master switch and window as staff-issued refunds.
Dunning_Email_Frequency__c Dunning_Email_Frequency Number Number of days between payment-failure reminder emails, e.g. 3.
Docs: Read by SettingsService.dunningEmailFrequencyDays() (default 3 when unset). Throttles how often the same failed schedule can email its donor, mirroring the reAuthThrottleDays pattern in RecurringDonationService so a donor is never double-emailed within one cool-down window.
EIN__c EIN Text Your organization's federal Employer Identification Number (EIN), shown on tax receipts.
Email_Accent_Color__c Email Accent Color (hex) Text Hex color (e.g. #b4863c) used for the donation-receipt gratitude accent. Alert emails keep their own status colors.
Email_Background_Color__c Email Background Color (hex) Text Hex color (e.g. #F4EDE0) for the outer email canvas behind the message card. Blank = built-in warm parchment.
Email_Heading_Color__c Email Heading Color (hex) Text Hex color (e.g. #1C2B46) for email headings, the gift amount, ledger values, and the footer background. Blank = built-in Midnight.
Email_Signature__c Email Signature TextArea Sign-off shown above the footer on every email (e.g. "With gratitude, The Rivergate team"). Leave blank to use the default.
Embed_Disabled__c Embed Disabled Checkbox Tick this to switch off every donation widget you have embedded on outside websites. Those pages will show a short "not available" notice instead of the donation form, and no new gifts can come through them — your donation pages on your own site are unaffected. Leave it unticked (the normal setting) to keep embedding on. Use it if a widget is misbehaving or you need to stop external giving quickly.
Docs: Read by SettingsService.isEmbedEnabled() using the same inverted-boolean pattern as Refunds_Disabled__c: unchecked (the backfilled default) reads as enabled. Gates the guest EmbedConfigResource CORS-checked donation widget entirely, not just its styling.
Event_Deductible_Mode__c Event_Deductible_Mode Text Controlled values: "Fair Market Value" or "Percentage". Custom Settings do not support the Picklist field type, so this is enforced in Apex rather than a valueSet.
Docs: Stored as free-text rather than a Picklist because Hierarchy Custom Settings cannot hold that field type; EventPurchaseService enforces the two valid values ("Fair Market Value", "Percentage") case-insensitively and falls back to Fair Market Value with a warning on any other stored text, rather than silently treating an unrecognized value as fully deductible.
Event_Donation_Product_Id__c Event Donation Product Id Text Machine-managed. Do not edit — Id of the packaged "Donation" add-on product seeded at install.
Docs: Id (as text) of the generic package Product2 used for the fully-deductible donation add-on line on event ticket orders. Seeded by PostInstallHandler; blank until that runs.
Event_Pricebook_Id__c Event_Pricebook_Id Text 18-character Id of the Pricebook used for event ticket pricing. Leave the seeded default unless you use your own Pricebook.
Docs: Id (as text) of the events Pricebook2 seeded by PostInstallHandler.seedEventPricebook(). Blank until seeded, at which point every Event_Ticket_Type__c PricebookEntry hangs off this Pricebook.
Event_Sync_Campaign_Members__c Event_Sync_Campaign_Members Checkbox When on, each ticket buyer/attendee is added as a Campaign Member for reporting. Off by default.
Docs: Opt-IN, not opt-out: off by default because writing a CampaignMember for every ticket attendee touches Campaign membership automation the subscriber org may already own. When on, finalizing an event order creates a CampaignMember for each attendee that resolved to a Contact.
Experience_Network_Id__c Experience Network Id Text The Experience Cloud site chosen above for your public donation pages. Leave blank to let the package auto-detect a site named "Fundraising" instead.
Experience_Site_URL__c Experience_Site_URL Text Base URL of your Experience Cloud site, e.g. https://yourorg.my.site.com/donate. Usually auto-populated.
Failed_Payment_Retries__c Failed_Payment_Retries Number Number of failed payment attempts allowed before a recurring gift is canceled, e.g. 3.
Docs: Read by SettingsService.maxFailedPaymentRetries() (default 3 when unset): the number of off-session retry attempts a recurring schedule gets before it is marked Failed and starts counting down Grace_Period_Days__c toward cancellation.
Fee_Coverage_Fixed__c Fee Coverage Fixed Amount Number The flat per-transaction part of your payment processor's fee, e.g. 0.30 for Stripe's standard US card rate. Used only to work out the extra amount a donor pays when they tick "cover the processing fee". Leave blank to use 0.30.
Docs: Expressed in Default_Currency__c, not converted per gift — the package is single-currency by design (see Settings__c.Default_Currency__c), so there is no rate to apply. A blank falls back to 0.30 for the same reason Fee_Coverage_Percent__c falls back to 2.9: an unconfigured org should produce a plausible Stripe-shaped estimate, not a silently wrong one. Zero IS a legitimate value here (some processors charge no flat component) and is honoured — only blank triggers the default.
Fee_Coverage_Percent__c Fee Coverage Percent Number The percentage part of your payment processor's fee, e.g. 2.9 for Stripe's standard US card rate. Used only to work out the extra amount a donor pays when they tick "cover the processing fee" — it never changes what your processor actually charges you. Leave blank to use 2.9.
Docs: Deliberately org-level rather than per-campaign: the rate is a property of the payment processor's pricing, not of an appeal, and an admin who has negotiated a different rate has one place to change it. The old Default_Fee_On_Top_Percent__c was removed 2026-08-03 as dead config because nothing consumed it; this field is its replacement and IS consumed, by FeeCoverageService.computeFee. Blank falls back to 2.9 rather than 0 — a zero rate would silently make every "cover the fee" tick add only the fixed component, which reads as a bug rather than as unconfigured.
Finalizer_Heartbeat__c Finalizer Heartbeat DateTime Machine-managed. Do not edit — the finalizer job stamps this every time it completes a pass, and the setup console reads it to tell you whether gifts are still being turned into records.
Docs: This exists because a scheduled-job COUNT cannot detect a job that is scheduled but not running. The setup console's "background jobs" check counts CronTrigger rows, which stay at 24/24 even when every run is aborting; this marker is written from inside the job itself, so it only advances when a pass genuinely completed. The guest donation path reads it before creating a PaymentIntent and, when it is stale, still takes the gift (a donor-facing outage is worse than a delayed record) but writes a PIPELINE_STALE Transaction_Log__c row so the admin's Recent Errors panel and Get Started step both go red. Staleness threshold lives in SettingsService.FINALIZER_STALE_MINUTES; the finalizer itself runs every 5 minutes, so 30 minutes is six missed passes.
Footer_Text__c Public Page Footer Text Text The line at the very bottom of your public pages, usually a copyright notice such as "© 2026 Your Organization". Campaigns can override it if one needs different wording.
Docs: Read through SettingsService.footerText(). Distinct from Receipt_Footer_Text__c, which is the legal block printed on receipts — this one is a single display line at the foot of a web page and carries no tax claim. Moved off Campaign_Design__c on 2026-08-12, where every packaged theme carried an identical copy of the same sentence.
From_Email__c From Email Email The address donor emails come from. It only takes effect if you have created a matching Org-Wide Email Address in Setup that is verified AND has "Allow All Profiles to Use this From Address" ticked. Check Settings - System - Health Check - Donor Email Sender to confirm it is really in use.
Docs: Apex cannot invent a From address. Messaging.SingleEmailMessage sends from the running user unless handed an OrgWideEmailAddress id, which is why this field is a lookup key rather than a value the package can use directly. EmailSenderService.applySender() is the single consumer; note that setOrgWideEmailAddressId() and setSenderDisplayName() are mutually exclusive in the platform API, so From_Name__c is skipped whenever this resolves.
From_Name__c From_Name Text Display name shown as the sender, e.g. "Riverbend Food Bank". If you set a From Email that matches a verified Org-Wide Email Address, that record's own display name is used instead and this field has no effect.
Docs: Skipped by EmailSenderService.applySender() whenever From_Email__c resolves, because setSenderDisplayName() and setOrgWideEmailAddressId() are mutually exclusive on Messaging.SingleEmailMessage.
Grace_Period_Days__c Grace_Period_Days Number Days to wait after the last failed attempt before canceling a recurring gift, e.g. 7.
Docs: Read by SettingsService.gracePeriodDays() (default 7 when unset). Counts days a recurring schedule may sit Failed (Failed_At__c) before RecurringAutoCancelBatch cancels it outright.
Inline_Video_Embeds_Enabled__c Inline Video Embeds Enabled Checkbox Play story videos directly on the public page instead of opening YouTube in a new tab. This only works after you create a CSP Trusted Site for https://www.youtube-nocookie.com yourself — see the Story media section of the Organization setup panel for the exact values. Leave off and videos show a poster card that links out, which needs no setup.
Docs: Read by SettingsService.inlineVideoEmbedsEnabled() and emitted to the guest page as the inlineVideoEnabled key on the CampaignService payload. Turning it on without creating the trusted site produces a blocked iframe, not an error, so the setup panel states the requirement next to the toggle rather than trying to detect it.
Log_Level__c Log Level Text OFF, ERROR, WARN, INFO, or DEBUG. Blank defaults to ERROR.
Docs: Authoritative for AppLogger persistence (OFF/ERROR/WARN/INFO/DEBUG, blank treated as ERROR). Supersedes the legacy Debug_Logging__c checkbox, which now only toggles a System.debug() echo and no longer controls whether a Log__c record is written.
Max_Donation_Amount__c Maximum Donation Amount Number Largest gift amount accepted, e.g. 25000. Leave blank or 0 for no maximum.
Docs: Read by SettingsService.maxDonationAmount(); null or a non-positive value both mean no maximum is enforced, not zero.
Min_Donation_Amount__c Minimum Donation Amount Number Smallest gift amount accepted, e.g. 5. Leave blank or 0 for no minimum.
Docs: Read by SettingsService.minDonationAmount(); null or a non-positive value both mean no minimum is enforced, not zero.
NPSP_Enabled__c NPSP_Enabled Checkbox Read-only. Automatically detected — do not set manually.
Docs: Same detected-AND-enabled precedence as Person_Account_Enabled__c: SettingsService.npspEnabled() requires both this flag and the presence of NPSP's core recurring-donation object. Gates NPSP_GAU_Sync_Enabled__c and NPSP_Sync_Enabled__c, which cannot be true unless this is.
NPSP_GAU_Sync_Enabled__c NPSP_GAU_Sync_Enabled Checkbox When on (and NPSP is installed), designation splits are also written as NPSP GAU Allocations.
Docs: Sub-toggle under NPSP_Enabled__c, read by NpspSyncService: when on, finalizing a donation creates an npsp__Allocation__c per Donation_Designation__c line so fund-level giving mirrors into NPSP's GAU allocation model.
NPSP_Sync_Enabled__c NPSP_Sync_Enabled Checkbox When on (and NPSP is installed), recurring gifts are also mirrored to NPSP Recurring Donations.
Docs: Sub-toggle under NPSP_Enabled__c, read by NpspSyncService: when on, each Recurring_Donation__c schedule gets a parallel npe03__Recurring_Donation__c maintained alongside it, so NPSP-native reports see the same recurring commitments this package tracks natively.
Org_Display_Name__c Org_Display_Name Text Your organization's public-facing name, e.g. "Riverbend Food Bank". Shown on campaign pages, the checkout header, the donor portal and every receipt. If you leave it blank, receipts print your Salesforce org's own name instead of an empty letterhead.
Org_Logo_Content_Document_Id__c Org Logo Content Document Id Text Set automatically when you upload a logo in Setup > Organization > Branding. Do not edit directly.
Org_Logo_Is_Custom__c Org Logo Is Custom Checkbox Set automatically when you upload or remove a logo in Setup > Organization > Branding. Do not edit directly.
Org_Logo_URL__c Org Logo URL Url Set automatically when you upload or remove a logo in the Organization panel — do not edit directly.
Org_Mailing_Address__c Organization Mailing Address TextArea Physical mailing address shown in the email footer. Required for CAN-SPAM (US) and CASL (Canada) compliance.
Person_Account_Enabled__c Person_Account_Enabled Checkbox Read-only. Automatically detected — do not set manually.
Docs: A detection MIRROR for the setup console, not a switch and not a runtime gate. Written only by SettingsService.detectAndUpdateOrgFeatures() and SetupController.detectOrgFeatures(), and shown read-only in the setOrganization panel so an admin can see what the app detected. Nothing in the donation, receipt or portal paths reads it: every runtime Person Account branch goes through DonorResolutionService.isPersonAccountOrg(), a pure Account.IsPersonAccount describe that never touches this record. SettingsService.personAccountEnabled() would combine the two, but has no production caller — see the note above that method for why pointing isPersonAccountOrg() at it would be unsafe while this field defaults to false and is not seeded at install. Never branch on this field directly.
Portal_Token_TTL_Hours__c Portal Link Expiry (Hours) Number Hours a donor-portal magic link stays valid before it expires, e.g. 3. Leave blank for the 3-hour default. Raise it only if donors report links dying before they get to them - the link is a bearer credential sitting in an inbox, so a longer window is a longer exposure.
Docs: Read by SettingsService.portalTokenTtlHours() (default 3h when unset/non-positive, hard-capped at 168h/7 days so a mis-set value cannot make a magic link effectively permanent). Drives how far ahead of issuance DonorPortalService sets Contact/Account.Portal_Token_Expires_At__c. The default was 24h through 1.0.0-16; it was cut to 3h because the token is neither single-use nor device-bound, so the expiry window is the whole of its security.
Privacy_URL__c Privacy URL Url Web address of your privacy policy. Donors see it as a link at the bottom of every public page. Leave blank if you do not publish one — the link is simply left out.
Docs: Read through SettingsService.privacyUrl(). Same 2026-08-12 move off Campaign_Design__c as Terms_URL__c, for the same reason. Campaign.Privacy_URL_Override__c wins when set.
Receipt_Email_Template__c Receipt_Email_Template Text API name of the email template used for donation receipts.
Docs: Admin-configured DeveloperName of an org-owned email template; when blank, ReceiptService falls back to the packaged "DonationReceipt" template rather than failing the send.
Receipt_Footer_Text__c Receipt Footer Text TextArea Your organisation's standing details — legal name, tax-exempt status, EIN, address — shown at the bottom of every receipt. Do not put "no goods or services were provided" here: this text also prints on event-ticket receipts, where that statement is false. Use the Receipt Legal Text fields for wording that depends on the type of gift.
Docs: Case-BLIND, unlike the three Receipt_Legal_Text_ fields. This one string is appended to every receipt the package prints — plain gift, event ticket, partial refund alike — so it cannot carry any claim that is only true for some of them. The package's own scratch-org seed script got this wrong until 2026-08-08: it seeded a footer ending "No goods or services were provided in exchange for this gift", which then printed on a gala receipt directly beneath a line itemising $400 of goods received. Anything conditional belongs in Receipt_Legal_Text_With_Goods__c / Receipt_Legal_Text_Partial_Refund__c / Tax_Receipt_Legal_Text__c, which the package selects between per gift.*
Receipt_Intro_Message__c Receipt Intro Message TextArea Intro paragraph shown on the donation receipt below the amount. Leave blank to use the default wording.
Receipt_Legal_Text_Partial_Refund__c Receipt Legal Text (Partly Refunded) TextArea Wording for the rare receipt covering an event order that was partly refunded. No tax-deductible amount is shown on these, so this text should tell the donor to contact you for it. Leave blank to use the default wording.
Docs: The third case of the legal-statement split (2026-08-08) — see the doc comment on Receipt_Legal_Text_With_Goods__c for why one override field became three. This one covers the single case the package refuses to compute: a partially refunded order that also carried goods. ReceiptStatusHelper.deductibleAmount returns null there on purpose (apportioning a refund across the deductible and non-deductible halves of a gala ticket is a judgement the package has no basis to make), so the statement has to send the donor to the org rather than state a figure. An org that overrides this must keep that property — wording that asserts a deductible amount would contradict the receipt above it, which deliberately shows none.
Receipt_Legal_Text_With_Goods__c Receipt Legal Text (Goods Received) TextArea Wording printed on receipts for event tickets and any other gift where the donor got something back. Must NOT say "no goods or services were provided" — that statement is false for these gifts and is regulated in the US. Leave blank to use the default wording.
Docs: The quid-pro-quo half of the legal-statement split (2026-08-08). Before the split there was one override field, Tax_Receipt_Legal_Text__c, and it won outright on every receipt — so an org that set it to the ordinary "no goods or services were provided" wording had that sentence printed verbatim on a gala receipt itemizing hundreds of dollars of goods received, which is a false IRC section 6115 disclosure. The override is now per-case: the no-goods field keeps its API name and its existing value (no data migration), and the two goods-aware cases get their own fields, blank on upgrade, falling back to the package wording until an admin sets them. Resolved by ReceiptStatusHelper.legalStatement for single receipts and GivingStatementService for annual statements, so the two surfaces can never make different claims about one gift.
Receipt_Number_Prefix__c Receipt Number Prefix Text Short code that starts every receipt number, for example RCPT gives RCPT-2026-000001. Leave blank to use RCPT. Changing this only affects receipts issued from now on.
Docs: Kept to 10 characters so the assembled number stays inside Opportunity.Receipt_Number__c (40). ReceiptNumberService upper-cases the value and strips everything outside A-Z, 0-9 and underscore before use — a prefix containing a space, comma or slash would otherwise produce receipt numbers that break the CSV exports and URL-addressed lookups they exist to identify. Hyphens are stripped too, because the hyphen is the separator between prefix, year and sequence: allowing one inside the prefix would make a receipt number ambiguous to parse. A prefix that sanitises away to nothing falls back to RCPT rather than emitting a number that starts with a stray hyphen.
Record_Type_Mapping_Chunk_1__c Record Type Mapping Chunk 1 Text Machine-managed. Do not edit — this stores part of the record-type mapping configuration and is only ever changed by the Setup console's record-type mapping screen.
Docs: Part of a 4-field chunked storage scheme for RecordTypeMappingService's compact-encoded mapping table (field separator U+00A7 "§", slot separator U+00B6 "¶") — replaces the former Record_Type_Mapping__c custom object, deleted 2026-08-03 because Custom Settings fields cap at 255 chars and don't support Long Text Area, so a dedicated Long-Text-capable object was the only other option; chunking across Settings__c fields was chosen instead to avoid a second custom object.
Record_Type_Mapping_Chunk_2__c Record Type Mapping Chunk 2 Text Machine-managed. Do not edit — this stores part of the record-type mapping configuration and is only ever changed by the Setup console's record-type mapping screen.
Record_Type_Mapping_Chunk_3__c Record Type Mapping Chunk 3 Text Machine-managed. Do not edit — this stores part of the record-type mapping configuration and is only ever changed by the Setup console's record-type mapping screen.
Record_Type_Mapping_Chunk_4__c Record Type Mapping Chunk 4 Text Machine-managed. Do not edit — this stores part of the record-type mapping configuration and is only ever changed by the Setup console's record-type mapping screen.
Recurring_Thankyou_Message__c Recurring Thank-You Message TextArea Body paragraph shown on the recurring-gift confirmation email. Leave blank to use the default wording.
Refund_Reconcile_Cursor__c Refund Reconcile Cursor DateTime Machine-managed. Do not edit — used internally by the reconciliation batch to avoid re-scanning processed refunds.
Docs: Machine-managed high-water mark for the refund-polling pass of RefundReconciliationBatch, independent of Dispute_Reconcile_Cursor__c. Neither cursor advances, and the reconciler does not poll at all, while Refunds_Disabled__c is checked.
Refund_Window_Days__c Refund Window (Days) Number Number of days after a donation's Close Date during which it remains eligible for refund, e.g. 180.
Refunds_Disabled__c Refunds Disabled Checkbox Inverted field: leave UNCHECKED to keep refunds enabled (the default). Check this box ONLY to turn refunds OFF org-wide.
Docs: Read by SettingsService.refundsEnabled(), which inverts the stored value so a Checkbox that backfills to false on an upgraded org row is read as ENABLED with no migration step needed. Follow the same inverted pattern for any future org-wide kill switch rather than a directly-read Is_Enabled field.
Reply_To_Email__c Reply-To Email Email Address donors reach when they reply to an email. Leave blank to use the From Email.
Retry_Interval_Days__c Retry_Interval_Days Number Days between automatic retry attempts for a failed recurring payment, e.g. 3.
Docs: Read by SettingsService.retryIntervalDays() (default 3 when unset). Cadence is flat: every retry waits the same number of days, there is no backoff curve.
Social_Facebook_URL__c Social — Facebook URL Url Full web address of your organization's Facebook page. Leave blank and that icon is not shown at all.
Docs: Read through SettingsService.socialFacebookUrl(). See Social_Twitter_URL__c for why the three networks are separate fields.
Social_Instagram_URL__c Social — Instagram URL Url Full web address of your organization's Instagram profile. Leave blank and that icon is not shown at all.
Docs: Read through SettingsService.socialInstagramUrl(). See Social_Twitter_URL__c for why the three networks are separate fields.
Social_Share_Image_URL__c Social Share Image URL Url The picture that appears when someone shares one of your public pages on social media. Best at 1200x630 pixels. Leave blank to use the packaged default image.
Docs: Read through SettingsService.socialShareImageUrl(), which falls back to Default_Social_Share_Image_URL__c when this is blank. The two are deliberately separate: this one is the admin's choice, the Default_ one caches the ContentDistribution link to the packaged artwork that SettingsService.ensureDefaultImagesSeeded() creates, and an upgrade re-seeds the cache without ever touching an admin's own picture.
Social_Twitter_URL__c Social — X (Twitter) URL Url Full web address of your organization's X (Twitter) profile. Leave blank and that icon is not shown at all.
Docs: Read through SettingsService.socialTwitterUrl(). The three social fields are separate rather than one multi-value field so the footer can render each network's own icon and omit the ones an org does not use, without parsing a delimited string on the guest path.
Soft_Credit_Automation_Enabled__c Soft_Credit_Automation_Enabled Checkbox When on, gifts given in honor of someone are automatically credited to that person, and a donor's household is credited alongside them. Turn it off if you assign soft credit yourself or with your own automation.
Docs: Starts ON for a fresh install, unlike Donation_Sync_Campaign_Members__c which is off for everyone. The difference is what each one writes: campaign membership creates rows in data an org may already own and report on, whereas soft credit only populates package fields that are blank until something fills them, so there is nothing for it to corrupt. The <defaultValue> below governs NEW Settings rows only — Salesforce does not backfill a field default onto records that already exist, so an org upgrading into this release keeps the false its existing Settings row was created with and has to opt in. That asymmetry is intentional: the package will not switch on a new automatic write in an org that is already configured. PostInstallHandler.seedDefaultSettings() sets it explicitly on the fresh-install path so the intent does not rest on the platform default alone. Read through SettingsService.softCreditAutomationEnabled().
Support_Phone__c Support Phone Phone Phone number donors can call for help. Shown in payment-failure and final-notice emails, and on public campaign pages that do not set their own contact number.
Tax_Receipt_Legal_Text__c Receipt Legal Text (No Goods Received) TextArea Legal disclaimer for ordinary donations where the donor received nothing back. Defaults to US 501(c)(3) wording; override for your jurisdiction and entity type. Event tickets and other gifts with goods received use their own wording.
Docs: Narrowed from "the receipt's legal statement" to "the plain-gift legal statement" on 2026-08-08. The API name and any stored value are unchanged so no org has to re-enter wording it had reviewed by counsel — but it no longer wins on receipts where the donor received goods, which is what made it a correctness bug rather than a config choice. See the doc comment on Receipt_Legal_Text_With_Goods__c for the full rationale and the other two cases.
Terms_URL__c Terms URL Url Web address of your terms and conditions page. Donors see it as a link at the bottom of every public page. Leave blank if you do not publish one — the link is simply left out.
Docs: Read through SettingsService.termsUrl(), never Settings__c.getInstance() directly. Lived on Campaign_Design__c until 2026-08-12, where switching a campaign from one theme to another silently changed which terms document donors were pointed at — a policy is a fact about the organisation, not about a colour scheme. Campaign.Terms_URL_Override__c wins when set; CampaignService.resolveIdentity owns that precedence, not this field.

Guest Request#

Guest_Request__c · custom · 13 fields · ReadWrite · Platform & Settings

A queued request submitted by an unauthenticated public visitor and applied later by privileged async code. The Experience Cloud guest user can insert one but can never update or delete it, so any public change to real data goes through this queue rather than writing to the target object directly.

Docs note

One object for EVERY public write, not one object per feature. The Guest License can never hold object-level Edit or Delete on anything (a platform limit, not a configuration choice), so every guest action that changes existing data has to be expressed as an insert-only work item that privileged code applies afterwards. Making that shape generic means a new public action costs a picklist value and a handler, not a new object, a new permission-set grant, a new sweeper and a new retention rule.

The trade this makes deliberately: Payload_JSON__c gives up field-level typing and validation in exchange for that reuse, so each handler owns its own payload validation. Acceptable because these rows are transient work items nobody reports on — not records of record. Donation_Staging__c stays separate and typed for exactly the opposite reason: it is payment-critical and several converging paths read named fields off it.

SECURITY — the authorization decision is made SYNCHRONOUSLY, before the insert. The controller validates the caller's token, then stamps Subject_Id__c from the record it just verified. The payload never gets to name what it acts on, so a handler running minutes later under privileged access can trust Subject_Id__c without re-deriving it from guest-supplied data. A handler that read a target id out of Payload_JSON__c would be an IDOR with an async delay bolted on.

Assigned rather than left on SYSTEM: the system compact layout shows only the record name, and GR-0042 on its own tells a reviewer nothing. See the layout for what it carries and, more importantly, what it deliberately withholds from hover cards.

Relationships

Direction Field Related object Relationship type
→ out Campaign__c Campaign Lookup
→ out Opportunity__c Opportunity Lookup

Fields

Field Label Type Notes
Admin_Note__c Admin Note LongTextArea Record why you approved or declined this request, and anything you did outside the app. This is the only record of the decision once the request is closed.
Docs: Internal, not donor-facing: nothing written here is emailed out or rendered on the public site. The donor is told the outcome, not the reasoning, so staff can be candid here. Only requests that route to Awaiting Review ever use this — a self-applying request closes without a human touching it.
Attempt_Count__c Attempt Count Number How many times this request has been attempted. Set automatically.
Docs: Incremented before the handler runs, not after it succeeds — a handler that dies from an uncatchable limit exception would otherwise never record the attempt and would be retried by every sweep for the life of the org. Once the count reaches the retry ceiling the sweep leaves the row at Failed and stops picking it up.
Campaign__c Campaign Lookup → Campaign The campaign this request belongs to.
Email__c Email Email The email address to reply to about this request.
Docs: Taken from the RESOLVED donor, not from anything the browser sent. A visitor who could supply this field could have the package email a stranger on the org's behalf.
Error_Message__c Error Message LongTextArea Why this request could not be processed. Written automatically; clear it only if you are re-queuing the request by hand.
Docs: Read back to the DONOR, not only to the admin — a failed naming request shows the seats as still unnamed with a short retry notice on the donor dashboard, because silently keeping the request Pending forever is how a visitor ends up believing their guests are named when they are not. Handlers must therefore write a message safe to show a member of the public: no record ids, no stack traces, no internal class names. The full detail goes to AppLogger.
Idempotency_Key__c Idempotency Key Text Internal key preventing duplicate requests; not for manual entry.
Docs: Derived server-side from the request type and the subject, NOT generated by the browser — a client-generated key would let a visitor defeat the deduplication simply by sending a new one each time, which is exactly what a double-submit does. Attendee naming keys on the order, so a second submission for the same order replaces rather than duplicates; a refund request keys on the donation, so a donor cannot queue five requests against one gift by pressing the button five times.
Opportunity__c Opportunity Lookup → Opportunity The gift this request is about. Filled in automatically when the request is raised; leave it alone, as changing it does not change which record the request is actually applied to.
Docs: A mirror of Subject Id, not a second source of truth. Subject_Id__c stays the field every handler reads and the only one authorization is decided on; this lookup exists so that a request whose subject happens to be an Opportunity is reachable from the gift's own page, by a related list and by a report, without anyone joining on an 18-character text field. It is stamped in exactly one place — GuestRequestService.submit(), from the same Submission.subjectId that fills Subject_Id__c, and only when that id is an Opportunity. Never set it from a payload or a second caller: the moment the two fields can disagree, the safe one (Subject_Id__c) and the visible one (this) tell different stories about the same request. Blank is normal. Not every request type has an Opportunity subject, and rows created before this field existed carry only Subject_Id__c.
Payload_JSON__c Payload JSON LongTextArea The details submitted with this request. Read-only in practice; the request is processed from this value.
Docs: Untrusted input. It arrived from an unauthenticated browser, so a handler must treat every value in it as hostile: bound the sizes it accepts, refuse anything it does not recognise, and never take a record id from it. The id a request acts on lives in Subject_Id__c, which Apex wrote after validating the caller's token — see the object description for why that separation is the whole security model here. Attendee Names carries {"seats":[{"attendeeId":"a09...","firstName":"...","lastName":"..."}]} and the attendeeIds ARE re-checked against Subject_Id__c's order before anything is written, so a tampered id names nobody. Refund Request carries {"reason":"..."} and no ids at all.
Processed_At__c Processed At DateTime When this request was finished. Blank means it is still open.
Docs: Deliberately distinct from LastModifiedDate: an admin adding a note to a closed request moves LastModifiedDate but not this, so "how long did requests take to action?" stays answerable from report data alone. Retention purging keys on CreatedDate, not on this field, so a row that somehow never reaches a final state is still cleaned up.
Request_Type__c \* Request Type Picklist The kind of request this is. Set automatically when the request is submitted.
Docs: Adding a value here is half of adding a request type; the other half is a case in GuestRequestRouter.handlerFor(). A value with no case is refused at routing time rather than silently left Pending forever, so the two cannot drift apart unnoticed. Attendee Names applies itself. Refund Request does not: its handler validates eligibility and then parks the row at Awaiting Review for a human, because issuing money back is a decision this package will not make on a stranger's say-so.
Status__c Status Picklist The current stage of this request. Move an Awaiting Review request to Completed or Declined once you have acted on it.
Docs: Two lifecycles share one picklist because the queue mechanics are identical and only the terminal states differ: self-applying Pending -> Processing -> Completed | Failed human-decided Pending -> Processing -> Awaiting Review -> Completed | Declined Processing is claimed before the handler runs so the immediate Queueable and the sweep batch cannot both work the same row. Completed, Declined and Failed are terminal — RetentionPurgeBatch deletes rows in those states once they are older than the org's retention window, and nothing re-opens them.
Subject_Id__c Subject Id Text The donation or order this request is about. Set automatically; changing it would point the request at a different donor's record.
Docs: This field IS the authorization decision, recorded. The controller resolves the caller's magic-link token to a donor, finds the order/gift among that donor's OWN records, and stamps the id it found here. The visitor never gets to name a target. Stored as Text rather than a lookup deliberately: one generic queue serves request types whose subjects are different objects, and a per-type lookup column would grow a field with every feature and leave the others blank. The cost is no related list on the target record — admin discovery is the Guest Requests tab and the Campaign related list instead.
Summary__c Summary Text A short description of this request, generated when it was submitted.
Docs: Exists because the alternative is a work queue whose rows all read "GR-0042 Refund Request Awaiting Review" and tell an admin nothing about which one to open first. Composed by the controller from records it has already read, never from the untrusted payload.

Log#

Log__c · custom · 6 fields · Private · Platform & Settings

Structured, engineering-facing diagnostic log written by AppLogger — captures unhandled exceptions, warnings, and debug traces to help troubleshoot the package. Distinct from Transaction_Log__c, which is the append-only Stripe API-call audit ledger; Log__c exists for engineering diagnostics, not payment audit trail. Sharing model is Private, so records are visible only to admin/read-only roles by design.

Docs note

AppLogger is a deliberate, documented exception to the package's Unit-of-Work-only DML rule — it is static without sharing and inserts Log__c rows directly, guarded by a Limits.getDmlStatements() check so a logging call can never itself blow the DML-statement governor limit inside the transaction it is trying to describe. Persistence is gated by SettingsService.logLevel(), which reads Settings__c.Log_Level__c (OFF/ERROR/WARN/INFO/DEBUG); Debug_Logging__c on Settings__c only toggles the additional System.debug() echo and does not affect whether a row is written here.

Relationships

No lookup/master-detail relationships to or from this object.

Fields

Field Label Type Notes
Context__c Context Text Where this log entry came from, e.g. "StagingReconciliationBatch".
Correlation_Id__c Correlation Id Text Shared Id linking all log entries for one transaction. Used to trace a single donation's activity end to end.
Docs: External ID but not unique/case-sensitive-enforced — many rows legitimately share one correlation id (every log line for the same donation), so treat it as a grouping key for a report/list-view filter, not as a record identifier.
Level__c Level Picklist Severity of this entry. Whether it gets logged at all depends on the org's Log Level setting.
Docs: This picklist has no OFF value — OFF only exists on Settings__c.Log_Level__c as the org-wide kill switch AppLogger checks before ever building a Log__c row. The four values here are ordered DEBUG < INFO < WARN < ERROR for the org setting's threshold comparison.
Message__c Message LongTextArea The log message text. Do not include donor personal data or full payment payloads.
Related_Record_Id__c Related Record Id Text Optional 18-character Id of a related record, e.g. an Opportunity or Recurring Donation.
Docs: Plain Text, not a polymorphic lookup — deliberate, since the related record can be any sobject type and Salesforce lookups cannot span arbitrary types outside a small allow-list. There is no referential integrity: the related record can be deleted without this field being cleared, and no FLS/sharing check is applied when an admin clicks through to it manually.
Stack_Trace__c Stack Trace LongTextArea Exception stack trace, if one was available when this entry was logged.

Theme Token#

Theme_Token__mdt · mdt · 6 fields · Platform & Settings

Catalog of overridable public page-design CSS tokens. Package-controlled; records are protected and change only via package upgrade.

Docs note

Single source of truth for which pf-prefixed CSS custom properties a Campaign_Design__c or per-campaign page-design record may override. There is no hand-synced allowlist elsewhere: CampaignService.normalizeThemeTokensJson() loads this catalog via ThemeTokensSelector and strips any JSON key the donor-facing guest shell would otherwise render, and the internal setDesigns editor reads the same catalog through SetupController.getThemeTokenCatalog() so the admin UI and the runtime validator can never drift. Records are packaged and protected, so the catalog only grows or changes across a package upgrade, never via subscriber-org admin action. Adding a new overridable token means adding a record here, not code elsewhere.

Relationships

No lookup/master-detail relationships to or from this object.

Fields

Field Label Type Notes
CSS_Var__c \* CSS Var Text CSS custom-property name this token controls, e.g. "--pf-brand-primary". Must start with --pf-.
Docs: This is the exact JSON key CampaignService.normalizeThemeTokensJson() matches a page design's Theme_Tokens_JSON__c override map against; a key not present in this catalog is stripped before the guest shell ever receives it, so there is no path to inject an arbitrary CSS custom property through a page-design record.
Data_Type__c Data Type Picklist Determines which input control the token editor shows for this token (color swatch, size field, etc.).
Docs: Also editor-only: it selects the input widget (color swatch vs. size field vs. raw text) shown in setDesigns, and is not consulted by CampaignService.normalizeThemeTokensJson() when validating an incoming override; that check is presence-in-catalog only, not type-checked.
Default_Value__c Default Value LongTextArea Default CSS value for this token, shown in the editor and used when no page design overrides it.
Description__c Description Text Short explanation shown next to this token in the page-design editor's override list.
Group__c Group Picklist Category this token is grouped under in the page-design editor (brand, layout, hero, etc.).
Docs: The five values story/sections/tabs/payment/footer were added 2026-08-12 with the two-column public page; adding a value here means adding a matching entry to ThemeTokenCatalogService.GROUP_NAMES in the same change, or the editor renders the group heading as blank. Purely an editor-organization concern; ThemeTokenCatalogService.getCatalog() returns the full catalog to setDesigns regardless of group, and the group only decides which section of the "Add override" dropdown a token is listed under. It has no bearing on validation.
Sort_Order__c Sort Order Number Controls the order this token appears in within its group. Lower numbers show first.