Skip to content

Getting Started

After you install#

The install seeds everything it is allowed to seed. Seven things are left, and five of them can only be done by a human in Salesforce Setup. This page is the written version of the Get Started panel inside the app — every screenshot below was taken while completing the setup by hand in a brand-new org, in the order you will hit them.

You were probably sent here automatically

Salesforce redirects you to this page as soon as the package finishes installing or upgrading. Nothing is broken — this is the handover.

Watch it first#

What it is. The complete install, filmed. Twenty-five minutes, from the Get Started checklist to a real card payment landing as a Closed Won gift with a receipt number — recorded while a fresh subscriber org was configured step by step through the real Salesforce UI, not staged and not re-enacted. Every screen runs at the speed the org actually responded at, and the banner underneath names the action and where in Salesforce you are standing. Captions are in the player’s subtitles menu; the chapter list jumps straight to a step. Each part is also published on its own, on the page that covers it: the Get Started checklist, the donation site, the payment account, the first campaign and gift, and the rest of the console.

The seven steps#

The Get Started panel (App Launcher → Pledgivo → Settings) is the same checklist, checked against your org live. A step there is never a box you tick — it reports what your org actually looks like right now, so if someone later deactivates the last payment account or aborts the scheduled jobs, the step reopens on its own.

# Get Started step Where the work happens On this page
1 Give your team access Salesforce Setup 1. Give your team access
2 Confirm record type mapping Already done by the install 2. Confirm record type mapping
3 Activate your donation site Setup → Experience Builder → Workspaces 3, 4 and 10
4 Set up your payment account Salesforce Setup, then the console 5 and 7
5 Let your donation site use the payment credential Salesforce Setup 6. Grant the credential
6 Start the background jobs The console, once — the install alone is not enough 8. Background jobs
7 Set your organization name and logo Settings console 9. Organization name and logo

Two more items are listed after the seven but never count against them, because neither is needed to take a donation:

  • Add the two grants the permission sets cannot give reports how many of your fundraising staff hold Create Public Links and Manage Recurring Gifts, read live from your org. See Two things the permission sets cannot give.
  • Trust the content hosts you actually use lists the optional video and stock-photo hosts. See Third-party content.

Two of the seven steps span more than one section here, because the checklist counts outcomes and this page follows work order. Step 3 is created early and activated last, on purpose: you publish a site while you are building it and activate it once everything behind it works. Step 4 splits because its credential pair is built in Setup before the console can grade any of it — that dependency is spelled out in section 5.

The screenshots step themselves

Every run of screens below is one panel showing a single screenshot at a time, with its explanation underneath. Click the screenshot — or the arrows — to move to the next step, or use the numbered row to jump anywhere. Leave it alone and it advances by itself while it is on screen; hovering pauses it, and touching any control hands it back to you for good.

Get Started panel reading 6 of 7 complete with one step left
What it shows. The console at the end of a real setup: 6 / 7 Complete — "One step left before this org is ready to take donations. It is walked through on another panel." Steps 1 and 2 are DONE with their evidence quoted from the org — "2 user(s) hold Fundraising Admin or Fundraising User", and a mapping line reading N of N — and step 3, Activate your donation site, is the one still open. Nothing here was ticked by hand.
Get Started steps 3, 4 and 5 with their live evidence lines
What it shows. The middle of the checklist. Step 3 is Partly done and says exactly why — Site "Fundraising" exists but is not activated yet (status: UnderConstruction). Step 4 is done, "1 active payment account(s) configured". Step 5 is done and carries a SETUP ONLY flag with a Do this in Setup block: the app can verify the grant but can never make it for you.
Get Started steps 6 and 7 plus the optional third-party content step
What it shows. The tail of the list. Start the background jobs — "24 of 24 background jobs are scheduled". Shown here after the restart; on a fresh install this row is amber until the jobs are re-owned. Set your organization name and logo — 'Public pages show "Acme Nonprofit"'. Below the seven sits the optional Trust the content hosts you actually use, marked OPTIONAL and SETUP ONLY. Optional steps never move the counter. (This screenshot predates the second optional step, Add the two grants the permission sets cannot give, which now sits just above it.)
7 steps 5 need Salesforce Setup ~45 minutes

1. Give your team access#

Nobody can see a campaign, a donation, or the Settings console without one of the packaged permission sets. The install cannot assign them for you — Salesforce gives a post-install script no way to know who on your team does what.

Permission set Give it to
Fundraising_Admin Whoever runs fundraising. Full CRUD on every object, plus the Settings console.
Fundraising_User The rest of the fundraising team. Day-to-day record access, no setup.
Fundraising_ReadOnly Anyone who needs to see gifts but never change them — finance, leadership.
Fundraising_GuestDonor Not a person. Assign to your Experience Cloud site's guest user only. Least-privilege grants for the public donation flow. See section 4.

The route that always works is from the user, not from the permission set:

  1. Setup → Users → Users → click the person's name.
  2. Scroll to the Permission Set Assignments related list.
  3. Edit Assignments.
  4. Move the set from Available Permission Sets to Enabled Permission Sets → Save.
Setup Permission Sets list showing the packaged Fundraising sets
What it shows. Setup → Permission Sets. All four packaged sets arrive with the install and none of them is assigned to anyone yet. They carry the package namespace, which is how you tell them from a lookalike of your own.
A user detail page with the Permission Set Assignments related list showing no assignments
What it shows. A user's detail page in Setup, with the Permission Set Assignments related list reading 0 items and its Edit Assignments button. This same list is the fastest answer to "why can't this person see the console?" — and it is the state the Get Started panel reads when it counts how many users hold Admin or User.

The Settings console is Fundraising_Admin only. A Fundraising_User cannot open it, and cannot change what it configures by another route either: payment accounts, page designs, and every app setting are read-only for that set. They still see those records — picking a design or a payment account for a campaign keeps working — they just cannot edit them from a record page, the API, or Data Loader. If someone on your team needs to configure payments, designs, or org settings, give them Fundraising_Admin; there is no middle tier.

Saving anything in the Settings console also needs the Manage Fundraising Settings custom permission, which Fundraising_Admin carries. It only matters if you build an admin permission set of your own instead of assigning the packaged one: add Custom Permissions → Manage Fundraising Settings to it, or every save in the console is refused with "You are not authorized to change fundraising settings."

Two things the permission sets cannot give#

The packaged sets cover the package's own objects and screens. Two jobs also need a permission the sets do not carry, so check them for the people who will do those jobs. The Get Started panel's optional step Add the two grants the permission sets cannot give counts, live, how many of your fundraising staff already hold each one:

Job What they also need Where to add it
Publish a file on a campaign's Documents panel The Salesforce system permission Create Public Links. It is a platform permission, and a package permission set cannot carry it. The Standard User, Marketing User and System Administrator profiles already include it; Minimum Access does not. The user's profile, or a permission set of your own → System Permissions → Create Public Links. Who can publish a file
Pause, resume, change, or stop a donor's recurring gift The Manage Recurring Gifts custom permission. Fundraising_Admin includes it; Fundraising_User does not. Without it the recurring-gift panel is read-only and Stop Recurring Donation refuses with "You are not authorized to cancel recurring gifts." A permission set of your own → Custom Permissions → Manage Recurring Gifts, assigned to the fundraisers you trust with billing changes. Who can use it

Changing a fundraiser needs Campaign edit, which takes the Marketing User checkbox and an object permission. Salesforce lets a user create or edit campaigns only when they have both the Marketing User checkbox on their user record and Create and Edit on the Campaign object. Neither can come from the packaged sets: the checkbox lives on the user record, and Salesforce removes standard-object grants from a managed package's permission sets. System Administrators already have the object permission but still need the checkbox. Staff on the Standard User profile, which only reads campaigns, need both. Every change made in the Fundraisers console needs it: creating a fundraiser, editing it, switching it live or pausing it, the Story tab, ticket tiers, the FAQ and question selections, and showing or hiding the Updates and Documents panels. A staff user without it can still open and read every fundraiser, but each save is refused with "You don't have edit access to this campaign." (or "You don't have permission to create campaigns." for a new one). To give it: Setup → Users → Users → Edit next to the person → tick Marketing User → Save; then, unless their profile already grants it, create a permission set of your own with Object Settings → Campaigns → Read, Create, Edit and assign it to them. Your campaign sharing must also give them edit access to the fundraiser itself.

Edits and deletes follow your org's sharing. A staff user can edit or delete a campaign post, or edit a shared FAQ or question template, only when your sharing gives them edit access to that record. Fundraising_ReadOnly cannot edit either. Checking an event seat in is the exception: Fundraising_User and Fundraising_Admin carry Modify All on Event Attendee, so anyone holding either set can check in any seat (checking in).

2. Confirm record type mapping#

The app writes donations, events, and tributes onto Opportunity and Campaign record types. The install seeds a default mapping of eight records pointing at the record types it ships, so this step arrives green — the evidence line on Get Started reads "8 of 8 mapping records present".

Open Settings → System → Record Types only if your org already uses its own record types for gifts and you want the app to write to those instead.

Change this before you take gifts, not after

Remapping later does not move donations that were already written under the old record type. Decide now if you are going to.

Assign the Offline Donation layout#

Setup only — one minute, and only if you record gifts that arrive outside the payment gateway.

The app ships a record type called Offline Donation for cheques, cash at an event, and wires reconciled by hand, together with an Offline Donation Layout that omits every gateway field and the Refund action — a gift that was never charged through a processor has no payment account, no PaymentIntent, and nothing to refund, so showing those fields invites staff to fill in numbers that mean nothing.

The package can ship the layout but cannot assign it to your profiles. Which layout a profile sees for a record type is stored on the Profile, and Profiles are not a packageable component in a second-generation managed package — so on a fresh install the Offline Donation record type falls back to whatever Opportunity layout your profile already had, and the gateway fields reappear. Nothing breaks; the form is just wrong for the job.

To fix it, in Salesforce Setup → Object Manager → Opportunity → Page Layouts → Page Layout Assignment → Edit Assignment: find the Offline Donation column, select the cells for the profiles your fundraising staff use, and set them to Offline Donation Layout. Save. Do the same for Donation Layout and Grant Layout in their columns if those are not already assigned. Packaged layouts appear with the namespace prefix in that list, so look for the one whose name ends in Offline Donation Layout rather than an exact string match.

Why the record type exists at all

Anything saved on the Donation record type must name a payment account — the app rejects the save otherwise, because a gift recorded without one cannot be refunded, receipted from the processor, or reconciled against a payout. An offline gift never had an account to name. The separate record type is what lets that rule stay strict without making genuinely offline gifts unrecordable, and it follows your record-type mapping rather than a fixed name, so it still guards the right record type if you point the Donation slot at one of your own. Offline gifts are receipt-numbered on the same series as gateway gifts — the year-end statement shows no gaps.

The record pages, and the part of them that is yours#

Read once — nothing to do today, but it explains a surprise that otherwise arrives with your first upgrade.

The package ships ten Lightning record pages — for Campaign, Contact, Account, Opportunity and its own objects — and assigns each one as the Pledgivo app's default for that object. That scoping is deliberate: a Contact opened inside the Pledgivo app gets the donor page with lifetime giving and recurring schedules, and the same Contact opened in Sales or Service still gets whatever page your org already showed there. Nothing you had is replaced.

The pages themselves are managed. You cannot edit them, and each upgrade updates them for you. If you would rather use your own page, assign it — your assignment always wins over the package's, and an upgrade never takes it back. The trade is that you then own it: components added in a later version land on the packaged page, not on yours.

Page layouts do not upgrade — new fields will not appear on their own

Every one of these pages renders the record's fields through the standard Details section, and that section reads your page layout, not the Lightning page. Page layouts install unmanaged: they are yours to edit from day one, and for exactly that reason Salesforce never upgrades them. So when a later version adds a field, the field is installed and the app writes to it — but it is invisible until someone adds it to the layout in Setup → Object Manager → object → Page Layouts. Release notes name new fields for this reason; treat that list as a short layout to-do, not just news.

These pages are assigned for desktop. In the Salesforce mobile app, records open on whatever page your org already uses for them — the app's consoles and record-page components are built for a full-width screen, and a phone-sized version is not part of this release.

3. Create the donation site#

Get Started step 3, part one

Donors give on an Experience Cloud site, not inside Salesforce. Internal-only use — staff recording gifts taken over the phone — does not need one; anything public does.

The console's Experience Cloud → Site & Domain panel is the checklist for this whole section. It grades each step against your org where Salesforce exposes an API and says so plainly where it does not: a step marked Can't verify is not a warning, it is the honest state, and it is yours to confirm.

If Setup has no Digital Experiences menu, switch the feature on first: Setup → Digital Experiences → Settings → Enable Digital Experiences. It is org-wide, asks you to pick an Enhanced Domain subdomain, and cannot be undone.

Site and Domain panel header with its progress counter and first steps
What it shows. Settings → Site & Domain, reading 4 of 7 steps confirmed done. The counter line spells out its own arithmetic: Done and Pending are read from your org just now, and Can't verify is not counted as progress either way. Re-check all at the top re-reads everything — you will press it after each trip into Setup.
Setup All Sites page with the New button
What it shows. Setup → Digital Experiences → All Sites, where New starts the template gallery. The panel also offers a one-line sf community create command for anyone who works from a terminal; the UI route below is the same outcome.
Experience Cloud template gallery with Build Your Own LWR
What it shows. The gallery — Salesforce titles it "Choose the Experience You Love", not "New Site". Pick Build Your Own (LWR). The package's public components are Lightning Web Runtime components and cannot be dropped onto any of the Aura templates beside it.
Enter a Name screen with Name and URL fields
What it shows. The whole of the create form: a Name, an optional URL, and Create. There is no authentication or "public access" choice anywhere in this wizard — guest access is a separate switch in Experience Builder, two screens later. Keep the URL short and permanent: it becomes part of every public donation link, so fundraising, not fundraising-2026-test.
Experience Workspaces home after the site is created
What it shows. Creation takes about a minute and drops you in Experience Workspaces. Two tiles here matter later: Builder, where you design pages and switch on public access, and Administration, which is the only place with an Activate button.
Experience Builder open on the new site
What it shows. Builder on a freshly created site. The "What's New in Summer '26" dialog over it is Salesforce's own release notice, not the app — dismiss it. Settings live behind the gear icon in the left rail.
Experience Builder Settings General page before public access is enabled
What it shows. Builder → gear → Settings → General. Template, public access, published status and the guest user profile link all sit on this one screen — you will come back to it in the next section to reach the guest user.
Public Access checkbox ticked in Experience Builder settings
What it shows. Guest users can see and interact with the site without logging in, ticked. Without it the donation page serves a login screen to donors. Salesforce exposes no API for this toggle, so the Site & Domain step for it stays Can't verify however many times you re-check — set it once and move on.

4. The site's guest user#

Get Started step 3, part two

Every public visitor to your site is one Salesforce user: the site's guest user. What that user can reach is what the public can reach, so this section is the security boundary of the entire public experience — and it is where a first setup most often stalls, because the guest user is not where you would look for it.

How to actually reach the guest user

It is not in Setup → Users (no list view contains it), and Experience Workspaces → Administration → Members cannot assign to it — that page defines which profiles and permission sets count as site members, which is a different thing entirely.

The route that works: Workspaces → Administration → Pages → Go to Force.com → Public Access Settings → View Users → the user named "<your site> Site Guest User".

From there, Permission Set Assignments → Edit Assignments behaves like any other user.

Two permission sets have to reach that user, and only one of them ships with the package.

Set Where it comes from Why
Fundraising_GuestDonor Packaged Every custom object and field a public donation, event or ticket page reads
A Guest Campaign Read set you create (the name is yours; that is the one in the screenshots) You, by hand Campaign is a standard object, and Salesforce strips standard-object permissions out of a packaged permission set at build time

A fundraiser is a Campaign record, so without that second grant every public campaign, donation and ticket page fails to load.

Read, and nothing else

A guest user must never hold Create, Edit or Delete on Campaign. When you create the permission set, leave the License field as --None--: the picklist offers no "Guest User License" option, and a set created under the org's Salesforce licence cannot be assigned to a guest user at all — the assignment fails three clicks later with a licence mismatch.

The site guest user record reached through Public Access Settings
What it shows. The guest user record, reached through Public Access Settings → View Users. It is a real user with a real name derived from the site — this one is Fundraising Site Guest User — and a real Permission Set Assignments related list.
The guest user holding the packaged Fundraising Guest Donor permission set
What it shows. The packaged Fundraising Guest Donor set assigned. This is per site, not org-wide: repointing the package at a different site later means assigning it there too. A site whose guest user never got this fails quietly — pages load, forms render, and every donation breaks on submit.
Select Users to Assign screen showing a list view error banner
What it shows. Manage Assignments → Add Assignment on your own Campaign-read set, and the banner Salesforce puts there: "The list view you requested was deleted, or you don't have permission to view it." The only view offered is Recently Viewed. This is not something you broke. Open the guest user's record first — as in the two screens above — and it will be in Recently Viewed when you get here.
Recently Viewed users list with the site guest user ticked
What it shows. The guest user ticked in Recently Viewed. Read the name before you tick — a site guest user and a real staff member can sit two rows apart.
Select an expiration option for assigned users
What it shows. Salesforce asks for an expiry on the assignment. Leave it at No expiration date — an expiring grant on a guest user is a donation page that stops working on a date nobody wrote down.
Assignment result showing the guest user, Guest User License, Success
What it shows. The result row: the guest user, Guest User License, Success. The licence column is the confirmation that matters — it is what proves the set you built was assignable to a guest at all.
Site and Domain steps 1 through 7 with their statuses after the guest user work
What it shows. The checklist after Re-check all: steps 1, 4 and 5 Done, steps 3 and 6 Can't verify by design, step 7 Pending until you publish. Below the numbered list sits an un-numbered row headed "Comes later — after you set up payments" — the guest credential grant. It is checked here, because it is a property of this site's guest user, but the Payments panel owns the procedure and there is exactly one copy of the instructions. That is [section 6](#grant-the-credential). Under it, the Embedding block holds your site's base URL and the allowed-origins list.

5. Build the Stripe credential pair#

Get Started step 4, stages 1 and 2

This package never ships, stores, or creates a payment credential of its own. You build the External Credential and Named Credential yourself in Setup — that is where your Stripe secret key lives, encrypted, and it never touches a custom field or an Apex method.

You build two pairs, not one. This is deliberate, and it is the security boundary of the whole integration:

Pair Suggested names Key to use Who is allowed to reach it
Donor Stripe_Auth_Donor_Restricted + Stripe_Donor_Restricted A restricted key (rk_live_… / rk_test_…) that can take a payment and read its result, but never refund Staff and the site's guest user
Admin Stripe_Auth_Admin_Restricted + Stripe_Admin_Restricted A second restricted key, scoped to refunds, renewals and the reconcilers' reads Staff only. Never the guest user

Those names are suggestions, not requirements. The app reads whichever credential names you save on the Payment Account record, so call the pair anything your team will recognise — Stripe_Auth_Chapter_West and Stripe_Chapter_West if that is the Stripe account it belongs to. If you connect more than one Stripe account you will need different names for the second pair. The full list of what you may rename and what you may not is in Which names are yours, and which must match.

A full secret key (sk_live_… / sk_test_…) is not an accepted value on either credential. The setup guide calls each one and grades it Failed when it answers a request a scoped key would have refused, so an account running a secret key can never reach Ready to take payments.

New to Stripe, or not sure which key is which and what to switch on when you create the restricted one? The two Stripe keys, and what each one may do covers it from the start, with the exact permissions to grant.

Why the panel cannot grade this stage yet

The ten steps are graded per payment account — the checks read the credential a payment account names. Until section 7 creates that account, the four build steps report Action needed no matter how correctly you have built them in Setup. That is why the panel leads with a Do this next card instead of the step list, and why Add account stays disabled until at least one named credential exists in the org. Build the pair first, and let the grading catch up afterwards.

Payments panel with its four stages and ten steps
What it shows. Settings → Payments: ten steps in four stages — get your keys (Stripe), build the credential pair (Setup), grant it (Setup), connect and test (this page). The header carries a live tally and the legend that explains it. The console guides the build; it never asks you to type a secret into it.
The Do this next card at the top of the Payments panel
What it shows. The first thing in the panel body: one card, one move, derived from the same org state as the steps below it so it cannot contradict them. On a fresh org it points at Setup and carries an Open Named Credentials in Setup link plus an I've built them — check again button, because the shape of this whole job is leave the console, build something in Setup, come back.
Stage 2 build the credential pair with its public and admin toggle
What it shows. Stage 2 with its Public key / Admin key toggle. You walk the same four steps twice, once per key, and the toggle keeps each side's progress separate. Every value you need to paste is printed here with a copy control.
Callout explaining that stage 2 cannot be graded until a payment account names the credential
What it shows. The callout that saves an hour of second-guessing: these four steps stay ungraded until a payment account names the credential they check. Build them anyway. If you press Re-check and see "Nothing built yet" with four perfectly good records sitting in Setup, this is why — you have not made a mistake.
Stage 3 grant it and stage 4 connect and test
What it shows. The back half of the guide. Stage 3 is the two grants — staff, then the site guest user — covered in [section 6](#grant-the-credential). Stage 4 is the payment account and the two live-call proofs, covered in [section 7](#add-the-payment-account). Both proof buttons stay disabled, with the reason printed beside them, until the account exists.

Build it in Setup#

The external credential holds the secret; the named credential holds the URL and the callout options. Neither is usable without the other.

New External Credential form with label, name and authentication protocol
What it shows. Setup → Security → Named Credentials → External Credentials → New. Label it for humans and give it any Name you like — Stripe_Auth_Donor_Restricted here is the panel's suggestion, not a value the app looks for. Set Authentication Protocol to Custom, which is not optional — Stripe authenticates with a bearer token, not OAuth.
Saved external credential showing the Principals and Custom Headers related lists
What it shows. Saved. The Principals and Custom Headers lists only appear after the first save, which is why this is two operations and not one. Check the Name field on the saved record before you go further — whatever Salesforce stored there is what the header formula must use. (This capture is from a namespaced test org taken before the 2026-09-01 namespace swap, so the saved name carries a pledgivo_test__ prefix where yours would read pledgivo__; in your org the name is exactly what you typed.)
Create Principal modal with parameter name, sequence number and identity type
What it shows. The Create Principal modal. Parameter name OrgPrincipal, Sequence Number 1 (required, and it defaults correctly), identity type Named Principal. Add one authentication parameter named SecretKey whose value is Bearer rk_… — the word Bearer and a space must precede the key, because the header is passed through verbatim. Once saved the value is write-only: Salesforce will never show it again, to you or to any Apex.
External credential showing its principal and an Authorization custom header
What it shows. The donor credential with both pieces: the principal, and a custom header named Authorization whose value is {!$Credential.YourCredentialName.YourParameterName}. The header name Authorization is fixed — Stripe requires it and the setup guide looks for it. Both halves inside the braces are names you chose: the credential's saved API name and the authentication parameter you stored the key in. Copy them from the record rather than retyping them. A formula pointing at a name that does not exist saves without complaint and fails silently at the first live donation. The dialog has a third field, Sequence Number: it is required, Salesforce prefills it with the next free number, and on a credential holding one header there is nothing to change.
The completed admin external credential
What it shows. The second external credential, Stripe_Auth_Admin_Restricted — identical in shape, different key, its own OrgPrincipal. The two share no state, which is the point: a leak of one does not expose the other.
New Named Credential form with URL and callout options
What it shows. Named Credentials → New. URL https://api.stripe.com, no trailing path, and the external credential you just built. Everything else you need is on this same form — you do not have to save and re-edit.
Saved donor named credential with its callout options
What it shows. The saved donor credential and the two checkboxes that decide whether any of this works. Allow Formulas in HTTP Header must be on or your merge field is sent to Stripe as literal text. Generate Authorization Header must be off or Salesforce overwrites your header with its own and Stripe answers 401. Salesforce's defaults are the wrong way round on both.
Saved admin named credential
What it shows. The admin named credential — same URL, same two checkbox corrections, pointed at the admin external credential.
Named credential detail page showing the allowed namespaces field
What it shows. The part of the credential screen nothing warns you about. A named credential built by an admin is invisible to managed-package code until the package's namespace is listed in Allowed Namespaces for Callouts. It is a free-text field with no picker, so a typo fails at callout time rather than at save time. Do it on both credentials.
Setup Named Credentials list showing both Stripe credentials
What it shows. Setup listing both named credentials, each a Secured Endpoint against https://api.stripe.com. This is the finished state of stage 2 — and the state that unlocks the Add account button back in the console.

6. Grant the credential#

Get Started step 5 · Payments stage 3

This is the step that catches people out. A credential nobody is granted is a credential nobody can use — including the staff who issue refunds from a donation record. And granting it to your site's guest user widens what an anonymous visitor can reach, so it stays your decision. The package never makes it for you.

Public donations fail at the payment step until you do this. Everything else on the page works, which is exactly what makes it easy to miss.

Why the permission set can't be part of the package

Salesforce does not allow a managed permission set to grant access to an external credential principal — the API rejects it outright. That is why these sets have to be ones you create in your own org, and why Fundraising_Admin can never ship with the grant on it.

Build two permission sets, because two audiences hold them — not one per credential. The site set carries the donor credential's principal and goes to your site guest user and to staff who take payments; its License must stay --None--, because a set with a license on it cannot be assigned to a guest user. The admin set carries the admin credential's principal and goes to staff who issue refunds and to every user who owns a scheduled job, and never to the guest user. Name these sets whatever you like — the app never looks for them by name; it discovers which of your permission sets carries the grant and reports it back to you. Something like Fundraising_Site_Guest_Access / Fundraising_Payments_Admin will read better in six months than anything generic:

  1. Setup → Permission Sets → New. Set License as above — the site set must stay --None--, the admin set can be Any.
  2. On the set: Apps → External Credential Principal Access → Edit.
  3. Enable the entry for that set's credential principal — it reads like Stripe_Auth_Donor_Restricted - OrgPrincipal on the site set, or whatever you named yours → Save.
  4. On the same set: Object Settings → User External Credentials → Edit, tick Read and Create → Save.
  5. Manage Assignments → site set: add anyone who takes payments. Admin set: add anyone who issues refunds and every user who owns a scheduled job — a job authenticates as its owner, so a reconciler scheduled by someone without the set fails quietly.
  6. Assign the site set — and only the site set — to the site guest user, via the Public Access Settings route.

Step 4 is not optional, and it is a different page from step 3

A credential callout needs two grants: the principal (step 3) and Read + Create on the standard User External Credential object (step 4). Miss the second and the donation fails at exactly the same point as missing the first — the donor sees a generic apology and the real error only appears in Diagnostic Logs:

You don't have read permissions on the User External Credential object.

The package ships this grant in its own permission set, and Salesforce strips it during packaging: a managed permission set cannot carry standard-object permissions, and the build reports success anyway. That is why it has to be yours. All three places in the Settings console that grade this step check for it — Get Started step 5, the Payments panel's Let your public site use the credential, and the Site & domain panel — and each names the object by name when it is missing, so none of them can show green on an org where donations still fail.

External Credential Principal Access page listing both principals
What it shows. The page that is easy to miss — it is under Apps on the permission set, not under Object Settings or System Permissions. Both principals are listed, and they look almost identical: read the credential name in the row before you enable one.
Permission set showing the enabled external credential principal
What it shows. The grant saved on the permission set. One set per role is enough for every gateway account you add: the site set can carry the donor principal of each account and the admin set each admin principal. What must never happen is both principals of one account on a set the guest user holds — that hands your donation pages the refund key.
Guest user holding three permission sets
What it shows. The finished guest user: Fundraising Guest Donor (packaged), your Campaign read set, and the site set (whatever you named it). Three sets, no more. From this moment a public donation can reach Stripe.

Don't forget the guest user — and don't give it the admin credential

Assigning to staff but not to the site guest user is the failure mode that looks healthiest: internal testing works and every public donation fails at payment.

The opposite mistake is worse and just as quiet. Putting the guest user on the set that grants the admin credential lets an anonymous visitor's session reach the back-office key, refunds included.

7. Add the payment account and prove it works#

Payments stage 4 — this is what closes Get Started step 4

A named credential is plumbing. The Payment Account record is what a campaign actually charges through, and it is the record every one of the ten checks is graded against. Until an active one exists, the donation form cannot take money and the guide cannot grade itself.

New payment account modal
What it shows. Add account, now enabled because named credentials exist. A display label of your own, the two credential pickers, your publishable key, and the mode. "Charity Stripe (test)" is a useful convention while you are still on test keys — the label appears wherever staff pick an account.
The two credential pickers listing named credentials by label and API name
What it shows. Both pickers list every named credential in the org by Label (API name), so you can tell two similarly-labelled credentials apart. Public goes in the public slot, admin in the admin slot — swapping them hands your site guest user a refund-capable key. The publishable key is masked as you type; it is the only key that belongs in Salesforce data, because it is public by design.
Payment account saved and reporting ready to take payments
What it shows. Saved — "Charity Stripe (test) · ready to take payments", with a tally of 9 done · 0 to do · 2 to prove. "To prove" is not a warning: it is the two live-call checks that cannot be answered without actually calling Stripe.
Stage 2 steps now graded green after the account was saved
What it shows. The four build steps you completed in [section 5](#build-the-credential-pair), grading green for the first time. Nothing about the credentials changed — the account gave the checks something to read.
Stage 4 with the restricted-key check and the connection test
What it shows. Stage 4 with both buttons now live, and the reference table of values beside them. Everything in that table is a placeholder pattern — rk_test_…PUBLIC, {!$Credential.…SecretKey}, https://api.stripe.com — never a real key. The console has no way to display a secret, by design.
The three callout option rows all reading correct
What it shows. The checkbox audit, read back from your actual credential rather than assumed: Enabled for Callouts on, Generate Authorization Header off, Allow Formulas in HTTP Header on. Two of those three are the opposite of Salesforce's defaults, so this row is worth reading rather than skimming.
Payments panel with the key-scope check, before the connection test
What it shows. Prove the donor key is restricted asks Stripe what the donor key is actually allowed to do, and fails you if it turns out to carry refund rights. Its sibling, Prove the admin key is restricted, asks the same question of the back-office key and fails you if it turns out to be a full secret key. They are the only checks that can catch either mistake — no amount of Setup inspection can, because Salesforce cannot read back a saved secret. (Filmed before the admin check was added, so the shot shows one live check where there are now two.)
Connection test reporting a successful Stripe response
What it shows. 1 step left, and step 10 answered: a real callout to Stripe under the credential you built, reporting the account Stripe replied with. This exercises the whole chain end to end — key, header, callout options, namespace grant, permission-set grant. If it passes, the credential is correct regardless of what anything else says.
Payment accounts tab listing the configured account
What it shows. The Payment accounts tab — every account, its mode, its credentials and whether it is active, with the guide one tab away. Most orgs need exactly one row here; multiple accounts exist for organizations that split gifts across separate Stripe accounts.

Full walkthrough: Connecting Stripe.

8. Background jobs#

Donations are finalized, payments swept, refunds reconciled, and campaign totals repaired by scheduled Apex — 24 cron entries on the settings a fresh install ships with. The install creates all 24, and the step still reports "24 of 24 background jobs are scheduled". Press the button anyway.

24 is the default, not a fixed number

Salesforce has no sub-hourly schedule, so a job that runs every five minutes is really twelve separate cron entries an hour apart in start time. Two of the families let you choose that cadence — see the table below — and the count moves with it: putting the finalizer on 15 minutes replaces its twelve entries with four, and the step then reports 16 of 16. The console always grades your org against the cadence you chose, never against 24.

This is the one step a fresh install does not finish for you

A scheduled job belongs to whoever scheduled it, and CronTrigger.OwnerId is stamped when the job is created and never changes afterwards. The post-install script runs as a Salesforce system user your org cannot reach — so the 24 jobs it creates are real cron entries owned by a user that no longer resolves. They are scheduled, they sit at WAITING on the right cadence, and every pass dies the moment it starts: an owner holding no permission set cannot see the objects the jobs read, so the query the batch opens with throws before it reads a row. The count is honest and reassuring and tells you nothing about the thing that matters.

Measured on a virgin org: all 24 jobs owned by an unreachable user, ten consecutive batch runs, zero successes, and the finalizer marker still empty half an hour later. Re-owned, the very next pass completed and stamped the marker. The failure is silent — the unhandled-exception notice goes to a mailbox that does not exist.

On a fresh install the step therefore reads amber, not green: "The background jobs have not completed a pass yet, and no gift is waiting on them." Amber rather than red because nothing is at risk yet — you have taken no gift — but the work is real, and it will not clear on its own. Press Restart the background jobs. It aborts each orphaned job and reschedules it under your own login, and the step turns green within a few minutes, once the finalizer has completed its first pass and stamped the marker the console reads.

The Scheduled Jobs health row is the thing to read, not the count. It resolves each job's owner and names the problem when an owner does not resolve. Saving any Settings panel performs the same repair in passing, so if you configured the console before you reached this step you may find the row already green — that is the reclaim having happened early, not the install having done it for you.

Do it before you share a donation link, not after.

Restart them as a user who can reach Stripe — order matters

Re-owning the jobs fixes who runs them. It does not, on its own, make them able to authenticate. A named-principal callout runs as the owner of the job, so a job owned by someone holding no permission set that grants the external credential's principal fails every single time it fires — refunds and chargebacks never come back, recurring gifts never charge, and a payment taken but not recorded is never swept up. Every cron row still reads WAITING, on time, and the job count still reads 24 of 24.

So do step 6 before this one, and press Restart the background jobs while logged in as a user who holds that grant — the jobs are rescheduled under whoever presses it. The Scheduled Jobs health row checks this from that point on: it names the owner when the owner holds no such grant, and offers its own Fix button only when you hold one, because the repair is to take the jobs over.

One limit, stated plainly: a package cannot see which credential a given grant belongs to (SetupEntityAccess names the permission set but its target is not queryable). So the row can prove an owner holds no grant, and it can never prove the grant they hold is the right one. When every owner is granted, the row keeps its colour and adds a sentence asking you to confirm the grant is on the credential your payment account names.

Confirm this step is green before you share a donation link

The Finalizer-<m> family is what turns a confirmed card payment into an Opportunity, a donor record and a receipt. With the finalizer stopped, a donor's card is still charged and they still land on the thank-you page reading "your receipt is on its way" — but nothing is created in Salesforce. The gift waits as a Donation Staging record and finalizes on the next pass once the jobs are running again, so nothing is lost permanently. The danger is the gap in between, when your reports show fewer gifts than your Stripe dashboard does.

Two different situations, and the form treats them differently on purpose.

No pass has ever completed in this org — the state a fresh install sits in until the jobs are re-owned, on this step or by the first Settings save. Nothing has ever demonstrated that a charged card becomes an Opportunity here, so the donation form and the ticket page refuse the payment outright. Instead of a card field the visitor sees: "Online giving is temporarily unavailable on this site. Its background jobs have not completed a pass yet…" — with the remedy named, because on a fresh install the person seeing that message is almost always you, testing your own page. It clears itself: the finalizer stamps its marker at the end of every pass whether or not there was anything to reconcile, so the form comes back on its own within one cadence of your pressing Restart the background jobs — there is nothing else to switch back on.

The pipeline was running and has stalled — a healthy org whose jobs paused. Here the form deliberately keeps taking gifts. Refusing live donations over a job pause that recovers by itself would be the larger outage, and nothing is lost: the gift waits as a staging record and finalizes on the next pass. What happens instead is the alarm — every gift taken while the pipeline is stopped writes a PIPELINE_STALE error to the Transaction Log, and both this step and the Gifts Being Recorded health check turn red and name the number — "3 gifts are charged but NOT recorded." That count is what separates red from the amber above: amber means the jobs are not running and nothing is waiting on them; red means gifts are.

A green job count is not proof the jobs are running

"24 of 24 background jobs are scheduled" says the cron entries exist — a job that is scheduled but fails on every fire still counts. The console therefore reads a second signal: the finalizer stamps the time each time it completes a pass, and if that stamp is too old this step goes red regardless of the job count. That is the number to trust.

"Too old" tracks the cadence you chose rather than being a fixed clock — three missed passes, and never less than 30 minutes. On the default five-minute finalizer that is 30 minutes, six missed passes. On an hourly one it is three hours. A fixed threshold could not work here: 30 minutes is shorter than a single pass at the slowest cadence, so an org finalizing hourly would have reported itself broken for half of every hour while working perfectly. Slowing the finalizer therefore buys you slower detection as well as fewer jobs — that is the trade, and it is the reason the default is five minutes.

You need this step after a fresh install, if some jobs were aborted (a deployment that touched a scheduled class will do it), or if the post-install script did not complete. Settings console → Get Started, where the button reads Restart the background jobs when the jobs exist but are not running and Schedule the missing jobs when some are genuinely absent — the same button either way. Scheduled Jobs has the per-job breakdown and on-demand runs. Pressing it on a healthy org changes nothing: a job that exists and is running keeps its schedule, including a cadence you customized. A job whose owner no longer resolves is aborted and recreated under you.

To confirm a gift really landed, open Donation Monitor: it lists every staging record still waiting, and warns at the top when the Finalizer family is not scheduled.

The families, and which cadences you can change#

Family Count Cadence Yours to change?
Finalizer-<m> 12 Every 5 minutes Yes — 5, 10, 15, 20, 30 or 60
ReconciliationHeartbeat-<m> 4 Every 15 minutes Yes — same six choices
Fundraising — Guest Request Sweep-<m> 3 Every 20 minutes No
Recurring donations, retention purge 2 Daily No
StripePaymentSweep, RefundReconcile 2 Hourly No
CampaignRollup 1 Nightly No

Counts and cadences above are the defaults. Change the two settable ones in Settings → System → Scheduled Jobs, which shows what each cadence costs you in cron entries before you save it — Salesforce caps an org at 100 scheduled Apex jobs in total, and this app is not the only thing in your org competing for them.

The finalizer is the one to leave alone unless you have a reason

It is what turns a confirmed card payment into a donation record, so its cadence is the delay a donor's gift waits before it appears in Salesforce — and, as described above, it also sets how long a genuinely stopped pipeline stays quiet before the console says so. Widen it to free up cron slots, not to tidy the list.

9. Organization name and logo#

Your organization name and logo appear on the donation form, the receipt email, and the donor portal. The install seeds a placeholder logo so nothing renders broken on day one — it is not your logo.

The display name has no placeholder. Leave it blank and the public pages do not quietly borrow your Salesforce org name — the donation form's trust bar, the thank-you page, the event-ticket page and the donor portal simply head with no name at all, and the campaign gallery falls back to the literal word "Fundraising". The one page that does substitute your Salesforce org name is the internal receipt print view, because a tax receipt with an empty letterhead is not a receipt. So set this before you share a link, not after.

Settings → General → Organization & Donations holds the rest of your public-facing identity too: the footer line, terms and privacy links, and the social handles that render in the footer band of every public donation page. They live here rather than on a page design because they describe your organization, not a visual style — so changing a campaign's style never changes which privacy policy its donors are pointed at. Leave any of them blank and the footer omits it.

The same panel holds your gift rules: minimum and maximum amount, suggested amounts, processing-fee percentage and fixed portion, the donation page path, the three donor-portal sign-in windows (code expiry, idle timeout, session cap), and the default thank-you button. There is no mailing address field — receipts take their address from your Salesforce company information.

The Get Started step reads back what a donor will see: 'Public pages show "Acme Nonprofit"'. Set the display name before you send anyone a link.

Set the address donor email comes from#

Not a Get Started step — but do it before you take a real donation

Out of the box, every receipt, ticket confirmation and payment-failure notice is sent from whichever user or background job triggered it. On a live org that means a donor's tax receipt arrives from a scheduled-job address nobody monitors. Apex cannot invent a sender, so fixing this is a two-part job and the Salesforce half comes first.

  1. Setup → Organization-Wide Addresses → Add. Enter the address donors should see (e.g. donations@your-org.org) and a display name.
  2. Tick Allow All Profiles to Use this From Address. This is not optional: receipts are sent by the Experience Cloud guest user and by scheduled jobs, and an address restricted to selected profiles simply won't be used by either.
  3. Open the confirmation email Salesforce sends to that address and click through, so the record shows Verified.
  4. Back in Settings → General → Email & Receipts, put the same address in From email and save.
  5. Settings → System → Health Check → the Donor Email Sender row confirms it. That row is the only place the difference shows: a From email that matches no verified, all-profiles Org-Wide Email Address costs no donor a receipt — it just silently keeps sending from the triggering user, with no error anywhere.

Once a From email is in effect, the From name field stops being applied — Salesforce takes the display name from the Org-Wide Email Address record instead. Set the name you want on that record.

Full walkthrough: Brand your receipt and notification emails.

10. Publish and activate the site#

This is what finally closes Get Started step 3

Back to the one step the earlier sections left open. Two operations remain, they are different things, and they live on different screens.

Published is not the same as Live

Publish pushes your page changes to the site. Activate makes the site reachable by the public. A published site that has never been activated serves nothing to anyone. The Activate button is not in Experience Builder — it is in Workspaces → Administration → Settings, next to the status.

Get Started shows step 3 as Partly done until then, and quotes the status by its API name: the Preview you see in Workspaces reads as UnderConstruction in the console. Same state, two names.

Experience Builder with the Publish button
What it shows. Publish, top-right in Experience Builder. Publish once your pages are built, and again after every page change — a change that is saved but not published is not on the live site.
Publish confirmation dialog showing the live site URL
What it shows. The confirmation dialog, printing the site's real public URL. Read it and keep it — this is the path donors will use, and it is the one the app's Embedding block reports. Other Salesforce screens sometimes show an internal Visualforce-era path for the same site; this dialog and the Site & Domain panel agree with each other, and they are the ones to trust.
Publishing in progress confirmation
What it shows. "We're publishing your changes now." Publishing runs in the background and Salesforce emails you when it finishes. Wait for that email before making further changes — and note that the site's status has not changed: it is still Preview.

Then activate:

  1. Setup → Digital Experiences → All Sites → Workspaces beside your site.
  2. Administration → Settings.
  3. Activate, next to the status.

Salesforce warns that activating sends a welcome email to members; you can suppress that beforehand under Administration → Emails. Once the status reads Live, come back to Get Started, press Re-check, and the counter closes at 7 / 7 — your org is ready to take donations.


Third-party content#

Optional — nothing here is required

During the install Salesforce shows you an Approve Third-Party Access dialog listing every external domain the package wants your org to trust. This package lists Stripe and nothing else:

Domain Why
js.stripe.com Loads Stripe.js, which collects the card details
api.stripe.com The payment API the browser confirms against
m.stripe.com, m.stripe.network Stripe's fraud-signal endpoints — payments fail without them
hooks.stripe.com 3-D Secure / bank authentication screens

That is deliberate. A fundraising package asking to reach a list of unfamiliar domains reads as a warning rather than as a payment integration, so the optional features that use other hosts ship without their trusted sites. You add those yourself, only if you want the feature.

Add this domain Directive Unlocks
https://images.unsplash.com Image Using an Unsplash photo as a campaign hero or social card
https://www.youtube.com Frame A YouTube video in a campaign page video block
https://www.youtube-nocookie.com Frame YouTube privacy-enhanced (no-cookie) embeds
https://player.vimeo.com Frame A Vimeo video in a campaign page video block

To add one: Setup → Security → CSP Trusted Sites → New Trusted Site. Enter the URL exactly as written above, name it anything, tick the directive shown, leave Context as All, and save.

The optional third-party content step listing four hosts and which are trusted

What it shows. The optional step, reading your org's trusted sites live: here images.unsplash.com and www.youtube-nocookie.com are TRUSTED and the other two are NOT ADDED — "2 of 4 optional content hosts are trusted in this org". It never counts against your seven steps and it never offers a fix button, because Salesforce does not let Apex create a trusted site. This one is yours.

A video in a campaign story is a narrower case

The story field's @[…](youtube:…) block only ever embeds https://www.youtube-nocookie.com, and only when Play story videos on the page is switched on in Settings → General → Organization & Donations → Story media. Left off — the default — a story video is a poster card that links out to YouTube in a new tab and needs no trusted site at all.

If you do switch it on, the Organization panel prints the exact recipe: URL https://www.youtube-nocookie.com, frame-src only, and Context Experience Builder Sites (narrower than All, because a story only ever renders on a public site). See Write a campaign story. www.youtube.com and player.vimeo.com above are for the Page Canvas video block, which is a different feature and accepts both hosts.

Your campaigns start with a packaged hero image, not a stock photo

The package ships its own artwork — a hero backdrop and a social share card, both drawn from the brand palette rather than licensed from a stock library — and publishes them into your org at install time. The six packaged page designs are stamped with the hero automatically and the share card resolves org-wide, so a campaign looks finished on day one. A design you create starts with an empty hero field and stays that way — a blank there is treated as your choice, not as something to fill in for you, and a campaign built on it shows a plain coloured hero unless the campaign supplies its own header image.

They are served from your own org's file domain, which browsers allow without a trusted site, so nothing on the list above is needed to see them. Replace the hero at any time by uploading your own image in Designs, or the share card in Organization & Donations; an image you have chosen is never overwritten by a package upgrade. To paste a URL from an outside host instead — Unsplash, your own CDN — that host needs a CSP trusted site first, per the table above.

An image from an untrusted host falls back to a packaged image

If a campaign's hero or an event banner points at a host your org does not trust, the browser blocks it. A page wearing one of the packaged themes then shows an abstract illustration packaged for that theme. A design you created tries its own default hero next, and then the generic packaged hero. The illustrations and the generic hero are part of the package itself, so they always load and need no trusted site. The donor sees a finished page, but not your picture.

The optional step above tells you when this is happening. After the count it lists every outside host that a published campaign or an active design takes its hero from and that no trusted site covers, for example "Campaign images come from 1 host this org does not trust, so donors see a fallback image instead: https://cdn.example.org". Add that host with the Image directive and the campaign's own picture comes back. Relative links and images stored in Salesforce are never listed, because the browser does not block them. A custom domain you set up for your Experience site is not recognised as Salesforce's own and can appear in the list. If you do not have access to read the org's trusted-site list, the step says it could not be checked instead of guessing, and shows Not checked against each optional host rather than a count.


When you're done#

The Get Started panel shows 7/7 and "Your org is ready to take donations." Setup is finished — but your org still has nothing to give to.

Everything you raise money with lives in the Fundraisers tab#

Campaigns and ticketed events are not created in Settings. Open the Fundraisers tab (App Launcher → Pledgivo → Fundraisers, or the Open Fundraisers button on the finished Get Started panel). It is the console your fundraising team works in day to day, and it is the only place a public donation page comes from.

In the Fundraisers tab What you get
New fundraiser wizard A Campaign, step by step: goal and fund, designations, page design, and its public URL.
Ticket types Turns a campaign into a ticketed event — tiers, prices, capacity, and the registration page.
The fundraiser list Search, filter, publish, or deactivate anything already live.
A fundraiser's detail page Its donations, registrations, attendees, questions, and FAQs in one place.

The Settings console you just finished is for org-wide configuration — payments, receipts, branding, jobs. You will rarely go back to it. The Fundraisers tab is where the actual work happens.

Full walkthrough: Set up your first campaign, or Set up a ticketed event.

Put a donate button on your own website#

Your donation pages work as links, and they also work embedded. The Embedding block at the bottom of Site & Domain is where that is switched on and locked down.

The Embedding block showing base URL, the embed switch and allowed origins

What it shows. Your site's Base URL — the real public path, the one the publish dialog printed — the Allow embedding master switch, and Allowed embed origins. That last list is an allowlist, not a formality: only the origins named there may host your widget, and it takes full origins (https://example.org), not bare domains. During development a local origin such as http://localhost:8910 belongs here and should come out again before you go live.

Full walkthrough: Embed a donate button on your website.

Then prove it end to end#

Once your first campaign is published, send yourself a real gift for the smallest amount Stripe allows and refund it. It is the only way to prove the whole path — form, payment, receipt, refund — works in your org.