The CustomPermission metadata type defines a custom security capability or feature toggle that can be assigned to users through PermissionSet or Profile metadata. Introduced in API version 31.0, CustomPermission is the gold-standard architectural pattern for replacing hardcoded $Profile.Name checks across ValidationRule formulas, Flow decisions, ApexClass logic, and LightningComponentBundle UI controls.

In Salesforce DX projects, custom permissions are stored in force-app/main/default/customPermissions/ with the .customPermission-meta.xml suffix.

CustomPermission(Bypass_Order_Validation) PermissionSet(Integration_Bypass_PS) ValidationRule(NOT($Permission.Bypass_Order_Validation)) ApexClass(FeatureManagement.checkPermission) LWC(@salesforce/customPermission/...) FlexiPage(Component Visibility Filter)

Key Metadata Fields

Field Type Required Description
label string Yes Human-readable label shown in Setup and Permission Set editors
description string No Explains what business capability or bypass this permission grants
isLicensed boolean No Indicates whether the permission requires a managed package license (v38.0+)
connectedApp string No Associates the custom permission with a specific ConnectedApp
requiredPermission CustomPermissionDependencyRequired[] No Declares prerequisite <customPermission> dependencies (dependency set to true)

Production-Grade XML Example

Below is a CustomPermission definition (Bypass_Account_Validation.customPermission-meta.xml) used to exempt data migration jobs from validation rules:

XML
<?xml version="1.0" encoding="UTF-8"?>
<CustomPermission xmlns="http://soap.sforce.com/2006/04/metadata">
    <description>Exempts the running user from Account and Contact validation rules during bulk data loads.</description>
    <isLicensed>false</isLicensed>
    <label>Bypass Account Validation</label>
</CustomPermission>

Checking CustomPermission Across the Stack

1. Validation Rules and Flows ($Permission)

Text
AND(
    NOT($Permission.Bypass_Account_Validation),
    ISBLANK(BillingCountry)
)

2. Apex (FeatureManagement.checkPermission)

JAVA
public with sharing class OrderApprovalService {
    public static void approveHighValueOrder(Order ord) {
        // Zero-SOQL, session-cached permission evaluation
        if (!FeatureManagement.checkPermission('Approve_Executive_Discounts')) {
            throw new AuraHandledException('You lack permission to approve executive discounts.');
        }
        ord.Status = 'Approved';
        update as user ord;
    }
}

3. Lightning Web Components (@salesforce/customPermission)

JavaScript
import { LightningElement } from 'lwc';
import hasExecutiveDiscountPerm from '@salesforce/customPermission/Approve_Executive_Discounts';

export default class DiscountApprovalAction extends LightningElement {
    get isApproveButtonDisabled() {
        return !hasExecutiveDiscountPerm;
    }
}

Auditing Custom Permissions with SOQL

Both CustomPermission and SetupEntityAccess are queryable in standard SOQL:

SQL
-- 1. List all Custom Permissions in the org
SELECT Id, DeveloperName, MasterLabel, NamespacePrefix, Description, IsLicensed
FROM CustomPermission
ORDER BY DeveloperName ASC

-- 2. Find every Permission Set that grants a specific Custom Permission
SELECT Id, Parent.Name, Parent.Label, Parent.IsOwnedByProfile, SetupEntityId
FROM SetupEntityAccess
WHERE SetupEntityType = 'CustomPermission'
  AND SetupEntityId IN (
      SELECT Id FROM CustomPermission WHERE DeveloperName = 'Bypass_Account_Validation'
  )

Tip

Testing Custom Permissions in Apex Unit Tests: Instead of querying and assigning profiles in @TestSetup, assign a lightweight Permission Set containing your CustomPermission to a test user via insert new PermissionSetAssignment(AssigneeId = u.Id, PermissionSetId = ps.Id); inside System.runAs(u).

Frequently asked questions

Why should I use CustomPermission instead of checking Profile.Name in Validation Rules or Apex?

Hardcoding Profile.Name breaks when organizations migrate to a Permission Set-led security model or rename profiles. Checking a CustomPermission decouples your code and formulas from specific Profiles and allows granular, time-bound assignment via Permission Sets.

How do I check a CustomPermission in Apex without consuming a SOQL query?

Call FeatureManagement.checkPermission('Permission_API_Name') in Apex. It evaluates the running user's active session permissions in memory and consumes zero SOQL queries.

How do I check a CustomPermission inside a Lightning Web Component?

Import the custom permission from the @salesforce/customPermission/Permission_API_Name module in your LWC JavaScript file. It returns true if the current user has the permission and undefined otherwise.

What does the requiredPermission field on CustomPermission do?

The requiredPermission array defines child or prerequisite CustomPermissions that must also be enabled whenever a parent CustomPermission is assigned in a Permission Set or Profile.

How do I query which Permission Sets grant a specific CustomPermission?

Query SetupEntityAccess where SetupEntityId matches the CustomPermission Id and SetupEntityType equals CustomPermission, joining ParentId to PermissionSet.

Comments

Questions, corrections and tips are welcome. Comments are GitHub Discussions, so you sign in with GitHub.