skills/catalog/ui-policies/SKILL.md
Complete guide to catalog UI policies including show/hide/mandatory actions, scripted conditions, and REST API limitations with workarounds
npx skillsauth add happy-technologies-llc/happy-servicenow-skills ui-policiesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill covers creating and managing UI policies for Service Catalog items:
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.
catalog_admin or admincatalog/variable-management for variable creation┌─────────────────────────────────────────────────────────┐
│ 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 │
└─────────────────────────────────────────────────────────┘
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
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)
| 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 |
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 |
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
CRITICAL: UI Policy Actions cannot be fully linked to variables via REST API due to ServiceNow limitations. The catalog_variable field requires special handling.
When you create a catalog_ui_policy_action via REST API:
catalog_variable field WILL NOT link properlyWhy This Happens:
catalog_variable field expects a special format: IO:[variable_sys_id]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.
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
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
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):
visible: true in actionreverse_if_false: true on policyMake 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 valuereverse_if_false: true - When condition false, field hidden and not requiredMake 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.
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');
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);
}
}
Scenario: Software request form where:
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
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
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.
Before Testing:
Test Scenarios:
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
| 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 |
Symptom: Variable visibility/mandatory doesn't change Causes:
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.
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
Symptom: Variable stays visible/mandatory when condition is false Causes:
Tool: SN-Update-Record
Parameters:
table_name: catalog_ui_policy
sys_id: [policy_sys_id]
data:
on_load: true
reverse_if_false: true
Symptom: Console errors when form loads, script not executing Cause: JavaScript syntax error in script_true or script_false Solution:
Symptom: Unpredictable variable behavior Cause: Multiple policies affecting same variable with conflicting actions Solution:
catalog/variable-management - Create and configure variablescatalog/item-creation - Full catalog item setupcatalog/approval-workflows - Approval configurationadmin/script-execution - Background script patternstesting
Manage supplier onboarding, qualification, performance monitoring, and offboarding with auditable lifecycle controls
tools
Identify emerging risks, prioritize intake signals, and route candidates into formal GRC risk assessment workflows
documentation
Screen inbound documents for completeness, policy risk, and routing readiness before extraction or case workflows
testing
Generate concise task summaries with status, timeline, blockers, SLA risk, and recommended next actions