Skip to main content
Version: 4.6.0

Service Catalog Variables

API reference and patterns for Service Catalog variables, variable sets, UI policies, and client scripts. For catalog items and record producers, see service-catalog-guide.md. Requires SDK 4.3.0 or higher.


Catalog Variables API Reference

Common Variable Properties

PropertyTypeDescription
questionstringRequired. Label text displayed to user.
ordernumberDisplay order (use increments of 100).
mandatorybooleanWhether field is required. Default: false.
readOnlybooleanWhether field is editable. Default: false.
hiddenbooleanWhether field is visible. Default: false.
tooltipstringHover help text.
exampleTextstringPlaceholder text.
instructionsstringInline help text.
defaultValuestringPre-filled value.
width25 | 50 | 75 | 100Field width percentage.
readRolesstring[]Roles that can read the variable.
writeRolesstring[]Roles that can write to the variable.
mapToFieldbooleanMap to target table field (Record Producers).
fieldstringTarget field name when mapToField is true.

Variable Types

Text Variables

  • SingleLineTextVariable -- Single line text input
  • MultiLineTextVariable -- Multi-line text area
  • WideSingleLineTextVariable -- Full-width single line
  • EmailVariable -- Email address input
  • UrlVariable -- URL input
  • IpAddressVariable -- IPv4/IPv6 input
  • MaskedVariable -- Masked/password input (supports useEncryption, useConfirmation)

Choice Variables

  • SelectBoxVariable -- Dropdown choice list. Requires choices object with { label, sequence }.
  • MultipleChoiceVariable -- Radio buttons. Supports choiceDirection: 'down' or 'across'.
  • YesNoVariable -- Yes/No choice list.
  • CheckboxVariable -- Checkbox. Use selectionRequired: true for mandatory.
  • NumericScaleVariable -- Likert scale radio buttons.

Lookup Variables

  • LookupSelectBoxVariable -- Dropdown from table data.
  • LookupMultipleChoiceVariable -- Radio buttons from table data.

Reference Variables

  • ReferenceVariable -- References a record in another table. Key properties: referenceTable, referenceQualCondition, useReferenceQualifier.
  • RequestedForVariable -- Specifies who the request is for.
  • ListCollectorVariable -- Select multiple records from a table.

Date/Time Variables

  • DateVariable -- Date picker.
  • DateTimeVariable -- Date and time picker.
  • DurationVariable -- Duration input.

Layout Variables

  • ContainerStartVariable / ContainerSplitVariable / ContainerEndVariable -- Multi-column layout containers. Must be properly paired.
  • LabelVariable -- Display-only label.
  • BreakVariable -- Horizontal line separator.

Special Variables

  • AttachmentVariable -- File upload.
  • HtmlVariable -- Rich content display.
  • RichTextLabelVariable -- Formatted label.
  • CustomVariable / CustomWithLabelVariable -- UI macro insertion.
  • UIPageVariable -- UI page insertion.

Variable Set API Reference

Properties

PropertyTypeDescription
$idNow.ID[string]Required. Unique identifier.
titlestringRequired. Display title.
internalNamestringInternal name. Auto-generated from title if not provided.
descriptionstringDescription of the variable set.
type'singleRow' | 'multiRow'Default: 'singleRow'.
layout'normal' | '2down' | '2across'Default: 'normal'.
ordernumberDisplay order. Default: 100.
displayTitlebooleanShow collapsible section header. Default: false.
setAttributesstringAdditional config (e.g., "max_rows=10,collapsible=true").
readRolesstring[]Roles that can view the variable set.
writeRolesstring[]Roles that can modify values.
createRolesstring[]Roles that can create instances (for multiRow).
variablesobjectVariable definitions for the set.

Attaching to Catalog Items

Attach variable sets via variableSets: [{ variableSet, order }] on a Catalog Item or Record Producer. Item-specific variables can be added alongside variable sets.

Multi-Row Variable Set (MRVS)

Use type: "multiRow" for grid/table data entry (e.g., multiple team members). Configure with setAttributes for row limits and collapsibility.

MRVS Unsupported Variable Types

  • AttachmentVariable
  • ContainerStartVariable / ContainerEndVariable / ContainerSplitVariable
  • HtmlVariable
  • CustomVariable / CustomWithLabelVariable
  • RichTextLabelVariable
  • UIPageVariable

MRVS Limitations

  • "Assign to Field" not supported
  • Cannot add variables with read roles
  • Set row limits using max_rows attribute
  • Will not display if added to a container

Role-Based Access

  • readRoles: Roles that can view the variable set
  • writeRoles: Roles that can modify values
  • createRoles: Roles that can create instances (multiRow)

Set-level permissions override variable-level permissions when access is denied at the set level.


Catalog UI Policy API Reference

Properties

PropertyTypeDescription
$idNow.ID[string]Required. Unique identifier.
shortDescriptionstringRequired. Description of what the policy does.
catalogItemrefRequired if not using variableSet.
variableSetrefRequired if not using catalogItem.
appliesTo'item' | 'set'Required if using variableSet. Default: 'item'.
activebooleanWhether the policy is active. Default: true.
onLoadbooleanRun on form load. Default: true.
reverseIfFalsebooleanReverse actions when condition is false. Default: true.
catalogConditionstringCondition using encoded query syntax.
appliesOnCatalogItemViewbooleanApplies to catalog item view. Default: true.
appliesOnTargetRecordbooleanApplies to target record. Default: false.
appliesOnCatalogTasksbooleanApplies to catalog tasks. Default: false.
appliesOnRequestedItemsbooleanApplies to requested items. Default: false.
runScriptsbooleanExecute client scripts. Default: false.
executeIfTruestringScript when condition is true.
executeIfFalsestringScript when condition is false.
runScriptsInUiType'desktop' | 'mobileOrServicePortal' | 'all'Default: 'desktop'.
actionsarrayList of variable actions.

Action Properties

PropertyTypeDescription
variableNamerefRequired. Variable reference.
visiblebooleanShow/hide the variable.
mandatorybooleanMake variable required.
readOnlybooleanMake variable read-only.
valuestringValue to set.
valueAction'clearValue' | 'setValue'How to apply the value.
ordernumberExecution order. Default: 100.
variableMessagestringMessage to display on the field.
variableMessageType'info' | 'warning' | 'error'Message severity.

Condition Syntax

// Simple condition
catalogCondition: `${catalogItem.variables.priority}=high^EQ`;

// Multiple conditions with AND
catalogCondition: `${catalogItem.variables.env}=prod^${catalogItem.variables.critical}=true^EQ`;

// Multiple conditions with OR
catalogCondition: `${catalogItem.variables.env}=prod^OR${catalogItem.variables.critical}=true^EQ`;

// Not empty check
catalogCondition: `${catalogItem.variables.reference}ISNOTEMPTY^EQ`;

Priority Rules

  1. Mandatory has highest priority
  2. If a variable is mandatory and has no value, readonly/hide actions do not work
  3. If a variable set/container has a mandatory variable without value, the entire set cannot be hidden
  4. "Clear value" action does not work on variable sets and containers

Variable Type Limitations

Policy TypeNot Applicable To
MandatoryFraction, Container Split, Container End, UI Macro, Label, UI Page
Read-onlyFraction, Container Split, Container End, UI Macro, Label, UI Page
VisibilityFraction, Container Split, Container End

Policy with Client Scripts

Set runScripts: true and provide executeIfTrue / executeIfFalse scripts via Now.include(...). These scripts run client-side in the browser where modules are not available, so Now.include() is the correct approach. Scripts must be wrapped in function onCondition() {}.


Catalog Client Script API Reference

Properties

PropertyTypeDescription
$idNow.ID[string]Required. Unique identifier.
namestringRequired. Name of the script.
scriptstringInline script or Now.include() reference. These are client-side scripts — modules are not available.
type'onLoad' | 'onChange' | 'onSubmit'Script trigger type.
uiType'desktop' | 'mobileOrServicePortal' | 'all'Default: 'desktop'.
activebooleanWhether the script is enabled. Default: true.
catalogItemrefRequired if not using variableSet.
variableSetrefRequired if not using catalogItem.
appliesTo'item' | 'set'Required if using variableSet. Default: 'item'.
variableNamerefRequired for onChange. The variable that triggers the script.
appliesOnCatalogItemViewbooleanApplies on catalog item view. Default: true.
appliesOnRequestedItemsbooleanApplies on requested items. Default: false.
appliesOnCatalogTasksbooleanApplies on catalog tasks. Default: false.
appliesOnTargetRecordbooleanApplies on target record. Default: false.

Script Types

onLoad -- Runs when the form loads. Use for initial setup (field states, defaults, visibility).

onChange -- Runs when a specific variable changes. Always guard with if (isLoading) return; to prevent execution during form load.

onSubmit -- Runs on form submission. Return false to block submission. Avoid GlideAjax here -- async calls will not complete before the form submits.

g_form API Reference

MethodDescription
getValue(fieldName)Get variable value
setValue(fieldName, value)Set variable value
setDisplay(fieldName, display)Show/hide variable
setMandatory(fieldName, mandatory)Set mandatory state
setReadOnly(fieldName, readOnly)Set read-only state
clearValue(fieldName)Clear variable value
hasField(fieldName)Check if field exists
showFieldMsg(fieldName, message, type, scrollForm)Show field message
hideFieldMsg(fieldName, clearAll)Hide field message
addErrorMessage(message)Add banner error message
clearOptions(fieldName)Clear all select options
addOption(fieldName, value, label)Add a select option
getReference(fieldName, callback)Get referenced record (legacy)

Note on getReference: Legacy convenience method. Works for simple lookups but GlideAjax is preferred for complex server-side logic. May make synchronous calls in some versions, which can freeze the UI.

Catalog Client Script vs Standard Client Script

AspectCatalog Client ScriptStandard Client Script
ScopeCatalog item or variable setTable (e.g., Incident)
onChange targetLinks to a variableLinks to a field
ContextCatalog ordering, RITM, Catalog Task formsTable forms
Variable accessDirect by variable nameUse variables.variable_name prefix
Applies toitem or setSpecific table

Scripts on Variable Sets

Scope scripts to a variable set using variableSet and appliesTo: 'set' so they apply to all catalog items using that set. Always use hasField() checks since the variable may not exist on every item that includes the set.

When multiple variable sets are attached to a catalog item, scripts execute in the order the variable sets are listed on the item. If both a variable set script and an item-level script target the same variable, the item-level script runs last and takes precedence.

GlideAjax

Use GlideAjax to call server-side Script Includes from client scripts. The client sends a request, the Script Include processes it, and returns a result via a callback.

Method comparison:

MethodExecutionUse WhenAvoid When
getXMLAnswer()AsyncSimple lookups, returning a single value/stringYou need the full XML response object
getXML()AsyncNeed full XML response, complex response parsingSimple value returns (use getXMLAnswer)
getXMLWait()SyncAlmost never -- legacy/global scope onlyScoped apps, any production code

Parameter rules: All custom parameters must start with sysparm_. The first addParam call must always be sysparm_name with the method name.

ga.addParam("sysparm_name", "methodName"); // REQUIRED: always first
ga.addParam("sysparm_user_id", userSysId); // Custom param: prefix with sysparm_

Script Include (Server-Side Companion)

Every GlideAjax call requires a corresponding Script Include on the server. The Script Include must extend AbstractAjaxProcessor and be marked Client Callable.

PropertyValue
NameMust match the string in new GlideAjax('ClassName')
Client callableChecked (required for GlideAjax access)
Extendsglobal.AbstractAjaxProcessor
Retrieve paramsUse this.getParameter('sysparm_param_name')
Return dataUse return (simple string) or return JSON.stringify(obj) for objects

Security: Client callable Script Includes run in the logged-in user's session context. ACLs still apply to GlideRecord queries. Always validate parameters from this.getParameter(). Never trust client-side input.


Examples

Variable Examples

Text variables:

SingleLineTextVariable({ question: "Employee Name", order: 100, mandatory: true, exampleText: "John Smith" });
MultiLineTextVariable({ question: "Description", order: 200, mandatory: true, width: 100 });
EmailVariable({ question: "Email Address", order: 300, mandatory: true });
MaskedVariable({ question: "Enter Password", order: 400, useEncryption: true });

Choice variables:

SelectBoxVariable({
question: "Priority Level",
order: 100,
choices: {
high: { label: "High", sequence: 1 },
medium: { label: "Medium", sequence: 2 },
low: { label: "Low", sequence: 3 }
},
includeNone: true
});

MultipleChoiceVariable({
question: "Services Required",
choiceDirection: "down",
choices: {
install: { label: "Installation", sequence: 1 },
config: { label: "Configuration", sequence: 2 },
training: { label: "Training", sequence: 3 }
},
order: 200
});

CheckboxVariable({ question: "I agree to the terms", order: 400, selectionRequired: true });

Reference variables:

ReferenceVariable({
question: "Point of Contact",
referenceTable: "sys_user",
referenceQualCondition: "active=true",
order: 100
});

ListCollectorVariable({
question: "Team Members",
listTable: "sys_user",
referenceQual: "active=true",
order: 300,
mandatory: true
});

Container layout (multi-column):

variables: {
contact_container_start: ContainerStartVariable({
question: "Contact Information",
layout: "2across",
displayTitle: true,
order: 100
}),
first_name: SingleLineTextVariable({
question: "First Name",
mandatory: true,
order: 110
}),
contact_split: ContainerSplitVariable({ order: 200 }),
email: EmailVariable({
question: "Email Address",
mandatory: true,
order: 210
}),
contact_container_end: ContainerEndVariable({ order: 300 })
}

Variables with pricing:

premiumSupport: CheckboxVariable({
question: "Premium Support (+$150)",
pricingDetails: [
{ amount: 150, currencyType: "USD", field: "price_if_checked" },
{ amount: 30, currencyType: "USD", field: "rec_price_if_checked" }
],
order: 500
});

hardwareType: SelectBoxVariable({
question: "Hardware Type",
choices: {
laptop: {
label: "Business Laptop (Base)",
sequence: 1,
pricingDetails: [{ amount: 0, currencyType: "USD", field: "misc" }]
},
workstation: {
label: "Developer Workstation (+$800)",
sequence: 2,
pricingDetails: [{ amount: 800, currencyType: "USD", field: "misc" }]
}
},
mandatory: true,
order: 600
});

Single-Row Variable Set

import { VariableSet, EmailVariable, SingleLineTextVariable, ReferenceVariable } from "@servicenow/sdk/core";

export const contactInfoSet = VariableSet({
$id: Now.ID["contact_info_set"],
title: "Contact Information",
description: "Standard contact information fields",
type: "singleRow",
layout: "2across",
order: 100,
displayTitle: true,
variables: {
email: EmailVariable({ question: "Email Address", mandatory: true, order: 100 }),
phone: SingleLineTextVariable({ question: "Phone Number", mandatory: true, order: 200 }),
department: ReferenceVariable({
question: "Department",
referenceTable: "cmn_department",
referenceQualCondition: "active=true",
order: 300
})
}
});

Multi-Row Variable Set (MRVS)

export const teamMembersSet = VariableSet({
$id: Now.ID["team_members_set"],
title: "Team Members",
description: "Add multiple team members who need access",
type: "multiRow",
layout: "2across",
displayTitle: true,
setAttributes: "max_rows=10,collapsible=true",

readRoles: ["admin", "manager"],
writeRoles: ["admin"],

variables: {
user: ReferenceVariable({
question: "User",
referenceTable: "sys_user",
referenceQualCondition: "active=true",
mandatory: true,
order: 100
}),
accessLevel: SelectBoxVariable({
question: "Access Level",
choices: {
read: { label: "Read Only", sequence: 1 },
write: { label: "Write", sequence: 2 },
admin: { label: "Admin", sequence: 3 }
},
mandatory: true,
order: 200
}),
startDate: DateVariable({ question: "Access Start Date", mandatory: true, order: 300 })
}
});

Attaching Variable Sets to a Catalog Item

export const accessRequest = CatalogItem({
$id: Now.ID["access_request"],
name: "Team Access Request",
shortDescription: "Request access for team members",
catalogs: [serviceCatalog],
categories: [itServicesCategory],

variableSets: [
{ variableSet: contactInfoSet, order: 100 },
{ variableSet: teamMembersSet, order: 200 }
],

variables: {
notes: MultiLineTextVariable({ question: "Additional Notes", order: 100 })
},

flow: "523da512c611228900811a37c97c2014"
});

Catalog UI Policy -- Show/Hide Based on Condition

import { CatalogUiPolicy } from "@servicenow/sdk/core";
import { hardwareRequestItem } from "./catalog-items/HardwareRequest.now";

export const managerApprovalPolicy = CatalogUiPolicy({
$id: Now.ID["manager_approval_policy"],
shortDescription: "Show manager approval when high priority selected",
catalogItem: hardwareRequestItem,
catalogCondition: `${hardwareRequestItem.variables.priority}=high^EQ`,
actions: [
{
variableName: hardwareRequestItem.variables.manager_approval,
visible: true,
mandatory: true
}
]
});

Catalog UI Policy -- Read-Only with Value

export const readOnlyPolicy = CatalogUiPolicy({
$id: Now.ID["readonly_license_policy"],
shortDescription: "Make license type read-only for standard software",
catalogItem: softwareRequestItem,
catalogCondition: `${softwareRequestItem.variables.software_type}=standard^EQ`,
actions: [
{
variableName: softwareRequestItem.variables.license_type,
readOnly: true,
value: "standard_license",
valueAction: "setValue"
}
]
});

Catalog UI Policy -- With Client Scripts

CatalogUiPolicy({
$id: Now.ID["vm_prod_controls_policy"],
shortDescription: "VM: Prod/BizCritical Controls",
catalogItem: cloudVmRequest,
catalogCondition: `${cloudVmRequest.variables.environment}=prod^OR${cloudVmRequest.variables.business_critical}=true^EQ`,
active: true,
onLoad: true,
reverseIfFalse: true,
runScripts: true,
runScriptsInUiType: "all",

actions: [
{
variableName: cloudVmRequest.variables.backup_required,
value: "true",
valueAction: "setValue",
readOnly: true,
order: 100
},
{
variableName: cloudVmRequest.variables.cost_center,
mandatory: true,
order: 200
}
],

executeIfTrue: Now.include("../../scripts/vm-production-controls.js"),
executeIfFalse: Now.include("../../scripts/vm-development-controls.js")
});

vm-production-controls.js:

function onCondition() {
var PROD_REGIONS = [
["AP-South-1", "AP-South-1 (Mumbai)"],
["EU-West-1", "EU-West-1 (Ireland)"]
];

g_form.clearOptions("region");
PROD_REGIONS.forEach(function (pair) {
g_form.addOption("region", pair[0], pair[1]);
});

g_form.showFieldMsg(
"environment",
"Production VMs enforce backup and require cost center.",
"info"
);
}

Catalog UI Policy -- Applied to Variable Set

export const internationalShippingPolicy = CatalogUiPolicy({
$id: Now.ID["international_shipping_policy"],
shortDescription: "Show customs fields for international shipping",
variableSet: shippingVariableSet,
appliesTo: "set",
catalogCondition: `${shippingVariableSet.variables.shipping_country}!=US^EQ`,
appliesOnCatalogItemView: true,
appliesOnRequestedItems: true,
actions: [
{
variableName: shippingVariableSet.variables.customs_declaration,
visible: true,
mandatory: true,
variableMessage: "Required for international shipping",
variableMessageType: "warning"
}
]
});

Catalog Client Script -- onLoad

import { CatalogClientScript } from "@servicenow/sdk/core";
import { laptopRequest } from "../catalog-items/laptop-request.now";

CatalogClientScript({
$id: Now.ID["laptop_onload"],
name: "Laptop Request - OnLoad",
script: Now.include("../../client/laptop-onload.js"),
type: "onLoad",
catalogItem: laptopRequest,
active: true,
appliesOnCatalogItemView: true
});

laptop-onload.js:

function onLoad() {
g_form.setReadOnly("estimated_cost", true);
g_form.setValue("estimated_cost", "$0");
g_form.setMandatory("justification", true);
}

Catalog Client Script -- onChange

CatalogClientScript({
$id: Now.ID["laptop_type_change"],
name: "Laptop Type - onChange",
script: Now.include("../../client/laptop-type-change.js"),
type: "onChange",
catalogItem: laptopRequest,
variableName: laptopRequest.variables.laptopType,
active: true
});

laptop-type-change.js:

function onChange(control, oldValue, newValue, isLoading) {
if (isLoading) return; // Always guard against initial load

if (newValue === "developer") {
g_form.setDisplay("accessories", true);
} else {
g_form.setDisplay("accessories", false);
g_form.clearValue("accessories");
}
}

Catalog Client Script -- onSubmit Validation

CatalogClientScript({
$id: Now.ID["laptop_validation"],
name: "Laptop Request - Validation",
script: Now.include("../../client/laptop-validation.js"),
type: "onSubmit",
catalogItem: laptopRequest,
active: true
});

laptop-validation.js:

function onSubmit() {
var justification = (g_form.getValue("justification") || "").trim();

if (justification.length < 20) {
g_form.showFieldMsg("justification", "Please provide at least 20 characters.", "error", true);
g_form.addErrorMessage("Justification is too short.");
return false;
}

return true;
}

Catalog Client Script -- onChange with GlideAjax

CatalogClientScript({
$id: Now.ID["asset_tag_lookup"],
name: "Asset Tag - Warranty Lookup",
script: Now.include("../../client/asset-tag-lookup.js"),
type: "onChange",
catalogItem: equipmentRepairItem,
variableName: equipmentRepairItem.variables.asset_tag,
active: true
});

asset-tag-lookup.js:

function onChange(control, oldValue, newValue, isLoading) {
if (isLoading) return;

if (!newValue) {
g_form.clearValue("warranty_status");
return;
}

var ga = new GlideAjax("global.AssetLookupUtil");
ga.addParam("sysparm_name", "getWarrantyStatus");
ga.addParam("sysparm_asset_tag", newValue);
ga.getXMLAnswer(function (response) {
if (!response) return;
var info = JSON.parse(response);
g_form.setValue("warranty_status", info.status);
});
}

Catalog Client Script -- Scoped to Variable Set

import { requesterInfoSet } from "./variable-sets/requester-info-set.now";

CatalogClientScript({
$id: Now.ID["department_change_script"],
name: "Department Change - Clear Manager",
type: "onChange",
variableSet: requesterInfoSet,
appliesTo: "set",
variableName: requesterInfoSet.variables.department,
script: Now.include("../../client/department-change.js"),
active: true,
uiType: "all"
});

department-change.js:

function onChange(control, oldValue, newValue, isLoading) {
if (isLoading) return;
g_form.clearValue("manager");
if (!newValue) return;
g_form.showFieldMsg("manager", "Please select a manager from the new department", "info", false);
}

GlideAjax -- Dynamic Options Based on Selection

Client script (onChange on 'department' variable):

function onChange(control, oldValue, newValue, isLoading) {
if (isLoading) return;

g_form.clearOptions("category");
g_form.addOption("category", "", "-- Select --");

if (!newValue) return;

var ga = new GlideAjax("CatalogOptionLoader");
ga.addParam("sysparm_name", "getCategoriesByDept");
ga.addParam("sysparm_department", newValue);

ga.getXMLAnswer(function (answer) {
if (!answer) return;
var categories = JSON.parse(answer);
categories.forEach(function (cat) {
g_form.addOption("category", cat.value, cat.label);
});
});
}

Script Include (CatalogOptionLoader, Client callable = true):

var CatalogOptionLoader = Class.create();
CatalogOptionLoader.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {
getCategoriesByDept: function () {
var deptId = this.getParameter("sysparm_department");
var categories = [];

var gr = new GlideRecord("sc_category");
gr.addQuery("department", deptId);
gr.addQuery("active", true);
gr.orderBy("title");
gr.query();

while (gr.next()) {
categories.push({ value: gr.getUniqueValue(), label: gr.getValue("title") });
}
return JSON.stringify(categories);
},

type: "CatalogOptionLoader"
});

GlideAjax -- Server-Side Validation (getXML)

Client script (onChange on 'asset_tag' variable):

function onChange(control, oldValue, newValue, isLoading) {
if (isLoading) return;
g_form.hideFieldMsg("asset_tag", true);

if (!newValue) {
g_form.clearValue("configuration_item");
return;
}

var ga = new GlideAjax("AssetValidator");
ga.addParam("sysparm_name", "validateAssetTag");
ga.addParam("sysparm_asset_tag", newValue);

ga.getXML(function (response) {
var answer = response.responseXML.documentElement.getAttribute("answer");
if (!answer) {
g_form.showFieldMsg("asset_tag", "Unable to validate. Try again.", "error");
return;
}

var result = JSON.parse(answer);
if (result.found) {
g_form.setValue("configuration_item", result.ci_sys_id);
g_form.showFieldMsg("asset_tag", "Found: " + result.ci_name, "info");
} else {
g_form.clearValue("configuration_item");
g_form.showFieldMsg("asset_tag", "Asset tag not found in CMDB.", "error");
}
});
}

Script Include (AssetValidator, Client callable = true):

var AssetValidator = Class.create();
AssetValidator.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {
validateAssetTag: function () {
var assetTag = this.getParameter("sysparm_asset_tag");

if (!assetTag) {
return JSON.stringify({ found: false, error: "No asset tag provided" });
}

var gr = new GlideRecord("cmdb_ci");
gr.addQuery("asset_tag", assetTag);
gr.setLimit(1);
gr.query();

if (gr.next()) {
return JSON.stringify({
found: true,
ci_sys_id: gr.getUniqueValue(),
ci_name: gr.getDisplayValue("name"),
ci_class: gr.getDisplayValue("sys_class_name")
});
}
return JSON.stringify({ found: false });
},

type: "AssetValidator"
});

Script Include -- Multi-Method Pattern

var CatalogUtils = Class.create();
CatalogUtils.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {
getItemPrice: function () {
var itemId = this.getParameter("sysparm_item_id");
var gr = new GlideRecord("sc_cat_item");
if (gr.get(itemId)) {
return gr.getValue("price");
}
return "0";
},

getManagerName: function () {
var userId = this.getParameter("sysparm_user_id");
var gr = new GlideRecord("sys_user");
if (gr.get(userId)) {
return JSON.stringify({
manager_sys_id: gr.getValue("manager"),
manager_name: gr.getDisplayValue("manager"),
department: gr.getDisplayValue("department")
});
}
return JSON.stringify({ error: "User not found" });
},

type: "CatalogUtils"
});

Script Include -- Input Validation

getUserInfo: function() {
var userId = this.getParameter('sysparm_user_id');

// Validate: check it looks like a sys_id
if (!userId || userId.length !== 32) {
return JSON.stringify({ error: 'Invalid user ID' });
}

var gr = new GlideRecord('sys_user');
if (gr.get(userId)) {
return JSON.stringify({ name: gr.getDisplayValue('name') });
}
return JSON.stringify({ error: 'User not found' });
}