Skip to main content
Version: 4.9.0

Service Catalog Variables

Servicenow guide to create Service Catalog Variables and Variable Sets. Creates a Variable(item_option_new) - single variable and Variable Set(item_option_new_set) - reusable collection of variables that can be attached to one or more catalog items.


Catalog Variables Reference

Common Variable Properties

Properties like question, order, mandatory, readOnly, hidden, defaultValue, and role-based access (readRoles, writeRoles, createRoles) are shared across all variable types. Each variable type also has its own additional properties — see the individual API docs in api/service-catalog/variables/ for the complete property reference.

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 Reference

Properties

For the complete property reference including all properties, types, and detailed descriptions, see the variableset-api documentation.

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.


Modifying Existing Variables

When the requirement is to change a variable that already exists on a catalog item (update properties, change its type, or modify its choices), follow the steps below. Do not create a new variable alongside the old one — this leaves duplicate fields on the form.

Step 1 — Query Before Acting

Before writing any code, check whether the variable exists. Query the item_option_new table filtering by cat_item (the catalog item sys_id) and name (the variable's programmatic key):

Query table: item_option_new Filter: cat_item=^name= Fields: sys_id, name, type, active

Use the result to confirm the variable exists and identify its current type.

Step 2 — Add to Working Set

Add the parent catalog item (sc_cat_item) to the working set using add_to_working_set. This generates the Fluent file containing the full catalog item definition, including all its variables.

Step 3 — Choose the Correct Modification Path

ScenarioAction
Changing properties only (label, mandatory, order, etc.) — same typeUpdate properties in-place on the same variable key.
Changing the variable type (e.g., SingleLineTextVariableSelectBoxVariable)Swap the constructor function on the same key. Add type-specific properties, remove incompatible ones.
Narrowing choices on an existing SelectBox/MultipleChoiceKeep all existing choices; set inactive: true on removed ones. Do NOT delete choice keys.
Adding choices to an existing SelectBox/MultipleChoiceAdd new choice keys with appropriate sequence values.

In-Place Type Change (Correct Pattern)

The Fluent build system tracks variables by their key in the variables object. That key IS the variable's name field. When you change the constructor on the same key, the build system updates the existing record's type — it does NOT create a new record.

Before — Single line text:

import { CatalogItem, SingleLineTextVariable } from '@servicenow/sdk/core'

export const hardwareRequest = CatalogItem({
$id: Now.ID['hardware_request'],
name: 'Hardware Request',
shortDescription: 'Request hardware procurement for your team',
variables: {
hardware_type: SingleLineTextVariable({
question: 'Hardware Type',
order: 100,
mandatory: true,
exampleText: 'e.g. Laptop, Desktop, Monitor',
}),
},
})

**After — Select box with choices:

import { CatalogItem, SelectBoxVariable } from '@servicenow/sdk/core'

export const hardwareRequest = CatalogItem({
$id: Now.ID['hardware_request'],
name: 'Hardware Request',
shortDescription: 'Request hardware procurement for your team',
variables: {
// Same key = same record, type is updated in-place
hardware_type: SelectBoxVariable({
question: 'Hardware Type',
order: 100,
mandatory: true,
choices: {
laptop: { label: 'Laptop', sequence: 1 },
desktop: { label: 'Desktop', sequence: 2 },
monitor: { label: 'Monitor', sequence: 3 },
},
includeNone: true,
}),
},
})

Managing Choices on Existing Variables When reducing the available options on a SelectBox or MultipleChoice variable, never delete choice keys from the source. This triggers deletedRecordWarnings requiring manual cleanup.

✅ Correct — deactivate unwanted choices:

hardware_type: SelectBoxVariable({
question: 'Hardware Type',
order: 100,
mandatory: true,
choices: {
// Active choices — visible to users
laptop: { label: 'Laptop', sequence: 1 },
desktop: { label: 'Desktop', sequence: 2 },
monitor: { label: 'Monitor', sequence: 3 },
// Retired choices — hidden but preserved for historical data
docking_station: { label: 'Docking Station', sequence: 4, inactive: true },
keyboard_mouse: { label: 'Keyboard/Mouse', sequence: 5, inactive: true },
mobile_phone: { label: 'Mobile Phone', sequence: 6, inactive: true },
},
includeNone: true,
})

❌ Wrong — removing choice keys entirely:

// Causes deletedRecordWarnings and breaks historical data
hardware_type: SelectBoxVariable({
question: 'Hardware Type',
choices: {
laptop: { label: 'Laptop', sequence: 1 },
desktop: { label: 'Desktop', sequence: 2 },
monitor: { label: 'Monitor', sequence: 3 },
// docking_station, keyboard_mouse, mobile_phone REMOVED — BAD
},
})

Examples

Variable Examples

Text variables:

import { SingleLineTextVariable, MultiLineTextVariable, EmailVariable, MaskedVariable } from '@servicenow/sdk/core'

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:

import { SelectBoxVariable, MultipleChoiceVariable, CheckboxVariable } from '@servicenow/sdk/core'

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:

import { ReferenceVariable, ListCollectorVariable } from '@servicenow/sdk/core'

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
});

Dynamic defaults with dependentQuestion:

Use dependentQuestion to auto-populate reference variables based on another variable's value. When the source variable changes, the dependent variable automatically updates by following the dotWalkPath from the source record.

This is commonly used with RequestedForVariable to auto-fill organizational context (manager, department, location, cost center) when the user selects who the request is for.

PropertyTypeDescription
dependentQuestionstringName of the source variable (must be RequestedForVariable or ReferenceVariable).
dotWalkPathstringDot-walk path from the source record to get the value (e.g., 'manager', 'department', 'location.building').
useDynamicDefaultbooleanMust be true to enable dynamic defaults.
import { CatalogItem, RequestedForVariable, ReferenceVariable } from '@servicenow/sdk/core'

export const accessRequest = CatalogItem({
$id: Now.ID['access_request'],
name: 'Access Request',
shortDescription: 'Request system access for a user',
variables: {
// Source variable — user selects who needs access
requestedFor: RequestedForVariable({
question: 'Who needs access?',
order: 100,
mandatory: true,
referenceQualCondition: 'active=true',
}),

// Auto-populated from requestedFor.manager
manager: ReferenceVariable({
question: 'Manager (auto-filled)',
order: 200,
referenceTable: 'sys_user',
referenceQualCondition: 'active=true',
useDynamicDefault: true,
dotWalkPath: 'manager',
dependentQuestion: 'requestedFor',
}),

// Auto-populated from requestedFor.department
department: ReferenceVariable({
question: 'Department (auto-filled)',
order: 300,
referenceTable: 'cmn_department',
referenceQualCondition: 'active=true',
useDynamicDefault: true,
dotWalkPath: 'department',
dependentQuestion: 'requestedFor',
}),

// Auto-populated from requestedFor.location
location: ReferenceVariable({
question: 'Location (auto-filled)',
order: 400,
referenceTable: 'cmn_location',
referenceQualCondition: 'active=true',
useDynamicDefault: true,
dotWalkPath: 'location',
dependentQuestion: 'requestedFor',
}),
},
})

dependentQuestion rules:

  • The source variable (dependentQuestion) must be either RequestedForVariable or ReferenceVariable
  • The source variable must exist in the same variables object
  • useDynamicDefault must be true on the dependent variable
  • dotWalkPath specifies the field path from the source record (supports dot-walking like 'location.building')
  • The dependent variable's referenceTable should match the type returned by the dot-walk path

Container layout (multi-column):

Container variables must follow the exact triplet order: ContainerStartVariable → variables → ContainerSplitVariable → variables → ContainerEndVariable. Every start must have exactly one split and one end. Containers are not supported in multiRow variable sets.

import { ContainerStartVariable, ContainerSplitVariable, ContainerEndVariable, SingleLineTextVariable, EmailVariable } from '@servicenow/sdk/core'

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

import { CheckboxVariable, SelectBoxVariable } from '@servicenow/sdk/core'

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)

import { VariableSet, ReferenceVariable, SelectBoxVariable, DateVariable } from '@servicenow/sdk/core'

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

import { CatalogItem, MultiLineTextVariable } from '@servicenow/sdk/core'
import { contactInfoSet } from './variable-sets/contact-info-set'
import { teamMembersSet } from './variable-sets/team-members-set'
import { serviceCatalog } from './catalogs/service-catalog'
import { itServicesCategory } from './categories/it-services'

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"
});