Skip to contentIntroducing the Integration Factory
Back to Blog
Managed Packages

What Is the Setup Page in a Salesforce Managed Package?

Jul 9, 2024
ATAppnigma Team
What Is the Setup Page in a Salesforce Managed Package?

Salesforce does not give an installed package a setup page. What it gives you is three hooks. A first-generation (1GP) package can add a Configure link, a second-generation (2GP) package can send the admin to a post install URL, and either kind can run an Apex post install script. Everything the customer's admin actually does to get your app working, like connecting their account to yours, assigning access and choosing settings, happens on a page your team builds and ships inside the package.

For a SaaS company, that page decides how a deal starts. You build the package, pass security review and publish the listing, and then the first real test of the native integration enterprise customers asked for is whether their admin can finish setup without a support ticket. Appnigma's marketplace apps team builds "A Salesforce managed package or HubSpot app built around your product and customer workflows," including install docs that "Give customer admins installation guidance and your sales team a working app to demonstrate." Admins looking for Salesforce's own Setup menu want Salesforce Help rather than this guide.

Updated October 2026. Checked against Salesforce's 1GP and 2GP packaging guides, the Apex Reference and the Secure Coding Guide. The earlier version described a setup page as something every installed package shows, and told readers to have customers paste API keys without saying where those keys are stored. This version replaces that with what Salesforce provides, what your package has to build, and where a customer's credentials belong.

What Salesforce provides after install, and what you build

Salesforce's part of post-install setup ends when the package lands in the customer's org. It offers four hooks, and the setup tab is the fifth piece, which your team builds. This table shows where the customer's admin meets each one.

HookPackage typeWhat the admin seesWhere you set it
Configure link1GPA Configure option for your package that opens a URL or a Visualforce pageConfigure Custom Link field on the package detail
Post install URL2GPA redirect to your page after a successful install or upgrade, plus a link on the Installed Packages pagepostInstallUrl in sfdx-project.json, or --post-install-url
Release notes URL2GPA link on the installer page and the Installed Packages pagereleaseNotesUrl, or --release-notes-url
Post install script1GP and 2GPNothing visible. Apex runs once after install or upgradePost Install Script field (1GP) or postInstallScript (2GP)
Your setup tabAnyThe page that does the real workA Lightning tab, LWC or Visualforce page in your package

The 1GP Configure option is optional. Salesforce's 1GP package creation steps say "(Optional) Choose a custom link from the Configure Custom Link field to display configuration information to installers of your app." The 1GP app documentation guide adds that the link can point "to a URL that contains configuration information, or a Visualforce page that implements the configuration."

The 2GP guides don't describe a Configure Custom Link field. They use URLs instead. Per Share Release Notes and Post-Install Instructions, "subscribers are redirected to your post install URL following a successful package installation or upgrade," and both URLs "display on the Installed Packages page in Setup." The redirect is described for installs through an installation URL, so an admin who installs another way finds the link only on the Installed Packages page. Salesforce's own example sets the post install URL to a page on the vendor's website, so treat it as a hosted setup guide that sends the admin back to the in-org work. Your setup tab should be reachable from the app itself, not only from that redirect.

How to wire the post install URL and script in a 2GP package

In a 2GP package, the post install URL and the post install script are both declared in the package directory entry of sfdx-project.json. This is the shape from Salesforce's 2GP install script guide, with the setup URL pointing at your own instructions.

{
  "path": "force-app",
  "package": "Acme Connector",
  "versionNumber": "1.4.0.NEXT",
  "postInstallScript": "AcmeInstallHandler",
  "uninstallScript": "AcmeUninstallHandler",
  "postInstallUrl": "https://docs.example.com/salesforce/setup",
  "releaseNotesUrl": "https://docs.example.com/salesforce/release-notes"
}

Flags on sf package version create override the file, since "The CLI parameters override the URLs specified in the sfdx-project.json file." The same page shows how to name a Dev Hub user for failure emails with sf package create --error-notification-username. Set it before your first customer install, because a failed post install script is reported only to that user.

The script itself is an Apex class that implements InstallHandler. A useful first-install job is to create the record your setup tab reads, so the tab knows setup hasn't started. This sketch follows the InstallHandler interface. The object names are placeholders.

global class AcmeInstallHandler implements InstallHandler {
  global void onInstall(InstallContext ctx) {
    if (ctx.previousVersion() == null) {
      // First install. Seed the org default the setup tab checks.
      insert new Acme_Settings__c(SetupOwnerId = ctx.organizationId(), Setup_Complete__c = false);
    }
    // Callouts must be async and run after the install commits.
    System.enqueueJob(new AcmeNotifyInstallJob(ctx.organizationId(), ctx.isUpgrade()));
  }
}

Test it with Test.testInstall, which the post install script example shows taking your handler class and the previous version.

What a post install script can and can't do

A post install script runs without a user in the room, and its limits shape what setup it can take off the admin's plate. Salesforce's page on running Apex on package install and upgrade sets the rules. "It runs as a special system user that represents your package," it "is subject to default governor limits," and "If the script fails, the install/upgrade is aborted."

That last rule is the one that costs deals. A script that throws on one customer's data, for example a validation rule your test org didn't have, blocks the install entirely. Keep the synchronous part small and push anything that touches the customer's records into a job.

Calling your own backend from the script is allowed only one way. The script "can only perform callouts using an async operation." Salesforce adds that "The callout occurs after the script is run and the install is complete and committed." It "can initiate batch, scheduled, and future jobs," so a Queueable that tells your backend a new org installed is fine. Our guide to calling your own API from an AppExchange app covers the callout side. It can't call another class in your package that uses with sharing or inherit sharing, because those keywords "can prevent the package from successfully installing." It also never runs in a trial org created with Trialforce, so a trial customer's setup tab can't assume the script already seeded anything.

Uninstall works differently. The UninstallHandler interface says "If the script fails, the uninstall continues but none of the changes performed by the script are committed," and it can't make callouts or start batch, scheduled or future jobs. If your backend needs to know a customer left, don't rely on the uninstall script to tell it.

Where should the setup page store a customer's API key?

Not in a field the customer can read. Salesforce's Storing Sensitive Data guide draws the line by who the secret belongs to. Secrets "common across all users of the package (such as an API key) must be stored in Managed Protected Custom Metadata Types." A protected custom setting is "useful for secrets initialized by the admin user or generated at install time," and the guide tells you to build the entry page so it accepts the secret "without rendering it back on the page." Protected means hidden. Per Protecting Your Intellectual Property, "All data for the custom setting is hidden from the subscriber."

The security review checks this. The Store Sensitive Data Insecurely violation page says a secret kept in a custom object must be encrypted, and "Store the encryption key separately in a protected custom setting or a protected custom metadata API field."

Those rules cover your secrets. The customer's own connection to your product is a different case, and the better pattern there is to avoid a pasted key altogether. Package a named credential and external credential, and let the admin authorize their account with OAuth from your setup tab. The packaging guide is explicit that this is a setup step, not something the package brings with it. Packaged credentials "don't include the access tokens or certificates that are needed to perform authenticated callouts," and after install the admin must create the external credential's principal or populate its tokens, give permission sets access to that principal, and reauthenticate (Components Available in Second-Generation Managed Packages). In 2GP, an auth provider referenced by the external credential isn't packaged and "you must recreate the authentication provider in the subscriber org." Those three or four steps are exactly what your setup page should walk the admin through, in order. Upgrades don't undo the work, because "Salesforce retains the encrypted tokens when you upgrade named credentials in a managed package" (Upgrade a Named Credentials Managed Package). Our named and external credentials guide covers developer and subscriber control of packaged credentials.

Permission sets are the setup step admins skip

Your package can include permission sets, but installing it doesn't give anyone access. The permission sets in packages page says "Subscribers must assign packaged permission sets after installing the package." It also notes that "A permission set is only installed if the subscriber org has at least one user license that matches the permission set." A permission set built on a license the customer doesn't own never arrives, and the admin sees a setup page asking them to assign something that isn't there.

On upgrades, the post install script can assign access, and Salesforce publishes a sample push upgrade script that does it. The push upgrade best practices set the boundary. Auto-assign new components when they extend a feature people already use. "If you're adding a new feature, don't auto-assign the feature to existing users." Our post on customizing push upgrades covers the rollout side.

Salesforce has also published an AI skill for this step. The dx-pkg-post-install-configure skill is meant "to automate the post-install configuration for any Salesforce managed package," covering permission sets, object and field permissions, layouts and tab settings. It needs the package already installed and "Post-installation documentation that the agent can access." That makes your setup instructions part of the product even when an agent does the clicking, so write them as exact steps, not marketing copy.

What a good setup tab checks instead of explains

A package setup screen is often built as welcome text plus instructions. Instructions alone leave the admin to guess whether each step worked. A setup tab that reads the org's state answers that for them, and every check below maps to a documented failure point.

Start with the connection. If the external credential principal isn't authenticated, show the authorize button instead of a paragraph about OAuth. Then check access, meaning your permission set is assigned to the integration user and to at least one business user. If the permission set never installed because the org lacks the matching license, say which license is missing rather than asking the admin to assign it. Next, make one test call to your backend and show what came back. Only when all of that passes should the tab write Setup_Complete__c, so your support team can see which customers are stuck.

The order follows the dependencies. Nothing else can be tested until the account is connected, so connection goes first. A customer who stops at step 2 is a different conversation from one who never authorized, and a status record lets your customer success team tell them apart without a screen share. It is the same goal Appnigma's done-for-you page sets for customer integrations, "Get new customers connected and ready to use your product."

Mistakes that break setup after install

Most failed installs and stuck setups trace back to a short list of package decisions, each documented in Salesforce's packaging guides.

  • A synchronous callout in the post install script. It isn't allowed. Queue the call so it runs after the install commits.
  • A post install script that calls a `with sharing` class. Salesforce warns this can stop the install.
  • No error notification user. A failed script aborts the install, and without a named user you won't see why.
  • Assuming the script ran. It doesn't run in Trialforce trial orgs, so the setup tab must create missing defaults itself.
  • An API key in a public custom setting or plain field. The customer's users can read it, and security review flags it.
  • Expecting the auth provider to arrive with a 2GP package. It has to be recreated in the customer's org.
  • Auto-assigning a brand new feature on a push upgrade. Salesforce's guidance is to let the admin decide who gets it.

Frequently Asked Questions

Does every Salesforce package show a setup page after install?

No. Salesforce shows the install result and your package in Installed Packages. A Configure link (1GP), a post install URL (2GP) and any setup tab exist only if your team adds them.

What is the difference between the Configure link and the post install URL?

The Configure link is a 1GP custom link set in the Configure Custom Link field. The post install URL is a 2GP setting in sfdx-project.json that redirects the admin after install or upgrade and appears on the Installed Packages page.

Can a post install script call our API?

Only asynchronously. The callout runs after the script finishes and the install commits, for example from a Queueable the script enqueues.

Can a post install script assign permission sets?

Yes, and Salesforce publishes a sample for push upgrades. On a first install, admins assign packaged permission sets themselves.

Can Appnigma build and ship the package for us?

Yes. Appnigma's marketplace apps projects cover the managed package, Salesforce security review preparation and responses, and installation guidance for customer admins. The permissions and backend changes the app needs are agreed during scoping.

Sources

  1. Create a First-Generation Managed Package Using a UI
  2. Develop App Documentation, 1GP Developer Guide
  3. Share Release Notes and Post-Install Instructions for Second-Generation Managed Packages
  4. Customize Second-Generation Managed Package Installs and Uninstalls Using Scripts
  5. InstallHandler Interface, Apex Reference
  6. Example of a Post Install Script
  7. Run Apex on Package Install/Upgrade
  8. UninstallHandler Interface, Apex Reference
  9. Storing Sensitive Data, Secure Coding Guide
  10. Protecting Your Intellectual Property, 2GP Developer Guide
  11. Store Sensitive Data Insecurely, ISVforce Guide
  12. Components Available in Second-Generation Managed Packages
  13. Upgrade a Named Credentials Managed Package
  14. Permission Sets and Profile Settings in Packages
  15. Sample Post Install Script for a Push Upgrade
  16. Push Upgrade Best Practices
  17. Salesforce AI Skill for Post-Install Configuration

All Blogs

Bring the customer requirements and your current setup. Get a clear scope and delivery timeline to take back to them.