Custom Hooks for Template Operation
All five Sales Config Template APIs support Pre and Post invoke hooks via the standard CpqAppHandlerHook framework. This allows you to inject custom Apex logic before and after each template operation without any platform code changes.
Required Editions
| Available in: Classic and Lightning Experience |
| Available in: Enterprise, Unlimited, and Developer Editions for CME Managed Package |
Hook Keys
Hooks are identified by the pattern {methodName}.PreInvoke and {methodName}.PostInvoke:
| API | Pre Hook Key | Post Hook Key |
|---|---|---|
| Save Template | saveSalesConfigTemplate.PreInvoke | saveSalesConfigTemplate.PostInvoke |
| Get Template | getSalesConfigTemplate.PreInvoke | getSalesConfigTemplate.PostInvoke |
| Search Templates | searchSalesConfigTemplate.PreInvoke | searchSalesConfigTemplate.PostInvoke |
| Apply Template | applySalesConfigTemplate.PreInvoke | applySalesConfigTemplate.PostInvoke |
| Delete Template | deleteSalesConfigTemplate.PreInvoke | deleteSalesConfigTemplate.PostInvoke |
Hook Lifecycle
For every operation, execution occurs in this order:
- {methodName}.PreInvoke - runs before the core operation
- Core operation - Save / Get / Search / Apply / Delete logic
- {methodName}.PostInvoke - runs after output is fully populated
Implementation Script
Create an Apex class named CPQAppHandlerHookImplementation (outside the managed package) that implements either VlocityOpenInterface or VlocityOpenInterface2. The platform's CpqAppHandlerHookImplementation will automatically discover and delegate to it. Here is the same code for a hook on Save Template:
public class CPQAppHandlerHookImplementation implements
VlocityOpenInterface {
public Boolean invokeMethod(String methodName, Map<String, Object>
input,
Map<String, Object> output,
Map<String, Object> options) {
if (methodName == 'saveSalesConfigTemplate.PostInvoke') {
onSaveTemplatePost(input, output);
} else if (methodName == 'applySalesConfigTemplate.PreInvoke') {
onApplyTemplatePre(input);
}
return true;
}
private void onSaveTemplatePost(Map<String, Object> input,
Map<String, Object> output) {
// Custom logic after a template is saved
// output contains: result.records[0].templateId, success, errorCode
}
private void onApplyTemplatePre(Map<String, Object> input) {
// Custom logic before a template is applied
// input contains: templateId, cartId, name, asyncMode, pricing,
validation
}
}Message Structure
Each message in the messages array contains:
{
"code": "TEMPLATE_NOT_FOUND",
"severity": "ERROR",
"message": "Template with ID 'a1Bxx0000004XyzDEF' not found"
}| Field | Type | Value | Description |
|---|---|---|---|
| code | String | Varies | A machine-readable error code. |
| severity | String | ERROR, WARN, INFO | The severity level of the message. |
| message | String | Varies | A human-readable description of the error or warning. |
- Save Template Using OmniScript
Use the pre-built OmniScript to save an existing Quote or Order as a reusable template from the CPQ Cart UI.

