# Cpq Custom Actions

> Configure and troubleshoot Salesforce CPQ custom action buttons in the Quote Line Editor, product configurator, and amendment/renewal screens, covering action types, placement, conditional visibility, and the five-action limit.

- Skill: `pranavnagrecha/cpq-custom-actions` (Agent Skill, multi-file: 7 files)
- Install (CLI): `npx skillmds add pranavnagrecha/cpq-custom-actions`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pranavnagrecha/cpq-custom-actions/raw
- Safety review: PASS (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools, API Design
- Tags: Apex, Cpq, Custom Actions, Flow, Quote Line Editor, Salesforce, Url Actions
- Author: PranavNagrecha (https://skillmd.com/u/pranavnagrecha)
- Updated: 2026-08-22
- Page: https://skillmd.com/skills/pranavnagrecha/cpq-custom-actions

---


# 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:

- Confirm the CPQ managed package (`SBQQ__`) is installed and the user has the "Salesforce CPQ Admin" or appropriate permission set to create and edit `SBQQ__CustomAction__c` records.
- Identify the target screen: QLE (Quote Line Editor), product configurator, or amendment/renewal flow. Each is a separate `Location` value on the `SBQQ__CustomAction__c` object.
- Take a count of what already exists at that location. Five per location is a hard ceiling in CPQ, and it is worth knowing how much headroom is left before you design anything — **The Five-Action Hard Limit Per Context** below covers the consequences.
- Determine whether Apex logic is required. Custom actions cannot invoke Apex directly — only URL navigation, Screen/Autolaunched Flow execution, or standard CPQ operations (Save, Calculate, Add Group) are supported action types.

---

## 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:

| Field | Behavior |
|---|---|
| `SBQQ__Type__c` | The 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__c` | Controls 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__c` | Integer controlling the left-to-right display order of buttons when multiple custom actions share the same location. |
| `SBQQ__Active__c` | Boolean; only active records are rendered in the UI. |
| `SBQQ__FlowName__c` | API name of the Flow to invoke when type is `Flow`. |
| `SBQQ__URL__c` | The 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

| Situation | Recommended Approach | Reason |
|---|---|---|
| Need to execute Apex logic from a QLE button | Flow action type with an Apex invocable method inside the Flow | Custom actions have no Apex type; Flow is the supported bridge |
| Need to open an external tool in context | URL action type with CPQ merge field tokens in the URL | Simplest path; no managed package extension needed |
| Need to trigger standard CPQ recalculation on demand | `Calculate` action type | Purpose-built; no custom code required |
| Need conditional button visibility | CPQ 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 context | Consolidate actions into a single Flow with a choice screen | The 5-action limit is hard; the Flow can branch to multiple logical operations |
| Need a button per line row vs. per quote | Line Item location for per-row, Global location for per-quote | Location field controls rendering granularity |

---

## Recommended Workflow

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:

- [ ] Active custom action count for the target location is 5 or fewer
- [ ] Flow is activated (not Draft) before the custom action record is created
- [ ] For URL actions, the target URL domain is added to CSP Trusted Sites if it opens in a Lightning context
- [ ] Conditional visibility is configured via CPQ condition records, not via Flow decisions or Apex
- [ ] The action has been tested on both the target CPQ screen and, if applicable, on amendment/renewal quotes
- [ ] Button label in `Name` field is clear and rep-facing (not an internal API name)
- [ ] `SBQQ__DisplayOrder__c` is set to avoid collisions with existing actions

---

## Salesforce-Specific Gotchas

Each of these is written up in full, with the avoidance strategy, in [`references/gotchas.md`](references/gotchas.md). 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

| Artifact | Description |
|---|---|
| SBQQ__CustomAction__c record | The 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 records | Condition 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 results | SOQL results confirming action count per location is within the 5-action limit |

---

## Related Skills

- `cpq-architecture-patterns` — Covers QLE performance limits, Large Quote Mode, and bundle design patterns that interact with custom action placement
- `apex-invocable-actions` — Use alongside this skill when a Flow-backed custom action needs to call an Apex invocable method

