SfSkills Domains Docs GitHub

Home / apex / cpq-custom-actions

cpq-custom-actions

Use when adding, configuring, or troubleshooting custom action buttons in the Salesforce CPQ Quote Line Editor (QLE), product configurator, or amendment/renewal screens. Trigger keywords: CPQ custom action, QLE button, SBQQ__CustomAction__c, CPQ Flow button, CPQ URL action. NOT for code CPQ itself calls during calculation or contracting — use apex/cpq-apex-plugins. NOT for standard quick actions on non-CPQ objects — use admin/global-actions-and-quick-actions.

Open the skill folder on GitHub


CPQ Custom Actions

This skill activates when configuring or debugging SBQQ__CustomAction__c records that surface as clickable buttons in the Salesforce CPQ Quote Line Editor (QLE), product configurator, or amendment/renewal screens. It covers all action types (URL, Flow, standard CPQ operations), the Location field that controls placement, conditional visibility through the CPQ condition evaluation engine, and the hard limit of five custom actions per context.


Before Starting

Gather this context before working on anything in this domain:


Core Concepts

SBQQ__CustomAction__c — The Object Behind Every QLE Button

CPQ custom actions are standard Salesforce object records of type SBQQ__CustomAction__c in the SBQQ managed package. Each record represents one button. Fields that control behavior:

FieldBehavior
SBQQ__Type__cThe action type. Valid values: URL (opens a URL in a new tab or same tab), Flow (launches a Screen Flow or Autolaunched Flow), Calculate (triggers CPQ price recalculation), Save (triggers CPQ quote save), Add Group (adds a new quote line group). Only these values are recognized; arbitrary strings fail silently.
SBQQ__Location__cControls which CPQ screen and position renders the button. Values include Line Item (in each quote line row), Group (at the group header level), Global (once per quote, above the line grid). The configurator and amendment screens have their own location values.
SBQQ__DisplayOrder__cInteger controlling the left-to-right display order of buttons when multiple custom actions share the same location.
SBQQ__Active__cBoolean; only active records are rendered in the UI.
SBQQ__FlowName__cAPI name of the Flow to invoke when type is Flow.
SBQQ__URL__cThe URL to navigate to when type is URL. Supports merge field syntax for passing CPQ record IDs.

The Five-Action Hard Limit Per Context

Salesforce CPQ enforces a hard limit of five custom actions per location context. This is not a configurable setting or a governor limit — it is a rendering constraint in the QLE Lightning component. If more than five active SBQQ__CustomAction__c records exist for a given Location__c value, CPQ renders only five and silently ignores the rest (ordering is not guaranteed for the dropped actions). Architects must audit the existing action count before adding new ones.

Conditional Visibility — CPQ Condition Engine, Not Flow or Apex

Custom action visibility can be made conditional — for example, showing a validation button only when a quote is in Draft status. This is controlled by CPQ's built-in condition evaluation engine, the same engine that drives Product Rules and Price Rules. Conditions are configured by associating SBQQ__CustomActionCondition__c records (or equivalent condition child records) with the custom action and setting the SBQQ__ConditionsMet__c picklist on the action (All, Any, or Formula).

This is a frequent source of confusion: practitioners often attempt to control button visibility through Flow decisions or Apex triggers on the quote. Neither approach works. The CPQ rendering layer reads conditions at page load from the CPQ condition framework — it does not evaluate Apex triggers or Flow output to decide whether to render a button.

Apex Execution — Only via Workaround

Custom actions of type Flow or URL are the only paths to executing Apex logic from a custom button. There is no Apex action type on SBQQ__CustomAction__c. Two supported workarounds:

  1. Flow calling Apex — Configure the action as type Flow, then use an Apex-defined invocable method called from an Action element inside the Flow. The Flow acts as the bridge.
  2. URL to a Visualforce page — Configure the action as type URL pointing to a Visualforce page that invokes an Apex controller. Pass the quote or line ID via URL parameters using CPQ merge field syntax (e.g., {!SBQQ__Quote__c}).

Common Patterns

Pattern: Flow-Backed Validation Button in the QLE

When to use: A business rule requires reps to validate quote data (e.g., checking product compatibility or minimum order quantities) before submitting, and the validation logic is complex enough to require Apex.

How it works:

  1. Build an Autolaunched Flow with an Apex action (invocable method) containing the validation logic. The Flow receives the Quote ID as an input variable.
  2. Create an SBQQ__CustomAction__c record: SBQQ__Type__c = Flow, SBQQ__FlowName__c = the Flow API name, SBQQ__Location__c = Global (one button per quote, not per line).
  3. Add a SBQQ__CustomActionCondition__c record if the button should only appear in Draft status: field = SBQQ__Quote__r.SBQQ__Status__c, operator = Equals, value = Draft.
  4. Set SBQQ__Active__c = true and an appropriate SBQQ__DisplayOrder__c.

Why not the alternative: Using a standard CPQ Price Rule or Product Rule for validation fires automatically on save/calculate and cannot be triggered on demand by the rep. A custom action gives the rep explicit control over when validation runs.

Pattern: URL Action Opening an External Pricing Tool

When to use: The org integrates with an external pricing or CPQ tool that should be launched in context from the QLE, passing the current quote and line IDs to the external system.

How it works:

  1. Create an SBQQ__CustomAction__c record: SBQQ__Type__c = URL.
  2. In SBQQ__URL__c, use CPQ merge field syntax to embed the record ID: https://external-tool.example.com/quote?sfQuoteId={!SBQQ__Quote__c}&sfLineId={!Id}. The {!Id} token resolves to the current line item record ID when Location__c = Line Item.
  3. Set SBQQ__Location__c = Line Item if the external tool is line-specific, or Global if it operates at the quote level.
  4. Verify the URL is added to the org's Content Security Policy (CSP) trusted sites if it opens in a Lightning iframe context.

Why not the alternative: Visualforce pages embedded in the QLE introduce complexity and session context issues. A URL action that opens in a new browser tab is simpler, avoids CSP complications, and works with any external tool.


Decision Guidance

SituationRecommended ApproachReason
Need to execute Apex logic from a QLE buttonFlow action type with an Apex invocable method inside the FlowCustom actions have no Apex type; Flow is the supported bridge
Need to open an external tool in contextURL action type with CPQ merge field tokens in the URLSimplest path; no managed package extension needed
Need to trigger standard CPQ recalculation on demandCalculate action typePurpose-built; no custom code required
Need conditional button visibilityCPQ condition records on the action (SBQQ__CustomActionCondition__c)CPQ rendering evaluates its own condition engine; Flow/Apex are not evaluated at render time
At or near the 5-action limit per contextConsolidate actions into a single Flow with a choice screenThe 5-action limit is hard; the Flow can branch to multiple logical operations
Need a button per line row vs. per quoteLine Item location for per-row, Global location for per-quoteLocation field controls rendering granularity

Step-by-step instructions for an AI agent or practitioner working on this task:

  1. Audit existing custom actions — Query SBQQ__CustomAction__c filtered by the target SBQQ__Location__c and SBQQ__Active__c = true. Count records. If already at 5, the new action must replace an existing one or be consolidated into a Flow with branching logic before proceeding.
  2. Determine action type — Decide whether the button should navigate to a URL, launch a Flow, or trigger a standard CPQ operation (Save, Calculate, Add Group). If Apex logic is required, plan the Flow-as-bridge or Visualforce-URL workaround at this step.
  3. Build the Flow or Visualforce page if needed — For Flow-backed actions, build and activate the Flow first. Confirm the Flow API name. For URL actions pointing to an org page, create and deploy the Visualforce page before creating the action record.
  4. Create the SBQQ__CustomAction__c record — Set SBQQ__Type__c, SBQQ__Location__c, SBQQ__FlowName__c or SBQQ__URL__c, SBQQ__DisplayOrder__c, and SBQQ__Active__c = true. Use a meaningful label in Name — this appears as the button label in the QLE.
  5. Configure conditional visibility if required — Create SBQQ__CustomActionCondition__c records referencing the parent action. Set the SBQQ__ConditionsMet__c field on the action to All, Any, or Formula. Test by loading the QLE with a quote that meets and does not meet the conditions.
  6. Test end-to-end in the QLE — Open a test quote in the QLE. Verify the button renders at the correct location, the label is correct, the action fires correctly, and conditional visibility behaves as expected. Check browser console for JavaScript errors if the button does not appear.
  7. Review checklist before deploying to production — Confirm the 5-action limit is not exceeded, Flow is activated, URL CSP trusted sites are configured, and the action is tested on both new and amendment quotes if applicable.

Review Checklist

Run through these before marking work in this area complete:


Salesforce-Specific Gotchas

Each of these is written up in full, with the avoidance strategy, in 0. The short version:

  1. Going past five actions fails quietly. Nothing errors and nothing is logged — the surplus buttons simply never render at that location.
  2. A Draft Flow is not a working action. Activate it first, or the rep gets an unhelpful generic runtime error the moment they click.
  3. Conditional visibility is resolved once, at page load. Editing a field inside the QLE will not make a hidden button appear until the page is reloaded.
  4. {!Id} means different things in different Locations. At Line Item it resolves to the line's ID; at Global it resolves to the quote's.
  5. These buttons live inside CPQ screens only. Don't expect them on the standard Lightning record page.

Output Artifacts

ArtifactDescription
SBQQ__CustomAction__c recordThe configured custom action button record in the CPQ org
Flow (if type = Flow)The activated Flow wired to the action, optionally containing an Apex invocable action
SBQQ__CustomActionCondition__c recordsCondition records controlling button visibility (if conditional behavior required)
Visualforce page (if type = URL to VF)Page wired to an Apex controller for Apex-execution workaround via URL action
Audit query resultsSOQL results confirming action count per location is within the 5-action limit

Package files