---
schema_version: "1"
id: 66eec92dcece2a831c796d6a2eebe26f0b2728cc1635fa66898322d118af0e37
title: Create PSL Feature Tiers and License Management CLI with the Dreamhouse App
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/platform-pkg-exchange/isvforce/guides/gtm-app-psl-dreamhouse-guide.md
canonical_url: https://developer.salesforce.com/docs/platform/isvforce/guide/gtm-app-psl-dreamhouse-guide.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: ac7dd8876988526c4f527168e3498ed971bf353f5af777e8e0f73850bcf50625
products:
  - ISVforce Guide
  - merged-docs
---

# 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.

## Get Started With the Dreamhouse App

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](https://trailhead.salesforce.com/content/learn/projects/quick-start-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](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) |

## Understand Feature Tier Architecture

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. |

## Gate Features in Your Code

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.

### Pattern 1: Apex Gating

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.

#### Example from Dreamhouse (PropertyController.cls)

```nolang
public static List<Property__c> getPagedPropertyList() {
    if (!FeatureManagement.checkPermission('dreamhouse__ViewProperties')) {
        return null; // User doesn't have the Starter tier
    }
    // ... return property data
}
```

### Pattern 2: LWC Gating

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.

#### Example from Dreamhouse (propertyMap.js)

```nolang
import ViewPropertyMap from '@salesforce/customPermission/ViewPropertyMap';

export default class PropertyMap extends LightningElement {
    get isPremium() {
        return ViewPropertyMap;
    }
}
```

Template (propertyMap.html):

```nolang
<template>
    <template lwc:if={isPremium}>
        <!-- Premium map component -->
    </template>
    <template lwc:else>
        <p>This is a Premium feature. Please upgrade your license.</p>
    </template>
</template>
```

### Pattern 3: Flow Gating

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.

#### Example from Dreamhouse (Create_property.flow-meta.xml)

```nolang
<!-- Formula resource: evaluates the custom permission -->
<formulas>
    <name>user_has_manage_properties</name>
    <dataType>Boolean</dataType>
    <expression>$Permission.dreamhouse__ManageProperties</expression>
</formulas>

<!-- Decision element: first step in the flow -->
<decisions>
    <name>Check_Manage_Properties</name>
    <label>Check Manage Properties Permission</label>
    <defaultConnector>
        <targetReference>no_access</targetReference>
    </defaultConnector>
    <defaultConnectorLabel>No Access</defaultConnectorLabel>
    <rules>
        <name>Manage_Properties_Access_Rule</name>
        <conditions>
            <leftValueReference>user_has_manage_properties</leftValueReference>
            <operator>EqualTo</operator>
            <rightValue>
                <booleanValue>true</booleanValue>
            </rightValue>
        </conditions>
        <connector>
            <targetReference>new_property</targetReference>
        </connector>
        <label>Has Access</label>
    </rules>
</decisions>
```

## Install the License Management CLI Plugin

Before you begin, make sure you have the [Salesforce CLI](https://developer.salesforce.com/tools/salesforcecli) 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](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_setup_enable_devhub.htm).

1.  Install the plugin.

    ```nolang
    sf plugins install @salesforce/plugin-license-management
    ```

2.  Verify installation.

    ```nolang
    sf license provision --help
    ```

## Set Up Dreamhouse for Development

1.  Clone the Repository.

    ```nolang
    git clone https://github.com/sf-partner-solutions/dreamhouse-psl
    cd dreamhouse-psl
    ```

2.  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.

    ```nolang
    sf org login web -d -a myhuborg
    ```

3.  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.

    ```nolang
    sf org create scratch -d -f config/project-scratch-def.json -a dreamhouse
    ```

4.  Deploy the Source.

    Push the app to your scratch org to deploy it.

    ```nolang
    sf project deploy start
    ```

5.  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):

    ```nolang
    sf org assign permset -n dreamhouse
    ```

    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.

    ```nolang
    sf org assign permset -n Walkthroughs
    ```

6.  Import Sample Data.

    **note:** The Sample Data Import card in the app UI is only visible to users with the ManageProperties custom permission (Manage Properties tier).

    ```nolang
    sf data tree import -p data/sample-data-plan.json
    ```

7.  Open the Org.

    ```nolang
    sf org open
    ```

    Activate the Lightning Lite theme in **Setup** | **Themes and Branding**, and open the Dreamhouse app from App Launcher.

### Provision PSL Seats with the sf license provision

1.  Use the sf license provision CLI command to provision PSLs within your org.

    ```nolang
    sf license provision -o <target-org> [-n namespace] [-l license] [-q quantity] [-s start-date] [-e end-date] [-f definition-file]
    ```

2.  Provision Individual Tiers
    1.  Starter tier (view Properties only):

        ```nolang
        sf license provision --target-org dreamhouse --namespace dreamhouse --license base --quantity 1
        ```

    2.  Premium tier (adds Property Map):

        ```nolang
        sf license provision --target-org dreamhouse --namespace dreamhouse --license premium --quantity 1
        ```

    3.  Manage Properties tier (full access — edit, create, import):

        ```nolang
        sf license provision --target-org dreamhouse --namespace dreamhouse --license full --quantity 1
        ```

### Bulk Provision Licenses with a Definition File

1.  Create a JSON file (e.g., provisionPSLs.json):

    ```nolang
    [
      {
        "namespace": "dreamhouse",
        "license": "base",
        "quantity": 5
      },
      {
        "namespace": "dreamhouse",
        "license": "premium",
        "quantity": 3
      },
      {
        "namespace": "dreamhouse",
        "license": "full",
        "quantity": 1
      }
    ]
    ```

2.  Then run:

    ```nolang
    sf license provision --target-org dreamhouse --definition-file ./provisionPSLs.json
    ```

### Use --license Values

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      |

## Use the CI/CD Integration

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.

### Example

```nolang
# 1. Create scratch org
sf org create scratch -d -f config/project-scratch-def.json -a ci-org

# 2. Deploy source
sf project deploy start -o ci-org

# 3. Provision PSLs (before tests!)
sf license provision -o ci-org -n dreamhouse -l full -q 1

# 4. Assign permission to test user (if needed)
sf org assign permset -n dreamhouse -o ci-org

# 5. Run tests
sf apex run test -o ci-org --wait 10
```

## Create Your Own Package

Use PSLs with custom permissions in your own packages.

1.  Define your feature tiers and decide which features belong to each tier.

2.  Create custom permissions, using one permission per gated feature or tier.

3.  Create PSL definitions and link each PSL to the appropriate custom permissions.

4.  Enable PartnerLicensingAndProvisioningPlatform and add to your scratch org definition.

5.  Gate Apex methods using FeatureManagement.checkPermission().

6.  Gate LWC components and import @salesforce/customPermission/YourPermission.

7.  Gate Flows using $Permission.namespace\_\_PermissionName in decisions.

8.  Update CI scripts, adding the sf license provision command after you deploy, but before you run tests.

9.  Test each tier and verify users see only their tier's features.

10. Document for customers by creating clear tier descriptions and provisioning instructions.

## Use the Recommended Best Practices

- **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.

## Troubleshoot Your Implementation

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](https://github.com/salesforcecli/plugin-license-management/issues)
