# developer Sales Cloud - Full Documentation # API Access for the Salesforce Spiff Commission Estimator The Salesforce Spiff Commission Estimator managed package provides a widget in Salesforce for estimating potential commission amounts on opportunities and quotes. This managed package requires OAuth authentication with a Client ID and Client Secret that you generate from the **Admin » Settings » API Access Management** page. See [Set Up the Salesforce Spiff Commission Estimator](https://help.salesforce.com/s/articleView?id=sales.spiff_estimator_setup.htm&type=5) in _Salesforce Help_. # Assign Users to Programs with a Schedulable Apex Class Create a schedulable Apex class that bulk-assigns users to Enablement programs. | User Permissions Needed | | | ------------------------------- | ------------------------------------------------------------------------------------- | | To create Apex classes: | Author Apex | | To assign or unassign programs: | Design and Deliver Enablement Programs OR Design and Deliver Enablement Lite Programs | **note:** You can’t add Apex classes directly in production orgs. Only Developer Edition, trial, and sandbox orgs support direct Apex editing in Setup. To deploy Apex code in a production org, add it to a sandbox first, test it, and then promote it to production. The Apex class calls the [assignEnablementProgram](https://developer.salesforce.com/docs/atlas.en-us.api_action.meta/api_action/actions_obj_assign_enablement_program.htm) invocable action. 1. From the Setup menu, select **Developer Console**. 2. From the File menu, select **New | Apex Class**. 3. Enter a name for the Apex Class. 4. Click **OK**. 5. In the Developer Console, replace the code that you see with this Apex class, then save your work. ```apex global class ScheduleAssignments implements Schedulable { global void execute(SchedulableContext SC) { String enablementAdminToNotify = [SELECT AssigneeId FROM PermissionSetAssignment WHERE PermissionSet.Name = 'EnablementAdmin' LIMIT 1].AssigneeId; String programId = [SELECT Id FROM EnablementProgram WHERE Name = 'Test Program' LIMIT 1].Id; List allAssignees = [SELECT AssigneeId FROM PermissionSetAssignment WHERE PermissionSet.Name = 'EnablementUser']; Invocable.Action action = null; for (PermissionSetAssignment assignee : allAssignees) { if (action == null) { action = Invocable.Action.createStandardAction('assignEnablementProgram'); } else { action.addInvocation(); } action.setInvocationParameter('assigneeId', String.valueOf(assignee.AssigneeId)); action.setInvocationParameter('startDate', Date.today() + 1); action.setInvocationParameter('notificationUserId', enablementAdminToNotify); action.setInvocationParameter('programId', programId); } if (action != null) { List results = action.invoke(); for (Invocable.Action.Result result : results) { if (!result.isSuccess()) { for (Invocable.Action.Error error : result.getErrors()) { System.debug('Error: ' + error.getMessage()); } } } } } } ``` 6. To implement this schedulable class, execute this Apex code from Developer Console. ```apex ScheduleAssignments assignerJob = new ScheduleAssignments(); // Schedules for 08:30:20 AM on Feb 10th String scheduledTime = '20 30 8 10 2 ?'; String jobID = System.schedule('Enablement Assign Job', scheduledTime, assignerJob); ``` The `assignEnablementProgram` invocable action counts against your org’s [Bulk API 2.0 limits](https://developer.salesforce.com/docs/atlas.en-us.salesforce_app_limits_cheatsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platform_bulkapi.htm). To help accommodate these limits, this example shows a bulkified request. **See Also** - [_Salesforce Help_: Considerations and Limitations for Enrolling Users in Enablement Programs](https://help.salesforce.com/s/articleView?id=sales.enablement_assignments_guidelines.htm&type=5) - [_Salesforce Developers_: Apex Scheduler](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_scheduler.htm) - [_Trailhead_: Asynchronous Apex](https://trailhead.salesforce.com/content/learn/modules/asynchronous_apex) # Assign Users to Programs with REST API Create a REST API POST request that uses the assignEnablementPrograms invocable action for assigning Enablement programs | User Permissions Needed | | | ------------------------------- | ------------------------------------------------------------------------------------- | | To assign or unassign programs: | Design and Deliver Enablement Programs OR Design and Deliver Enablement Lite Programs | 1. In a REST API POST request, use the [assignEnablementProgram](https://developer.salesforce.com/docs/atlas.en-us.api_action.meta/api_action/actions_obj_assign_enablement_program.htm) invocable action. ``` /services/data/v58.0/actions/standard/assignEnablementProgram ``` 2. In the request, include the `assigneeId`, `programId`, `notificationUserId`, and `startDate` parameters. ```json { "inputs": [ { "assigneeId": "005xx12345", "programId": "0Qoxx123456", "notificationUserId": "005xx67890", "startDate": "2023-03-24" }, { "assigneeId": "005xx12346", "programId": "0Qoxx123456", "notificationUserId": "005xx67890", "startDate": "2023-03-24" }, { "assigneeId": "005xx12347", "programId": "0Qoxx123456", "notificationUserId": "005xx67890", "startDate": "2023-03-24" } ] } ``` The `assignEnablementProgram` invocable action counts against your org’s [Bulk API 2.0 limits](https://developer.salesforce.com/docs/atlas.en-us.salesforce_app_limits_cheatsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platform_bulkapi.htm). To help accommodate these limits, this example shows a bulkified request. **See Also** - [_Salesforce Help_: Considerations and Limitations for Enrolling Users in Enablement Programs](https://help.salesforce.com/s/articleView?id=sales.enablement_assignments_guidelines.htm&type=5) # Automate Program Assignment If your company plans to assign programs to a large number of users, you can work around the limitations of the default batch assignment window. Enablement provides an invocable action that you can call from Apex or REST API to automate the assignment workflow. **important:** Ensure that users have the required permission for taking the type of program you’re assigning. Otherwise, the assignment fails. See [Enablement Permission Set Groups, Permission Sets, and Permissions](https://help.salesforce.com/s/articleView?id=sales.enablement_permissions.htm&type=5) and [Assign Permissions to Your Enablement Team](https://help.salesforce.com/s/articleView?id=sales.enablement_setup_assign_perms.htm&type=5). # Cadence Data Model Cadence Builder lets you create and update cadences. When a manager or sales rep adds a target to a cadence, an Action Cadence Tracker keeps track of where the target is in the cadence and the target’s history of completed cadence steps. The other cadence objects store data used by Cadence Builder and the trackers. This section covers: - [Cadence Objects](https://developer.salesforce.com/docs/sales/sales-engagement/guide/sales-cadence-objects.md): Describes the family of cadence objects. - [Object Relationships](https://developer.salesforce.com/docs/sales/sales-engagement/guide/sales-cadence-object-relationships.md): Describes how the objects are related to each other and how objects fields are used. - [Object Reference](https://developer.salesforce.com/docs/sales/sales-engagement/guide/sales-cadence-object-reference.md): Contains links to the reference documentation of the cadence objects. # Cadence Object Reference Cadence Builder and cadence trackers use these standard objects. - [ActionCadence](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_actioncadence.htm) - [ActionCadenceRule](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_actioncadencerule.htm) - [ActionCadenceRuleCondition](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_actioncadencerulecondition.htm) - [ActionCadenceStep](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_actioncadencestep.htm) - [ActionCadenceStepTracker](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_actioncadencesteptracker.htm) - [ActionCadenceStepVariant](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_actioncadencestepvariant.htm) - [ActionCadenceTracker](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_actioncadencetracker.htm) # Cadence Object Relationships This diagram shows the relationships of Cadence Builder and tracker objects and their fields. For high-level information on the objects, see [Cadence Objects](https://developer.salesforce.com/docs/sales/sales-engagement/guide/sales-cadence-objects.md). This section discusses the fields important to describing the action cadence graph and the tracking of active targets. For the full reference documentation, see [Cadence Object Reference](https://developer.salesforce.com/docs/sales/sales-engagement/guide/sales-cadence-object-reference.md). These objects are read-only. You can use SOQL to query the object fields to inspect action cadences and create reports. **note:** We refer to many of the objects as Cadence objects, but their API names start with ActionCadence. ![Cadence object relationship diagram](https://developer.salesforce.com/docs/media/sales-engagement/object-relationships-detailed.png) ## Builder Objects The following fields relate the builder objects to one another to construct the cadence graph and define the builder operations. All builder objects relate to each other through foreign keys except the ActionCadenceStep to ActionCadenceStep record relationship. ActionCadenceStep records within an action cadence relate to each other through the StepName and ParentStepName string fields. To learn more about what happens when you modify a cadence while it’s active, see [Cadence Builder](https://developer.salesforce.com/docs/sales/sales-engagement/guide/sales-cadence-builder.md). ### ActionCadence - `Name`: The name of the action cadence that you see in the UI. - `Description`: The description of the action cadence. - `State`: The state of the action cadence, which can be `Active`, `Deleting`, `Draft`, `Error`, or `Inactive`. - `FolderId` and `FolderName`: The folder ID and name that contains the action cadence. - `ActivatedDate`: The date the action cadence was activated. Until the action cadence is activated, it is in `Draft` state. - `LastEditedDateTime`: If an action cadence is edited after activation, this field is updated. ### ActionCadenceStep - `ActionCadenceId`: The ID of the action cadence this step belongs to. - `RootStepId`: The ID of the `Root` step of this action cadence. - `Type`: The step type. These values are allowed. - `Root`: The first step of the action cadence. There is always one `Root` step. This appears as the Start step in Cadence Builder. - `AutoSendAnEmail`, `SendAnEmail`, and `MakeACall`: An action step to send an automated email, send an email manually, or make a call. You can use an email or call `TemplateId` with this step. - `CreateTask`: A custom step. It has only two fields, the `StepTitle` and optional `StepComments`. No special rules apply. - `Wait`: A wait step creates a delay between action steps. This step type requires a `WaitTimeInSeconds` value. - `Branch`: A branch step following with a call. ActionCadenceRule and ActionCadenceRuleCondition records define the rule and rule conditions. The `BranchDefaultStepName` is the next step if the result is false. The `OutcomeNextStepName` of the ActionCadenceRule record is the next step if the result is true. - `ListenerBranch`: A branch step following with emails. It tracks what happens to the sent email. This step requires the same related objects and fields as the `Branch` step to define the rule, rule condition, and next steps. - `StepName`: The unique identifier for the step. If you use Cadence Builder, it is a GUID generated by Salesforce. It doesn’t appear in the UI. - `StepTitle`: The step name that you see in Cadence Builder. - `StepComments`: The step description that you see in Cadence Builder. - `ParentStepName`: The `StepName` of the previous step in the action cadence. This field is required unless the step type is `Root` or the step state is `Retired` or `Orphaned`. - `TemplateId`: The ID of a call script or email template if you want to use one with a call or email step. - `WaitTimeInSeconds`: The duration in seconds for this wait step. Required if the step type is `Wait`, unless the action cadence state is `Draft`. - `BranchDefaultStepName`: Required if the step type is `Branch` or `ListenerBranch`. This step is the first step of the branch that’s taken when the rule condition evaluates as false. - `ChainedCadenceId`: Required if the step type is `DaisyChain`. When a target is on the last step of a path in the current action cadence, this is the ID of the action cadence the target goes to next. - `IsThreaded`: This field is valid for step type `SendAnEmail`. If true, the email for this email step is sent as a reply to the email conversation from the previous email step. By sending the email as a reply to a previous email, targets and users see a conversation view of the emails. This field can’t be true for the first email step in a cadence, because the first email from a cadence must start a new conversation with the target. - `HasVariant`: This field is valid for step types `AutoSendAnEmail`, `SendAnEmail`, and `MakeACall`. If true, the step has email or call template variants. The template variants are defined in ActionCadenceStepVariant records. - `GraphState`: The step state in the graph. These values are allowed. - `Included`: The step is inside the graph. If you add a new step, the state is automatically set to `Included`. - `Pending`: This step has been created but hasn’t been added to the step graph. Pending steps can be added to the step graph at a later time. - `Orphaned`: A step that is deleted while the ActionCadence record is in `Draft` state. When you delete a step in Cadence Builder before it is activated, the system removes it from Cadence Builder, sets the GraphState to `Orphan`, and nulls the `ParentStepName` value. The step still appears in the database until it is deleted asynchronously upon action cadence activation. After a step is `Orphaned`, you can’t add it back to the action cadence. We recommend avoiding building dependencies on orphaned steps. - `Retired`: A step that is deleted from graph after the cadence is activated. This step is not deleted from the database. After a cadence is active targets can be on the step and historical data (in ActionCadenceStepTracker records) can be associated with this step. When a step is `Retired`, you can’t undo it. For more information on what happens to retired steps, see [Cadence Builder](https://developer.salesforce.com/docs/sales/sales-engagement/guide/sales-cadence-builder.md). - `GoToStepName`: If targets exist on this step and the step is deleted, this field tells the action cadence where to send the targets next when they complete the deleted step. If this field is blank, the target exits the cadence when they complete the step. The rules for this field are: - It can’t point to the `Root` step. - If the destination is a `Branch` step, the retired step must be a `MakeACall` step. - If the destination is a `ListenerBranch` step, the retired step must be an `AutoSendAnEmail` or `SendAnEmail` step. - The destination and retired steps can’t both be `Wait` steps. The system doesn't allow two `Wait` steps in a row. - The destination and retired steps can’t both `AutoSendAnEmail` steps. ### ActionCadenceStepVariant - `ActionCadenceStepId`: The ID of the step with `HasVariant` set to true. - `TemplateId`: The call script or email template ID of the variant. - `SplitPercentage`: The percentage of targets that we expect to use this variant. The total percentage of variants must add up to 100%. - `Type`: Duplicate of the step type associated with this variant. ### ActionCadenceRule - `ActionCadenceStepId`: The ID of the associated step with step type `Branch` or `ListenerBranch`. - `ConditionLogic`: The logical operator used to evaluate the rule conditions. The only value is `AND`. If this rule has several conditions, all must be true for this step to be true. - `OutcomeNextStepName`: The `StepName` of the step that the target we expect to go to if the result is true. - `RuleName`: The name given to the rule. Every rule in an action cadence must have a unique name. This name is not visible in the UI. - `ParentRuleName`: The value of the `RuleName` field of the previous rule in the cadence. Must contain a valid rule name value unless this rule is the root rule with `RootStep` type, in which case it is `null`. - `RuleType`: The type of step that this rule applies to. Possible values are: - `BranchStep`: The rule evaluates the condition of a `Branch` or `ListenerBranch` step type of an ActionCadenceStep record. - `RootStep`: The rule evaluates a global exit condition defined by `GlobalEventType`. - `GlobalEventType`: Required if the rule type is `RootStep`. If the action cadence rule contains a global exit condition, this field contains the type of event that the rule represents. A global exit condition can be, for example, exit on email hard bounce, exit on email soft bounce, or caller not interested. In these cases, the target exits the action cadence immediately. You can find the global exit condition types in our object references. - `GraphState`: The state of the rule. The options are the same as for ActionCadenceStep: `Included`, `Orphaned`, `Pending`, and `Retired`. ### ActionCadenceStepRuleCondition - `ActionCadenceRuleId`: The ID of the ActionCadenceRule that this condition is associated with. - `RuleConditionName`: The name of the rule condition. Every rule condition in an action cadence must have a unique name. This name isn’t visible in the UI. - `Resource`: The field to evaluate. Possible values are: - `CallDispositionCategory`: Used by the `Branch` step type. - `EmailEngagement`: Used by the `ListenerBranch` step type. - `Operator`: The conditional operator for this rule. The only value is `Equal`. - `Value`: The event that your cadence rule condition listens for to decide when the event is complete. Examples are `EmailOpen`, `EmailLinkClick`, and `CallNotInterested`. ## Runtime Objects These objects and their fields track targets as they move through active action cadences. The tracker and step tracker records are the runtime version of a target. The tracker and step tracker records are kept in the database to keep a history of targets and the steps they’ve completed. ### ActionCadenceTracker - `ActionCadenceId`: The ID of the related action cadence. - `TargetId`: The ID of the lead, contact, or person account assigned to the action cadence target. - `CurrentStepId`: The ID of the current ActionCadenceStepTracker record for the step the target is on. - `State`: The state of the tracker. - `CompletionReason`: The reason that the target completed the action cadence. - `LastCompletedStepId`: The ID of the last completed ActionCadenceStepTracker record. - `ExitGlobalRuleId`: If the target exits because of a global exit condition, this field is the ID of the ActionCadenceRule record with the global exit rule. One example of a global exit condition is an email bounced because of an invalid email address. - Fields related to opportunities: - `RelatedToId`: The ID of the opportunity related to the target, if one exists. - `RelatedToAttributionType`: This field defines when the cadence gets credit for the opportunity. You can attribute the opportunity to the cadence when the opportunity is created or only when the opportunity stage advances. - `CompletionDisposition`: The opportunity state on completion of the cadence. Sales reps can also set this value when removing a target from an action cadence. - `ScheduledResumeDateTime`: The time when the action cadence is going to resume when it is paused or on a wait step. - `IsTrackerActive`: Indicates whether the action cadence target is active or not. If the state is `Running`, `Paused`, `Processing`, or `Initializing`, an action cadence target is active. Only active targets count against the limit of 150,000 trackers. - `DaisyChainIteration`: The number of this action cadence in a sequence of linked action cadences followed by this target. This value starts at 1 with the initial action cadence. A target can follow a sequence of up to 10 linked action cadences. ### ActionCadenceStepTracker - `ActionCadenceTrackerId`: The ID of the related tracker record. - `ActionCadenceStepId`: ActionCadenceStepTracker is the runtime version of an ActionCadenceStep. This field contains the ID of the related ActionCadenceStep. - `TargetId`: The ID of the lead, contact, or person account assigned to the cadence. - `State`: The current state of this step. - `CompletionReason` and `CompletionDate`: The reason and the date that the target completed the cadence. A step is completed when the action is taken or the step is skipped by a sales rep. - `StepType` and `StepTitle`: The `Type` and `StepTitle` of the related ActionCadenceStep. - `ActionCadenceId` and `ActionCadenceName`: The associated ID and name of the related action cadence. - `ActionTakenDateTime`: The date and time that the action described in this step was taken. If the action is scheduled, for example, send an email in 2 hours, this is the date and time the task is scheduled and not when the task is carried out. This field is useful for users and managers to see what steps are overdue. - `DueDateTime` and `SecondsOverdue`: When a scheduled task is due and by how much it’s overdue. - `IsActionTaken`: Indicates whether somebody has taken action on this step tracker, true if the sales rep completed an action during this step, such as making a phone call. Otherwise, the value is false. - `CompletedById`: The user ID of the sales rep who completed this step for the target. A target can be assigned to a different user, such as yourself, another Sales Engagement user, or another target owner, before a step is completed. - `WasEverPaused`: If you pause a tracker, the step tracker is paused. Users can use the pause functionality to prevent overdue steps. Managers can query this field to see whether any users have excessive numbers of paused step trackers. # Cadence Objects The cadence objects operate as a family to build a cadence and track the targets' progress through the cadence steps. This section describes how these objects relate to each other. The objects fall into three categories. **Cadence Builder objects** are the building blocks of a cadence. **Cadence runtime objects** track active sales targets and their progress through the cadence steps. **CRM data objects** are the targets sales managers or reps add to cadences. Targets can be leads, contacts, or person accounts. You can also associate an opportunity with active cadence targets. **note:** We refer to many of the objects as Cadence objects, but their API names start with ActionCadence. ![High-level overview of cadence object relationships](https://developer.salesforce.com/docs/media/sales-engagement/object-relationships-high-level.png) ## Cadence Graph Because each cadence is a structure composed of multiple objects, records, and relationships, you can think of it as a graph. The graph represents both the set of records that make up a cadence and the specific relations between those records. In Cadence Builder, the relationships of the builder objects (the graph) are represented visually. Unless otherwise stated, when we talk about the graph, you can think of the visual graph shown in Cadence Builder. Next, let’s describe the high-level relationships between Cadence Builder and the runtime objects. The [Object Relationships](https://developer.salesforce.com/docs/sales/sales-engagement/guide/sales-cadence-object-relationships.md) section dives deeper and describes the objects and their relationships at the object field level. ## Objects That Define a Cadence Builder objects are created and modified when you use Cadence Builder. A cadence has one top-level ActionCadence record. The cadence graph is made up of ActionCadenceStep records. The objects that flow from the ActionCadenceStep define the operation of the step. ### ActionCadence An ActionCadence record holds basic information, such as the action cadence `Name`, `Description`, and the `FolderId` of the folder that contains it. The ActionCadence record doesn’t define the graph. ### ActionCadenceStep An ActionCadenceStep record defines a step within an action cadence. You can determine the action of a step, such as an email or call, by its `type`. The first step in a cadence has the `Root` type. The builder generates this step automatically. Subsequent steps in the action cadence are related to each other not through foreign keys but through string fields called `StepName` and `ParentStepName`. Each step has a unique name within a cadence and the previous step is referenced by the `ParentStepName` of the step. To create an action cadence step that sends an email or makes a call, use the `AutoSendAnEmail`, `SendAnEmail`, or `MakeACall` step type. You can attach a template or have the builder choose a template from step variants defined with ActionCadenceStepVariants records. After an email or call step, you can create a branch in a cadence. Use branches to take targets through different cadence steps depending on the results of the call or email. Create a branch by inserting a `ListenerBranch` step after an email step or `Branch` step after a call step. Branches are defined through rules and rule conditions with ActionCadenceRule and ActionCadenceRuleCondition records. You can use custom steps to add specific actions other than calls, emails, or LinkedIn steps to an action cadence. To create a custom step, use the `CreateTask` step type and include instructions for the sales rep in the `StepComments` field. To include wait time between action steps, use the `Wait` step type. Wait steps don’t link to any other objects. When you’re at the end of the step sequence, the target can exit the action cadence, or you can send the target to another action cadence (daisy-chaining). To link targets to a subsequent action cadence, use the `DaisyChain` step type and specify the next action cadence in the `ChainedCadenceID` field. ### ActionCadenceRule and ActionCadenceRuleCondition An action cadence rule can be related to an email or call branch step or a global exit condition. The rule evaluates the related rule condition on an email or call step outcome and determines which branch of the action cadence the target follows when the result is true. The rule condition is made up of a resource, operator, and value. For example, `email equals opened`. Use a global exit condition to automatically remove targets from the action cadence before the last step. For example, exit on email hard bounce, exit on email soft bounce, or caller not interested. When a global exit condition evaluates to true, the target exits the cadence immediately. ### ActionCadenceStepVariant A step variant allows you to experiment with a single email or call step to test multiple email templates or call scripts to discover which yields better responses. An ActionCadenceStepVariant record has a `TemplateId` and `SplitPercentage`. The percentages of all variants associated with a step must add up to 100%. So you can have, for example, 60%, 20%, and 20% of targets going to templates A, B, and C respectively. At run time, when a target is added to an action cadence, the target is assigned a variant randomly, based on the split percentages. If a target goes through the same action cadence again, the same variant is assigned. This behavior is useful when you want the cadence target to have the same experience, for example, during quarterly check-ins, when looping over multiple action cadences through daisy chains, or when the target is exited from an action cadence and has to be restarted. During run time, an invocable action retrieves the original template ID for the target. ## Objects That Track Targets’ Progress at Runtime ActionCadenceTracker and ActionCadenceStepTracker track active targets and their action cadence steps. The tracker and step tracker are the runtime versions of a target in an action cadence. They track the status of active targets moving through active action cadences. The history of tracker and step tracker records is stored in the database after the target completes the action cadence. # Considerations for Packaging Enablement Programs and Dependencies Before you create a second-generation managed package for Enablement programs, plan your strategy for including Enablement measures and exercise content. Support users who install your package by understanding the components you can upgrade. ## Measures in Packaged Programs You can include Enablement measures in the same package as the program that contains the outcome or milestones that reference the measures. Or you can package measures separately. You can package Enablement measures that have a status of Draft or Published (Active). Including prebuilt measures in a package results in a package-deployment error. If you plan to package a program that references [prebuilt Enablement measures](https://help.salesforce.com/s/articleView?id=sales.enablement_measures_overview.htm&type=5), first clone the prebuilt measure. Reference the cloned measure in your program, and then package the program and the cloned measure. ## Digital Experiences Content for Packaged Programs Take program exercise content from the Enablement workspace in the Digital Experiences app and include it in the same package as the program. When you package the workspace and its content, specify a new name and API name for the workspace that you install in the destination org. Next, update the program's references to the packaged content. See [Modify the Retrieved Metadata for Digital Experiences Content](https://developer.salesforce.com/docs/sales/enablement/guide/enablement-package.md#3-modify-the-retrieved-metadata-for-digital-experiences-content). **tip:** Because only content with a status of Published can be used in a program, all content included in the package is set to Published when the package is installed. This is different behavior from when you package Digital Experiences content outside the Enablement workspace, where you can package and deploy content of other statuses. When you retrieve content to include in a package, take care to retrieve only the content from the Enablement workspace. ## Assessment Surveys for Packaged Programs If a packaged program contains a Feedback Request exercise that uses peer and manager feedback, include the assessment survey in the same package as the program. **important:** For packaging assessment surveys, use the Flow metadata type. Use the FlowDefinition metadata type only if you’re deploying programs with change sets. You can’t package Feedback Request exercises that use Einstein Coach. Including the default assessment survey, Discovery Call Assessment, in a package results in a package-deployment error. If you plan to package a program that references the default assessment survey, first clone the assessment survey. Reference the cloned assessment survey in your program and then create and deploy a package for the program along with the cloned assessment survey and other dependencies. ## Custom Exercise Types in Packaged Programs If a packaged program includes custom exercises, make sure that all the associated components are included in the package. See [Retrieve Metadata for Custom Exercise Types](https://developer.salesforce.com/docs/sales/enablement/guide/enablement-package.md#retrieve-metadata-for-custom-exercise-types). ## Upgrading Packaged Programs and Dependencies In a new version of the package, you can upgrade program dependencies, including measures, Digital Experiences, and custom exercise types. But programs are subscriber-controlled, so if users have already installed a program from a package, they can’t receive updates to the program by installing a new package version. Instead, we recommend that you clone a program that you previously packaged and create a version of the package that includes the cloned program. If you included a custom exercise type in a previous version of a packaged program and then you remove the custom exercise in a new version of the package, package creation fails. To continue, submit a Salesforce Customer Support case and request permission to create a package by deleting managed components. **See Also** - _Salesforce Help_: [Building Enablement Measures](https://help.salesforce.com/s/articleView?id=sales.enablement_measures_create_parent.htm&type=5) - _Salesforce Help_: [Managing Digital Experiences Content for Exercises in Enablement Programs](https://help.salesforce.com/s/articleView?id=sales.enablement_content.htm&type=5) - _Salesforce Help_: [Managing Assessment Surveys for Feedback Request Exercises](https://help.salesforce.com/s/articleView?id=sales.enablement_feedback_request_parent.htm&type=5) # Create a Custom Exercise Type That Uses a Screen Flow This implementation example demonstrates how to create a custom exercise type that uses an active screen flow. After creating this custom exercise type, an Enablement admin can search for a screen flow to use with the exercise in Program Builder. When users take the program in the Guidance Center, they see the screen flow when they complete the exercise. To follow the code in this custom exercise implementation example, download the [enablement_custom_exercise_screen_flow.zip](https://resources.docs.salesforce.com/rel1/doc/en-us/static/misc/enablement_custom_exercise_screen_flow.zip) file. ## Set Up the Objects for the Custom Exercise Type To store the records associated with the screen flows, create a custom object. Connect the screen flow records to the [LearningItem object](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_learningitem.htm). You can find the code examples in the `objects` folder of the downloaded files. 1. Create a custom object called `ScreenFlow_Object__c`. See [Create a Custom Object](https://help.salesforce.com/s/articleView?id=platform.dev_objectcreate_task_parent.htm&type=5) in _Salesforce Help_ or [Custom Objects](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_custom_objects.htm) in the _Object Reference for the Salesforce Platform_. ```xml ScreenFlow_Field__c SetNull ScreenFlow_Object__c Learning Learning false Lookup ``` 2. On the standard object LearningItem, create a custom field called `ScreenFlow_Field__c` that references the `ScreenFlow_Object__c` object, as shown in this example. See [Create Custom Fields](https://help.salesforce.com/s/articleView?id=platform.adding_fields.htm&type=5) in _Salesforce Help_ and [Custom Fields](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/custom_fields.htm) in the _Object Reference for the Salesforce Platform_. ```xml ScreenFlow_Field__c SetNull ScreenFlow_Object__c Learning Learning false Lookup ``` ## Create a Subcategory for the Custom Exercise Type To configure how a custom exercise appears when Enablement admins edit a program or when users take the program in the Guidance Center, define a corresponding category and subcategory for each custom exercise type. For the `category` property, assign the value `Exercise`. The `subcategory` property contains the name and icon of the custom exercise type in Program Builder and the Guidance Center. ![Custom items in the Enablement Programs Builder](https://developer.salesforce.com/docs/media/enablement/enablement-program-custom-items.png '{"class": "image-xl"}') Create a Screen Flow subcategory by using Metadata API. See [Quick Start: Metadata API](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_quickstart_retrieve.htm) in the _Metadata API Developer Guide_. You can find the code examples in the `learningItemTypes` and `enblProgramTaskSubCategories` folders of the downloaded files. 1. Create a [LearningItemType metadata type](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_learningitemtype.htm) record that represents the custom exercise type in the Guidance Center. This definition references the custom object and the custom field on the LearningItem object that you created in [Set Up the Objects for the Custom Exercise Type](#set-up-the-objects-for-the-custom-exercise-type). ```xml ScreenFlowEvaluationHandler ScreenFlowSerializerDeserializer ScreenFlow_Field__c ScreenFlow_Object__c ScreenFlowLearningItemType standard:flow screenFlowViewer Screen Flow Learning Item Type ``` Specify these properties: - `` - : Specifies an Apex class that defines how progress and completion of the custom exercise are assessed when users take the program in the Guidance Center. See [Track a User's Progress in a Custom Exercise](https://developer.salesforce.com/docs/sales/enablement/guide/enablement-custom-exercise-track-progress.md). - `` - : Specifies an Apex class that defines how data related to the custom exercise type is retrieved and deployed with change sets or managed packages. - `` - : Specifies the icon to use for the custom exercise type. Use the format _iconType_:_iconName_, where the values correspond to icon types and names from the [Salesforce Lightning Design System](https://core-main.lightningdesignsystem.com/icons/). This example uses the Standard type Flow icon, so this value is `standard:flow`. 2. Create an [EnblProgramTaskSubCategory metadata type](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_enblprogramtasksubcategory.htm) record that represents the custom exercise type in Program Builder. This definition references the LearningItemType metadata record that you created. ```xml ScreenFlowTaskSubCategory standard:flow ScreenFlowLearningItemType Screen Flow ``` ## Customize the Property Editor for the Custom Exercise Type When an Enablement admin adds the custom exercise type to a program in Program Builder, they search for a specific content record to use for that instance of the exercise. To specify how Enablement admins find the appropriate content for the exercise, customize the exercise’s property editor. This example implements a search box for finding screen flow records and a dropdown to select a record. ![Property editor of custom exercise in the Enablement Programs Builder](https://developer.salesforce.com/docs/media/enablement/enablement-program-custom-property-editor.png '{"class": "image-xl"}') To customize the property editor, create an LWC and add it to the Program Builder’s property sheet of the custom learning item. You can find the code examples in the `lwc` folder of the downloaded files. 1. Create an LWC bundle. Let's name our example bundle `screenFlowPropertyEditor`. See [_Trailhead_: Build Lightning Web Components](https://trailhead.salesforce.com/content/learn/trails/build-lightning-web-components) or [Create Lightning Web Components](https://developer.salesforce.com/docs/platform/lwc/guide/create-components-introduction.html) in the _Lightning Web Components Developer Guide_. 2. In the `screenFlowPropertyEditor.html` file, configure how the screen flow picker is rendered in the property editor. ```html ``` 3. In the `screenFlowPropertyEditor.js-meta.xml` configuration file, specify the `lightning__PropertyEditor` target, as shown in this example. ```xml 62.0 true Screen Flow Custom Property Editor Component lightning__PropertyEditor ``` 4. In the `screenFlowPropertyEditor.js` file, declare an `@api` variable named `value` to store the selected custom object record ID in Program Builder. Add the LWC of the custom property editor to the `customContent` property of the property sheet. See the [Add the Custom Exercise Type to Program Builder](#add-the-custom-exercise-type-to-program-builder) section. The ID of the record that the Enablement admin selects for the exercise in Program Builder is stored in the `value` attribute of the `customContent` property. To change the `value` from this property editor component, call the `valuechange` event and specify the custom object record ID. ```js import { LightningElement, api, track } from "lwc"; // This component is rendered in place of the customContent property // in the custom exercise that uses this LWC component as an editor. export default class ScreenFlowPropertyEditor extends LightningElement { @track _value; @track compConnected; connectedCallback() { this.compConnected = true; } // The value variable holds the selected custom object record ID. @api get value() { return this._value; } // Set the record-picker value. set value(val) { if (this.compConnected && this._value !== val) { this.refs?.screenFlowPickerRef?.clearSelection(); } this._value = val; } // Fire the value change event to let the Program Builder know // that the value for the customContent property has changed. handleRecordChanged(event) { this._value = event.detail.recordId; this.dispatchEvent( new CustomEvent("valuechange", { detail: { value: CUSTOM_OBJECT_RECORD_ID, }, }), ); } } ``` ### Limitations of Custom Property Editor - Only the selected `customContent` property `value` is passed to the LWC. No other metadata information like `min`, `max`, or `label`, are passed. - When the program is in the Published state, custom exercise properties in Program Builder, such as `day`, are in read-only mode. However, when you implement a custom property editor by adding the custom LWC to the `editor` attribute of the `customContent` property, the LWC doesn’t automatically get disabled when the program is published. This is because the LWC doesn’t know about the published state or read-only mode. - To add the LWC of the custom property editor to the `customContent` property of the `lightning__EnablementProgram` target, configure these attributes. - Add the LWC of the custom property editor to the `editor` attribute. - The `label` attribute isn’t used but must be included with a placeholder value. ```xml ``` See [Add the Custom Exercise Type to Program Builder](#add-the-custom-exercise-type-to-program-builder) ## Add the Custom Exercise Type to Program Builder To add the custom exercise type with screen flows to the Components palette in Program Builder, create an LWC. You can find the code examples in the `lwc` folder of the downloaded files. 1. Create an LWC bundle. Let's name our example bundle `screenFlow`. See [_Trailhead_: Build Lightning Web Components](https://trailhead.salesforce.com/content/learn/trails/build-lightning-web-components) or [Create Lightning Web Components](https://developer.salesforce.com/docs/platform/lwc/guide/create-components-introduction.html) in the _Lightning Web Components Developer Guide_. 2. Leave the `screenFlow.html` file empty. We don’t render the markup of this HTML file. ```html ``` 3. In the `screenFlow.js-meta.xml` configuration file, add and configure the [`lightning__EnablementProgram` target](https://developer.salesforce.com/docs/platform/lwc/guide/targets-lightning-enablement-program.html), as shown in this example. This LWC target adds the custom exercise type to the Components palette. The target properties are the options available in the Program Builder property sheet when an Enablement admin adds the custom exercise type to a program. Make the `default` attribute of the `subcategory` property point to the `developerName` of the EnblProgramTaskSubCategory Metadata type record. The `editor` attribute of the `customContent` property calls the `screenFlowPropertyEditor` LWC, which you created in the [Customize the Property Editor for the Custom Exercise Type](#customize-the-property-editor-for-the-custom-exercise-type) section. ```xml 62.0 true Screen Flows Screen Flows lightning__EnablementProgram ``` 4. In the `screenFlow.js` file, include the `@api` variables for each property configured in the `.js-meta.xml` file. ```js import { api, LightningElement } from "lwc"; export default class ScreenFlow extends LightningElement { @api title; @api description; @api day; @api category; @api subCategory; @api customContent; } ``` 5. In the `screenFlow.svg` file, add the custom exercise icon as an SVG image, as shown here. Program Builder shows this icon in the Custom Items section of the Components palette. **tip:** To find the SVG definition of an icon, download the icons from the [Salesforce Lightning Design System website](https://core-main.lightningdesignsystem.com/icons/) and navigate to the SVG file for the icon you want to use. ```xml ``` ## Configure the Custom Exercise for the Guidance Center Create a new LWC component for showing the custom exercise type in the Guidance Center. You can find the code examples in the `lwc` folder of the downloaded files. 1. Create an LWC bundle. Let's name our example bundle `screenFlowViewer`. See [_Trailhead_: Build Lightning Web Components](https://trailhead.salesforce.com/content/learn/trails/build-lightning-web-components) or [Create Lightning Web Components](https://developer.salesforce.com/docs/platform/lwc/guide/create-components-introduction.html) in the _Lightning Web Components Developer Guide_. 2. In the `screenFlowViewer.html` file, configure how to display the custom exercise in the Guidance Center. ```html ``` 3. In the `screenFlowViewer.js-meta.xml` configuration file, expose this component to other namespaces, and specify the API version. Salesforce’s internal LWC that invokes this LWC is in a different namespace. To make this component visible to other namespaces, expose this component. ```xml 62.0 true ``` 4. In the `screenFlowViewer.js` file, add these methods and variables. - `getFlowDeveloperName()`: Fetches the screen flow’s developer name by using this method of the optional custom `ResourceController` Apex class. You can add more Apex classes to `ResourceController` for additional customization. You can find an example in the `classes/ResourceController.cls` file of the download. - `@api customContent`: Contains the ID of the content record that the Enablement admin selected for the custom exercise in Program Builder. - `@api learningItemId`: Contains the learning item record ID, which you can use as input to evaluateLearningItem API to determine the progress of the custom exercise. See [Track a User's Progress in a Custom Exercise](https://developer.salesforce.com/docs/sales/enablement/guide/enablement-custom-exercise-track-progress.md) and [evaluateLearningItem LWC wire adapter reference](https://developer.salesforce.com/docs/platform/lwc/guide/reference-evaluate-learning-item) in the _Lightning Web Components Developer Guide_. ```js import { api, wire, LightningElement } from "lwc"; import getFlowDeveloperName from "@salesforce/apex/ResourceController.getFlowDeveloperName"; import isLearningItemStatusCompleted from "@salesforce/apex/ResourceController.isLearningItemStatusCompleted"; import getScreenNames from "@salesforce/apex/ResourceController.getScreenNames"; import { evaluateLearningItem } from "lightning/uiLearningPlatformApi"; import Id from "@salesforce/user/Id"; export default class ScreenFlowViewer extends LightningElement { flowDeveloperName; screenNames; loading = false; userId = Id; learningItemStatusCompleted; progressLoaded = false; isFlowCompleted = false; @api customContent; @api learningItemId; get renderFlow() { return ( this.flowDeveloperName && this.screenNames && this.progressLoaded && !this.learningItemStatusCompleted && !this.isFlowCompleted ); } @wire(isLearningItemStatusCompleted, { learningItemId: "$learningItemId", userId: "$userId" }) learningItemprogress({ error, data }) { if (error) { // show error } else if (data !== undefined) { this.progressLoaded = true; this.learningItemStatusCompleted = data; } } @wire(getFlowDeveloperName, { customScreenFlowId: "$customContent" }) flowDevName({ error, data }) { if (error) { // show error } else if (data) { this.flowDeveloperName = data; } } @wire(getScreenNames, { flowDeveloperName: "$flowDeveloperName" }) flowScreens({ error, data }) { if (error) { // show error } else if (data) { this.screenNames = data.join(","); } } async handleStatusChange(event) { if (this.flowStatus === "FINISHED") { return; } const learningItemId = this.learningItemId; this.loading = true; this.flowStatus = event.detail.status; this.currentScreen = event.detail.locationName; const res = await evaluateLearningItem({ learningItemId, details: { status: event.detail.status, currentScreen: event.detail.locationName, allScreens: this.screenNames, }, }); if (this.flowStatus === "FINISHED") { this.isFlowCompleted = true; } const { error } = res; if (error) { // show error } else { this.learningItemStatusCompleted = res.isCompleted; } this.loading = false; } } ``` That's it! Enablement admins can now start adding the screen flow custom exercise type to programs, and users can start taking the exercise when they enroll in programs. # Create a Managed Package for Enablement Programs, Measures, and Content To package Enablement programs and their dependencies, set up your source org environment and project settings for Enablement data, retrieve the appropriate metadata, and create the package. **tip:** Before you create a package, review the [considerations for packaging Enablement programs and dependencies](https://developer.salesforce.com/docs/sales/enablement/guide/enablement-package-considerations.md). ## 1. Specify Features and Metadata in Your Project In your Salesforce DX project, add the required scratch org features and settings, and retrieve the metadata related to the programs that you want to package. 1. Set up your source org for creating second-generation managed packages. To learn about setup options, see [Second-Generation Managed Packages](https://developer.salesforce.com/docs/atlas.en-us.pkg2_dev.meta/pkg2_dev/sfdx_dev_dev2gp.htm) in the _Second-Generation Managed Packaging Developer Guide_. 2. In your scratch org definition file, include these scratch org features and settings that you need for programs. See [Build Your Own Scratch Org Definition File](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_scratch_orgs_def_file.htm) in the _Salesforce DX Developer Guide_. For all programs, include the scratch org features `GuidanceHubAllowed` and `Enablement`. ```json { "features": ["GuidanceHubAllowed", "Enablement"] } ``` If the program contains a Feedback Request exercise that references an assessment survey, also include the `SurveyAdvancedFeature` scratch org feature. `SurveyAdvancedFeature` requires the SurveySettings where `enableSurvey` is `true` . The SurveySettings must be included in the the scratch org definition file. ```json { "features": ["GuidanceHubAllowed", "Enablement", "SurveyAdvancedFeatures"], "settings": { "SurveySettings": { "enableSurvey": true } } } ``` If the program is for Partner Enablement in an Experience Cloud site, also include the scratch org features `EnablePRM` and `Communities`. `Communities` also requires the CommunitiesSettings scratch org setting where `enableNetworkEnabled` is `true`. ```json { "features": ["GuidanceHubAllowed", "Enablement", "Communities", "EnablePRM"], "settings": { "CommunitiesSettings": { "enableNetworkEnabled": true } } } ``` 3. Retrieve these metadata types for the Enablement programs and dependencies that you want to package. - [EnablementProgramDefinition](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_enablementprogramdefinition.htm) - [EnablementMeasureDefinition](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_enablementmeasuredefinition.htm), if a program includes an outcome and milestones that reference Enablement measures - [Flow](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_visual_workflow.htm), if a program includes a Feedback Request exercise that references an assessment survey Include this entry in your `package.xml` file. ```xml * Flow 62.0 ``` - [Network](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_network.htm), if a program is for Partner Enablement in an Experience Cloud site - [DigitalExperienceBundle](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_digitalexperiencebundle.htm), if a program includes exercise content from the Enablement workspace in the Digital Experiences app Retrieve content only from the Enablement workspace. Don't retrieve content from other workspaces. Include this entry in your `package.xml` file. ```xml enablement/sfdcEnablement_EnablementWorkspace DigitalExperienceBundle 62.0 ``` **note:** Because only content with a status of Published can be used in a program, all content included in the package is set to Published when the package is installed. This is different behavior from when you package Digital Experiences content outside the Enablement workspace, where you can package and deploy content of other statuses. When you retrieve content to include in a package, take care to retrieve only the content from the Enablement workspace. 4. In your Salesforce DX project’s `force-app/main/default` directory, verify that you have these folders for the metadata components that you’re including in the package. - `enablementProgramDefinitions` - `enablementMeasureDefinitions`, if a program includes an outcome and milestones that reference measures - `flows`, if a program includes a Feedback Request exercise that references an assessment survey - `networks`, if a program is for Partner Enablement in an Experience Cloud site - `digitalExperienceBundles`, if a program includes exercise content from the Enablement workspace in the Digital Experiences app ## 2. Retrieve Metadata for Custom Exercise Types If a program includes custom exercise types, retrieve additional required metadata types and include them in your project. See [Create Custom Exercises for Enablement Programs](https://developer.salesforce.com/docs/sales/enablement/guide/enablement-custom-exercises-intro.md). 1. Retrieve these metadata types. - [ApexClass](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_classes.htm), for the evaluation handler, serializer, and deserializer classes associated with a custom exercise type - [CustomObject](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/customobject.htm), for the custom object associated with a custom exercise type - [CustomField](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/customfield.htm), for the custom field on the LearningItem object - [EnblProgramTaskSubCategory](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_enblprogramtasksubcategory.htm) and [LearningItemType](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_learningitemtype.htm), for the custom exercise definitions in Program Builder and Guidance Center, respectively - [LightningComponentBundle](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_lightningcomponentbundle.htm), for the custom Lightning web components associated with a custom exercise type 2. In your Salesforce DX project’s `force-app/main/default` directory, verify that you have these folders for the metadata components that you’re including in the package. - `classes` for Apex classes - `objects` for the standard object LearningItem with a custom field and the custom object - `enblProgramTaskSubCategories` - `learningItemTypes` - `lwc` for the Lightning web components ## 3. Modify the Retrieved Metadata for Digital Experiences Content In your project, update the retrieved metadata for Digital Experiences content related to program exercises, and update the program metadata that references that content. 1. In the `digitalExperienceBundles` folder, change the name of the workspace that’s installed in the destination org. Because the destination org has an Enablement add-on license, the destination org also has the default Enablement workspace with the `sfdcEnablement_EnablementWorkspace` API name. To avoid conflicts with that default namespace in the destination org, specify a unique name and API name for the workspace that the package installs. 1. Change the value of `