Create PSL Feature Tiers and License Management CLI with the Dreamhouse App
Permission Set Licenses (PSLs) are a permission type that allow you to grant gradual, gated, and custom feature access to your customers. They give you granularity and control over your permissioning structure. The Dreamhouse Lightning Web Components Sample Application acts as a reference implementation for building Second-Generation Managed Packages (2GP) with PSLs and custom permissions. It covers how to use the Salesforce CLI license management plugin to provision and manage PSL-based feature tiers in scratch orgs.
Use this guide to practice:
- Structuring feature tiers using PSLs.
- Gating features in Apex, LWC, and Flows using custom permissions.
- Installing and configuring the license management CLI plugin.
- Provisioning PSL seats for development and testing.
Dreamhouse is a fully formed sample app that serves as a reference implementation for building apps on the Salesforce platform. Today, we’re using it as a reference for building modular, tiered-based features that you can use in your managed packages and sell on AgentExchange. For more information about the Dreamhouse App, see Quick Start: Explore the Dreamhouse Sample App.
-
Find the source repository: The Go-To-Market app specific version of the Dreamhouse app with PSL support is available at https://github.com/sf-partner-solutions/dreamhouse-psl. The repository is maintained by Salesforce and contains PSL tier gating, custom permissions, and CI integration references. You can use it as a starting point for practicing and creating your own implementation.
-
Review the package structure:
Property Value Namespace dreamhouse Package DreamhouseLWC (single package directory under force-app) Package Type Second-Generation Managed Package (2GP) Scratch Org Feature PartnerLicensingPlatform (enabled in config/project-scratch-def.json)
The Dreamhouse app implements three feature tiers, each driven by a PSL and corresponding custom permissions. Each PSL definition is stored in permissionSetLicenseDefinitions/ metadata. When you assign a PSL to a user, they’re assigned a custom permission and the code checks for the custom permission to gate access to features. You no longer need a separate permission set.
| Tier | PSL Name (for CLI) | Custom Permission | Features Included |
|---|---|---|---|
| Starter | base | ViewProperties | View Properties (list, Property Explorer, Property Finder). Read-only access. |
| Premium | premium | ViewPropertyMap | Everything in the Starter tier + Property Map component. |
| Manage Properties | full | ManageProperties | View Brokers, edit Properties and Brokers, run Create Property flow, Sample Data Import. |
Use these patterns when implementing PSL-based gating in your own packages.
The following patterns show you how to gate features using custom permissions in Apex, LWC, and Flow.
Use FeatureManagement.checkPermission() to gate server-side logic.
Keep in mind:
- Always use the fully qualified API name: namespace__PermissionName.
- Return empty, null, or throw an exception when the permission is missing.
- Apex gating enforces access at the data layer, not just from the UI.
Import the custom permission and use it in a getter with lwc:if.
Keep in mind:
- Use the import syntax: @salesforce/customPermission/PermissionName.
- The import returns a boolean at runtime.
- Use lwc:if or lwc:else for conditional rendering.
- For standard user permissions, use @salesforce/userPermission.
Template (propertyMap.html):
Use a formula resource with $Permission and a decision element.
Key points:
- Use $Permission.namespace__PermissionName in formula resources.
- Place the permission check at the start of the flow.
- Create a clear “No Access” message for the user.
Before you begin, make sure you have the Salesforce CLI installed and access to a Dev Hub org with 2GP capability in your scratch org. For more info, see Select and Enable a Dev Hub Org.
-
Install the plugin.
-
Verify installation.
-
Clone the Repository.
-
Authorize Your Dev Hub.
If you haven’t already done so, authorize your hub org and provide it with an alias. The command uses myhuborg as the example alias.
-
Create a Scratch Org.
Create a scratch org and provide it with an alias. The command below uses dreamhouse as the example alias. The scratch org definition (config/project-scratch-def.json) must have PartnerLicensingPlatform enabled for PSL support.
-
Deploy the Source.
Push the app to your scratch org to deploy it.
-
Assign Permissions.
Assign the dreamhouse permission set to the default user providing full access, or use the sf license provision CLI command to provision PSL-based tiers.
Full access (no PSL, for basic development):
PSL-based tiers (recommended for testing feature gating): Follow the steps in Provision PSL Seats with the sf license provision to create feature gating.
(Optional) Assign the Walkthroughs permission set to the default user: The Walkthroughs permission allows your user to use In-App Guidance Walkthroughs, providing a guided tour of the sample app. The permission set gets auto-created with In-App guidance activation.
-
Import Sample Data.
The Sample Data Import card in the app UI is only visible to users with the ManageProperties custom permission (Manage Properties tier).
-
Open the Org.
Activate the Lightning Lite theme in Setup | Themes and Branding, and open the Dreamhouse app from App Launcher.
-
Use the sf license provision CLI command to provision PSLs within your org.
-
Provision Individual Tiers
-
Starter tier (view Properties only):
-
Premium tier (adds Property Map):
-
Manage Properties tier (full access — edit, create, import):
-
-
Create a JSON file (e.g., provisionPSLs.json):
-
Then run:
The –license flag must match the short name from the PSL definition file names, NOT the human-readable label in the metadata.
| Tier | –license value | Metadata label (do NOT use) |
|---|---|---|
| Starter | base | DreamHouse Base Access |
| Premium | premium | DreamHouse Premium Access |
| Manage Properties | full | DreamHouse Full Access |
Add PSL Provisioning to Your CI Pipeline. After you deploy your source to a scratch org in the CI, and before running tests, add the provisioning step. A base CI script is provided at scripts/ci.sh in the Dreamhouse repository.
Use PSLs with custom permissions in your own packages.
-
Define your feature tiers and decide which features belong to each tier.
-
Create custom permissions, using one permission per gated feature or tier.
-
Create PSL definitions and link each PSL to the appropriate custom permissions.
-
Enable PartnerLicensingAndProvisioningPlatform and add to your scratch org definition.
-
Gate Apex methods using FeatureManagement.checkPermission().
-
Gate LWC components and import @salesforce/customPermission/YourPermission.
-
Gate Flows using $Permission.namespace__PermissionName in decisions.
-
Update CI scripts, adding the sf license provision command after you deploy, but before you run tests.
-
Test each tier and verify users see only their tier’s features.
-
Document for customers by creating clear tier descriptions and provisioning instructions.
- Defense in depth: Gate at both UI (LWC/Flow) and server (Apex) layers.
- Graceful degradation: Show informative “upgrade” messages, not errors.
- Use a single package: You can manage multiple tiers within one 2GP package.
- Namespace consistency: Always prefix custom permissions with your namespace in code references.
If you encounter issues with PSL provisioning or feature gating, check:
- That PartnerLicensingAndProvisioningPlatform is enabled in your scratch org definition.
- That the –license value matches the definition file name, not the label.
- That you’re using the correct namespace prefix in permission checks.
- That the user has been assigned the provisioned PSL in Setup > Users > Permission Set License Assignments.
For CLI plugin issues, file a GitHub issue at: https://github.com/salesforcecli/plugin-license-management/issues