ui-policies
Catalog UI Policies
Overview
This skill covers creating and managing UI policies for Service Catalog items:
- Creating UI policies with conditions
- Show/hide/mandatory/disabled actions
- Scripted UI policy conditions
- UI Policy Actions and their REST API limitations
- Workarounds for linking actions via background scripts
- Testing and debugging UI policies
When to use: When building dynamic catalog forms that need to show/hide fields, make fields mandatory based on other selections, or implement complex conditional logic.
Who should use this: Catalog administrators and developers building service catalog items with dynamic form behavior.
Prerequisites
- Roles:
catalog_adminoradmin - Access: catalog_ui_policy, catalog_ui_policy_action, item_option_new tables
- Knowledge: Basic catalog item structure, variables
- Related Skills:
catalog/variable-managementfor variable creation
Understanding Catalog UI Policies
UI Policy Components
┌─────────────────────────────────────────────────────────┐
│ Catalog UI Policy │
├─────────────────────────────────────────────────────────┤
│ Catalog Item: [reference to sc_cat_item] │
│ Short Description: "Show approval fields for >$1000" │
│ Conditions: amount>1000 │
│ On Load: true Reverse If False: true │
│ Script True: [optional advanced logic] │
│ Script False: [optional reverse logic] │
├─────────────────────────────────────────────────────────┤
│ UI Policy Actions │
├─────────────────────────────────────────────────────────┤
│ Action 1: manager_approval → visible=true, mandatory=true │
│ Action 2: justification → visible=true │
│ Action 3: cost_center → disabled=false │
└─────────────────────────────────────────────────────────┘
UI Policy Evaluation
User Opens Form
│
▼
┌─────────────────┐
│ On Load = true?│───No──► Conditions evaluated only on change
│
Yes
│
▼
┌─────────────────┐
│ Evaluate │
│ Conditions │
└────────┬────────┘
│
┌────┴────┐
│ │
True False
│ │
▼ ▼
┌────────┐ ┌────────────────┐
│ Apply │ │Reverse If False│───Yes──► Apply opposite actions
│Actions │ │ = true? │
└────────┘ └───────┬────────┘
│
No
│
▼
Do nothing
Procedure
Phase 1: Create Basic UI Policy
Step 1.1: Create UI Policy Record
Simple Condition-Based Policy:
Tool: SN-Create-Record
Parameters:
table_name: catalog_ui_policy
data:
catalog_item: [catalog_item_sys_id]
short_description: "Show approval fields when amount exceeds $1000"
active: true
on_load: true
reverse_if_false: true
order: 100
global: false
run_scripts: false
Result: Returns sys_id of created UI policy (e.g., policy_sys_id)
Step 1.2: Understand UI Policy Fields
| Field | Type | Description |
|---|---|---|
| catalog_item | Reference | Target catalog item |
| short_description | String | Policy name/description |
| active | Boolean | Enable/disable policy |
| on_load | Boolean | Evaluate when form loads |
| reverse_if_false | Boolean | Apply opposite when condition false |
| order | Integer | Execution order (lower = first) |
| global | Boolean | Apply to all catalog items |
| run_scripts | Boolean | Enable Script True/Script False |
| conditions | Conditions | Encoded query against variables |
Phase 2: Add UI Policy Conditions
Step 2.1: Simple Field Condition
Conditions compare variable values using encoded query syntax:
Create Policy with Condition:
Tool: SN-Create-Record
Parameters:
table_name: catalog_ui_policy
data:
catalog_item: [catalog_item_sys_id]
short_description: "Show manager approval for external vendors"
conditions: "vendor_type=external"
active: true
on_load: true
reverse_if_false: true
order: 100
Condition Syntax Examples:
| Condition | Meaning |
|---|---|
priority=1 |
Priority equals 1 |
amount>1000 |
Amount greater than 1000 |
category=hardware^urgency=1 |
Category is hardware AND urgency is 1 |
department=IT^ORdepartment=Security |
Department is IT OR Security |
requested_for.active=true |
Referenced user is active |
descriptionISNOTEMPTY |
Description has a value |
quantityBETWEEN1@10 |
Quantity between 1 and 10 |
Step 2.2: Multiple Conditions (AND/OR)
AND Conditions:
Tool: SN-Create-Record
Parameters:
table_name: catalog_ui_policy
data:
catalog_item: [catalog_item_sys_id]
short_description: "Require justification for high-cost IT equipment"
conditions: "department=IT^hardware_type=laptop^estimated_cost>2000"
active: true
on_load: true
reverse_if_false: true
order: 200
OR Conditions:
Tool: SN-Create-Record
Parameters:
table_name: catalog_ui_policy
data:
catalog_item: [catalog_item_sys_id]
short_description: "Show security review for sensitive departments"
conditions: "department=Finance^ORdepartment=Legal^ORdepartment=HR"
active: true
on_load: true
reverse_if_false: true
order: 300
Phase 3: Create UI Policy Actions
CRITICAL: UI Policy Actions cannot be fully linked to variables via REST API due to ServiceNow limitations. The catalog_variable field requires special handling.
Step 3.1: Understand the Limitation
When you create a catalog_ui_policy_action via REST API:
- The record is created successfully
- The
catalog_variablefield WILL NOT link properly - The action won't work until manually fixed or updated via background script
Why This Happens:
- The
catalog_variablefield expects a special format:IO:[variable_sys_id] - The REST API doesn't properly resolve this reference type
- This is a known ServiceNow platform limitation
Step 3.2: Create Action Record (Step 1 of 2)
First, create the UI policy action record:
Tool: SN-Create-Record
Parameters:
table_name: catalog_ui_policy_action
data:
ui_policy: [policy_sys_id]
visible: true
mandatory: true
disabled: false
Result: Returns action sys_id (e.g., action_sys_id)
Note: The action is created but NOT linked to any variable yet.
Step 3.3: Link Action to Variable (Step 2 of 2)
REQUIRED: Use background script to properly link the action:
Tool: SN-Execute-Background-Script
Parameters:
script: |
// Link UI Policy Action to Catalog Variable
var actionSysId = '[action_sys_id]';
var variableSysId = '[variable_sys_id]';
var gr = new GlideRecord('catalog_ui_policy_action');
if (gr.get(actionSysId)) {
// The catalog_variable field requires IO: prefix
gr.setValue('catalog_variable', 'IO:' + variableSysId);
gr.update();
gs.info('Linked UI policy action ' + actionSysId + ' to variable ' + variableSysId);
} else {
gs.error('UI policy action not found: ' + actionSysId);
}
description: Link UI policy action to catalog variable
Step 3.4: Batch Link Multiple Actions
For multiple actions, batch them in one script:
Tool: SN-Execute-Background-Script
Parameters:
script: |
// Batch link UI Policy Actions
var links = [
{ action: '[action1_sys_id]', variable: '[var1_sys_id]' },
{ action: '[action2_sys_id]', variable: '[var2_sys_id]' },
{ action: '[action3_sys_id]', variable: '[var3_sys_id]' }
];
var success = 0;
var failed = 0;
links.forEach(function(link) {
var gr = new GlideRecord('catalog_ui_policy_action');
if (gr.get(link.action)) {
gr.setValue('catalog_variable', 'IO:' + link.variable);
gr.update();
gs.info('Linked: ' + link.action + ' -> ' + link.variable);
success++;
} else {
gs.error('Action not found: ' + link.action);
failed++;
}
});
gs.info('Batch link complete: ' + success + ' success, ' + failed + ' failed');
description: Batch link UI policy actions to variables
Phase 4: UI Policy Action Types
Step 4.1: Show/Hide Variables
Show a variable when condition is true:
Tool: SN-Create-Record
Parameters:
table_name: catalog_ui_policy_action
data:
ui_policy: [policy_sys_id]
visible: true
mandatory: false
disabled: false
Then link to variable using background script (Step 3.3).
Hide a variable (with reverse_if_false):
- Set
visible: truein action - Set
reverse_if_false: trueon policy - When condition is FALSE, variable will be hidden
Step 4.2: Mandatory Actions
Make variable mandatory when condition is true:
Tool: SN-Create-Record
Parameters:
table_name: catalog_ui_policy_action
data:
ui_policy: [policy_sys_id]
visible: true
mandatory: true
disabled: false
Common Pattern: Show AND make mandatory:
visible: true- Show the fieldmandatory: true- Require a value- With
reverse_if_false: true- When condition false, field hidden and not required
Step 4.3: Disabled/Read-Only Actions
Make variable read-only when condition is true:
Tool: SN-Create-Record
Parameters:
table_name: catalog_ui_policy_action
data:
ui_policy: [policy_sys_id]
visible: true
mandatory: false
disabled: true
Use Case: Lock calculated fields or display-only values.
Phase 5: Scripted UI Policies
Step 5.1: Enable Script Execution
For complex conditions that can't be expressed in simple encoded queries:
Tool: SN-Update-Record
Parameters:
table_name: catalog_ui_policy
sys_id: [policy_sys_id]
data:
run_scripts: true
script_true: |
// Runs when condition is TRUE
// Access variables via g_form
var amount = g_form.getValue('amount');
var dept = g_form.getValue('department');
if (parseFloat(amount) > 5000 && dept == 'IT') {
// Additional client-side logic
g_form.showFieldMsg('amount', 'Requires CIO approval', 'info');
}
script_false: |
// Runs when condition is FALSE (optional)
g_form.hideFieldMsg('amount');
Step 5.2: Script Examples
Complex Validation:
// script_true
var hardware = g_form.getValue('hardware_type');
var quantity = parseInt(g_form.getValue('quantity')) || 0;
// Show warning for bulk orders
if (hardware == 'laptop' && quantity > 10) {
g_form.showFieldMsg('quantity', 'Bulk orders require procurement approval', 'warning');
}
// Dynamic mandatory based on multiple fields
if (hardware == 'other') {
g_form.setMandatory('other_description', true);
}
Date Validation:
// script_true
var startDate = g_form.getValue('start_date');
var endDate = g_form.getValue('end_date');
if (startDate && endDate) {
var start = new Date(startDate);
var end = new Date(endDate);
if (end < start) {
g_form.showFieldMsg('end_date', 'End date must be after start date', 'error');
g_form.setMandatory('end_date', true);
}
}
Phase 6: Complete UI Policy Example
Step 6.1: Full Implementation
Scenario: Software request form where:
- If license type is "Enterprise", show cost center and manager approval
- If quantity > 5, require business justification
Step 1: Get variable sys_ids:
Tool: SN-Query-Table
Parameters:
table_name: item_option_new
query: cat_item=[catalog_item_sys_id]
fields: sys_id,name,question_text
limit: 50
Step 2: Create Policy 1 - Enterprise License:
Tool: SN-Create-Record
Parameters:
table_name: catalog_ui_policy
data:
catalog_item: [catalog_item_sys_id]
short_description: "Show approval fields for Enterprise license"
conditions: "license_type=enterprise"
active: true
on_load: true
reverse_if_false: true
order: 100
# Result: policy1_sys_id
Step 3: Create Actions for Policy 1:
Tool: SN-Create-Record
Parameters:
table_name: catalog_ui_policy_action
data:
ui_policy: [policy1_sys_id]
visible: true
mandatory: true
disabled: false
# Result: action1a_sys_id (for cost_center)
Tool: SN-Create-Record
Parameters:
table_name: catalog_ui_policy_action
data:
ui_policy: [policy1_sys_id]
visible: true
mandatory: true
disabled: false
# Result: action1b_sys_id (for manager_approval)
Step 4: Link Actions to Variables:
Tool: SN-Execute-Background-Script
Parameters:
script: |
var links = [
{ action: '[action1a_sys_id]', variable: '[cost_center_var_sys_id]' },
{ action: '[action1b_sys_id]', variable: '[manager_approval_var_sys_id]' }
];
links.forEach(function(link) {
var gr = new GlideRecord('catalog_ui_policy_action');
if (gr.get(link.action)) {
gr.setValue('catalog_variable', 'IO:' + link.variable);
gr.update();
gs.info('Linked: ' + gr.sys_id);
}
});
description: Link enterprise license policy actions
Step 5: Create Policy 2 - Bulk Order:
Tool: SN-Create-Record
Parameters:
table_name: catalog_ui_policy
data:
catalog_item: [catalog_item_sys_id]
short_description: "Require justification for bulk orders (>5)"
conditions: "quantity>5"
active: true
on_load: true
reverse_if_false: true
order: 200
# Result: policy2_sys_id
Step 6: Create and Link Action for Policy 2:
Tool: SN-Create-Record
Parameters:
table_name: catalog_ui_policy_action
data:
ui_policy: [policy2_sys_id]
visible: true
mandatory: true
disabled: false
# Result: action2_sys_id
Tool: SN-Execute-Background-Script
Parameters:
script: |
var gr = new GlideRecord('catalog_ui_policy_action');
if (gr.get('[action2_sys_id]')) {
gr.setValue('catalog_variable', 'IO:[justification_var_sys_id]');
gr.update();
gs.info('Linked bulk order justification action');
}
description: Link bulk order policy action
Phase 7: Testing and Debugging
Step 7.1: Verify UI Policy Configuration
Query all policies for a catalog item:
Tool: SN-Query-Table
Parameters:
table_name: catalog_ui_policy
query: catalog_item=[catalog_item_sys_id]^active=true
fields: sys_id,short_description,conditions,on_load,reverse_if_false,order,run_scripts
limit: 50
Query all actions for a policy:
Tool: SN-Query-Table
Parameters:
table_name: catalog_ui_policy_action
query: ui_policy=[policy_sys_id]
fields: sys_id,catalog_variable,visible,mandatory,disabled
limit: 20
Step 7.2: Debug Action Links
Check if catalog_variable is properly linked:
Tool: SN-Query-Table
Parameters:
table_name: catalog_ui_policy_action
query: ui_policy=[policy_sys_id]
fields: sys_id,catalog_variable.name,visible,mandatory,disabled
limit: 20
If catalog_variable.name is empty, the link failed - run the background script again.
Step 7.3: Testing Checklist
Before Testing:
- All UI policies are active
- All policy actions have linked variables (catalog_variable not empty)
- Order numbers are set correctly (no conflicts)
- reverse_if_false is set appropriately
Test Scenarios:
- On Load: Open form fresh - verify initial visibility
- True Condition: Set values to make condition true - verify actions apply
- False Condition: Change values to make condition false - verify reverse
- Multiple Policies: Test combinations of conditions
- Script Policies: Verify scripts execute without errors
Step 7.4: Common Debug Commands
Find unlinked actions:
Tool: SN-Query-Table
Parameters:
table_name: catalog_ui_policy_action
query: ui_policy.catalog_item=[catalog_item_sys_id]^catalog_variableISEMPTY
fields: sys_id,ui_policy.short_description
limit: 50
Get variable sys_ids by name:
Tool: SN-Query-Table
Parameters:
table_name: item_option_new
query: cat_item=[catalog_item_sys_id]^name=manager_approval
fields: sys_id,name
Tool Usage Summary
| Operation | MCP Tool | Notes |
|---|---|---|
| Create UI Policy | SN-Create-Record | catalog_ui_policy table |
| Create UI Policy Action | SN-Create-Record | catalog_ui_policy_action table |
| Link Action to Variable | SN-Execute-Background-Script | REQUIRED - REST API limitation |
| Query Policies | SN-Query-Table | catalog_ui_policy |
| Query Actions | SN-Query-Table | catalog_ui_policy_action |
| Update Policy | SN-Update-Record | For script_true/script_false |
Best Practices
- Descriptive Names: Use clear short_description for debugging
- Order Carefully: Lower order executes first - avoid conflicts
- Use reverse_if_false: Cleaner than creating two opposing policies
- Test Both States: Always test true AND false conditions
- Minimize Scripts: Use conditions when possible - scripts are slower
- Document Logic: Comment complex conditions in description field
- Batch Link Actions: Use single background script for multiple links
- Verify Links: Always query to confirm catalog_variable is populated
Troubleshooting
UI Policy Not Executing
Symptom: Variable visibility/mandatory doesn't change Causes:
- Policy inactive
- Condition syntax error
- Action not linked to variable Solution:
Tool: SN-Query-Table
Parameters:
table_name: catalog_ui_policy
query: catalog_item=[item_sys_id]
fields: short_description,active,conditions
Check active=true and verify condition syntax.
Action Not Linked (catalog_variable Empty)
Symptom: Policy executes but field doesn't change Cause: REST API limitation - action not properly linked Solution:
Tool: SN-Execute-Background-Script
Parameters:
script: |
var gr = new GlideRecord('catalog_ui_policy_action');
gr.get('[action_sys_id]');
gr.setValue('catalog_variable', 'IO:[variable_sys_id]');
gr.update();
gs.info('Fixed action link');
description: Fix unlinked UI policy action
reverse_if_false Not Working
Symptom: Variable stays visible/mandatory when condition is false Causes:
- reverse_if_false not enabled
- on_load not enabled (only evaluates on change) Solution:
Tool: SN-Update-Record
Parameters:
table_name: catalog_ui_policy
sys_id: [policy_sys_id]
data:
on_load: true
reverse_if_false: true
Script Errors
Symptom: Console errors when form loads, script not executing Cause: JavaScript syntax error in script_true or script_false Solution:
- Check browser console for errors
- Validate JavaScript syntax
- Test script in browser console first
Multiple Policies Conflicting
Symptom: Unpredictable variable behavior Cause: Multiple policies affecting same variable with conflicting actions Solution:
- Query all policies affecting the variable
- Adjust order numbers
- Consider consolidating into single policy with complex condition
Related Skills
catalog/variable-management- Create and configure variablescatalog/item-creation- Full catalog item setupcatalog/approval-workflows- Approval configurationadmin/script-execution- Background script patterns
References
More from happy-technologies-llc/happy-platform-skills
happy-platform-skills
Reusable development patterns and automation recipes for enterprise platforms - 180+ skills across 23 categories
17scheduled-jobs
Comprehensive guide to creating and managing ServiceNow scheduled jobs - run frequencies, conditional execution, performance optimization, error handling, and debugging
4flow-generation
Generate ServiceNow Flow Designer flows from natural language descriptions including triggers, actions, conditions, subflows, approval flows, notification flows, and data manipulation flows
4application-scope
Manage scoped application development including setting application context and update set alignment
4scripted-rest-apis
Comprehensive guide to creating, securing, and testing Scripted REST APIs in ServiceNow for custom integrations and external system connectivity
4automated-testing
Comprehensive Automated Test Framework (ATF) guide for creating, managing, and executing automated tests in ServiceNow
4