ServiceNow
Background Scripts — 20 Interview Examples
JavaScript (Rhino/ES5) | GlideRecord | Server-Side APIs | All Scopes
[ GlideRecord · Query ]
01.
Query Incidents by State
Fetch all Active (state=1) P1 incidents and log their numbers. This is the most fundamental GlideRecord pattern
tested in every ServiceNow interview.
var gr = new GlideRecord('incident');
[Link]('state', 1); // Active
[Link]('priority', 1); // P1
[Link]('sys_created_on');
[Link]();
while ([Link]()) {
[Link]('Incident: ' + [Link] +
' | Caller: ' + gr.caller_id.getDisplayValue());
}
■ Tip: Always use [Link]() in a while loop — never for..in on a GlideRecord.
[ GlideRecord · Insert ]
02.
Create a New Incident Programmatically
Insert a new Incident record using [Link](). Asked to test knowledge of record creation without using the
UI.
var gr = new GlideRecord('incident');
[Link]();
gr.short_description = 'Server CPU > 90%';
[Link] = 'hardware';
[Link] = 2; // High
[Link] = 1;
[Link] = 1;
gr.caller_id.setDisplayValue('John Smith');
var sysId = [Link]();
[Link]('Created incident sys_id: ' + sysId);
■ Tip: [Link]() clears default values; always call it before setting fields on a new record.
[ GlideRecord · Update ]
03.
Mass Update Records Matching a Condition
Update the assigned_to field on all unassigned open incidents. Tests bulk update patterns and field setting.
var gr = new GlideRecord('incident');
[Link]('state', 1);
[Link]('assigned_to');
[Link]();
while ([Link]()) {
gr.assigned_to.setDisplayValue('Help Desk');
gr.work_notes = 'Auto-assigned by background script';
[Link]();
}
[Link]('Records updated: ' + [Link]());
■ Tip: Use [Link]('field') to find records where a field is empty.
[ GlideRecord · Delete ]
04.
Delete Old Test / Duplicate Records
Safely delete records that match a specific condition. Interviewers check that candidates avoid deleting without a
WHERE clause.
var gr = new GlideRecord('incident');
[Link]('short_description',
'STARTSWITH', 'TEST - ');
[Link]('state', 7); // Closed
[Link]();
var count = 0;
while ([Link]()) {
[Link]();
count++;
}
[Link]('Deleted ' + count + ' test records.');
■ Tip: Never call [Link]() unless you are certain — it bypasses Business Rules.
[ GlideAggregate ]
05.
Count Records by Category
Use GlideAggregate to count open incidents grouped by category — much faster than iterating all records.
var ga = new GlideAggregate('incident');
[Link]('state', 1);
[Link]('COUNT', 'category');
[Link]('category');
[Link]('COUNT', 'category');
[Link]();
while ([Link]()) {
[Link](
[Link] + ': ' +
[Link]('COUNT', 'category')
);
}
■ Tip: GlideAggregate is far more efficient than GlideRecord for COUNT/SUM/AVG operations.
[ GlideDateTime ]
06.
Calculate SLA Breach Countdown
Compute how many hours remain before an SLA breaches using GlideDateTime arithmetic.
var gr = new GlideRecord('incident');
[Link]('state', 1);
[Link]();
var now = new GlideDateTime();
while ([Link]()) {
var due = new GlideDateTime([Link]('due_date'));
var diff = [Link](now, due);
var hours = [Link]() * 24
+ [Link]();
if (hours < 4) {
[Link]('[WARN] ' + [Link] +
' breaches in < 4 hours!');
}
}
■ Tip: Always use GlideDateTime for date math — never JavaScript Date() in ServiceNow scripts.
[ [Link] / [Link] ]
07.
Read & Write System Properties
Access and modify System Properties (sys_properties) used as feature flags or config values.
// Read a system property
var maxRetries = [Link](
'[Link].max_retries', '3'
);
[Link]('Max retries: ' + maxRetries);
// Write / update a system property
[Link](
'[Link].last_run',
new GlideDateTime().getDisplayValue()
);
[Link]('Property updated successfully.');
■ Tip: Always provide a default value as the 2nd arg to [Link]() to avoid null errors.
[ RESTMessageV2 ]
08.
Call an External REST API
Make an outbound HTTP GET request to an external API using RESTMessageV2 — essential for integrations.
var rm = new sn_ws.RESTMessageV2();
[Link]('GET');
[Link](
'[Link]
);
[Link]('Authorization',
'Bearer ' + [Link]('myapp.api_token'));
[Link]('Content-Type', 'application/json');
var response = [Link]();
var status = [Link]();
var body = [Link]();
if (status == 200) {
var data = [Link](body);
[Link]('Users fetched: ' + [Link]);
} else {
[Link]('API call failed: ' + status);
}
■ Tip: Store API tokens in System Properties, never hard-code them in scripts.
[ GlideEmailOutbound ]
09.
Send a Custom Email Notification
Programmatically send an email from a background script — useful for alerts and one-time notifications.
var email = new GlideEmailOutbound();
[Link]('manager@[Link]');
[Link]('teamlead@[Link]');
[Link](
'Alert: Critical Incidents Spike Detected'
);
[Link](
'Hi Team,\n\n' +
'There are currently 25 P1 incidents open.\n' +
'Please review the queue immediately.\n\n' +
'Regards,\nServiceNow Automation'
);
[Link]();
[Link]('Email queued successfully.');
■ Tip: [Link]() queues the email — it sends when the email sender job runs (usually < 1 min).
[ Table · Relationship ]
10.
Traverse Related Tables (dot-walking)
Use dot-walking to access fields on referenced tables without a second query. A key concept tested in ServiceNow
interviews.
var gr = new GlideRecord('incident');
[Link]('state', 1);
[Link]();
while ([Link]()) {
// Dot-walk to caller's department name
var dept = gr.caller_id.department
.getDisplayValue();
// Dot-walk to assignment group manager
var mgr = gr.assignment_group
.[Link]
.getDisplayValue();
[Link]([Link] + ' | Dept: ' + dept
+ ' | Mgr: ' + mgr);
}
■ Tip: Dot-walking works in queries too: [Link]('caller_id.[Link]', 'IT').
[ GlideRecord · Encoded Query ]
11.
Use Encoded Query String
Copy encoded queries directly from List filters and use them in scripts — great for complex filter conditions.
// Copy encoded query from List > Right-click filter
// > Copy query
var encodedQuery =
'state=1^priority=1^' +
'assignment_groupISNOTEMPTY^' +
'sys_created_onONLast 7 days@javascript:' +
'gs.beginningOfLast7Days()@' +
'javascript:gs.endOfLast7Days()';
var gr = new GlideRecord('incident');
[Link](encodedQuery);
[Link]();
[Link]('Count: ' + [Link]());
■ Tip: Build complex filters in the UI, right-click the breadcrumb, and select 'Copy query'.
[ Workflow · Activity ]
12.
Trigger a Flow / Workflow Programmatically
Start a published Workflow or Flow Designer flow on a record from a background script.
// Trigger a legacy Workflow
var gr = new GlideRecord('incident');
[Link]('sys_id_of_incident_here');
var wf = new Workflow();
[Link](
'Incident Resolution Workflow', // Workflow name
gr, // Target record
'trigger', // Trigger type
{} // Workflow inputs
);
[Link]('Workflow triggered for: ' + [Link]);
// OR trigger a Flow Designer flow
// sn_fd.[Link](
// 'my_scope.my_flow_name', gr, 'trigger', {});
■ Tip: For Flow Designer (Vancouver+), prefer sn_fd.[Link]() over the older Workflow API.
[ Impersonation · Roles ]
13.
Check User Roles & Permissions
Validate whether the current user or a specific user has a required role before performing an action.
// Check current user's role
if ([Link]('itil')) {
[Link]('User has ITIL role — proceeding.');
} else {
[Link]('Access denied: ITIL role required.');
}
// Check a specific user's role
var user = new GlideRecord('sys_user');
[Link]('user_name', '[Link]');
var grole = new GlideRecord('sys_user_has_role');
[Link]('user', user.sys_id);
[Link]('[Link]', 'admin');
[Link]();
[Link]('Is admin: ' + [Link]());
■ Tip: [Link]('admin') returns true even if the role is inherited through a group.
[ JSON · Script ]
14.
Parse and Build JSON in Server Scripts
Serialize and deserialize JSON objects — critical for integration scripts and REST API responses.
// Parse JSON string into object
var jsonStr = '{"name":"Alice","dept":"IT"}';
var obj = [Link](jsonStr);
[Link]('Name: ' + [Link]); // Alice
// Build JSON from GlideRecord data
var incidents = [];
var gr = new GlideRecord('incident');
[Link]('state', 1);
[Link](5);
[Link]();
while ([Link]()) {
[Link]({
number: [Link]('number'),
priority: [Link]('priority'),
caller: gr.caller_id.getDisplayValue()
});
}
[Link]([Link](incidents, null, 2));
■ Tip: Use [Link](obj, null, 2) for pretty-printed output when debugging.
[ GlideScopedEvaluator ]
15.
Execute a Script Include from Background Script
Instantiate and call a Script Include (reusable server-side library) inside a background script.
// Assume a Script Include named 'IncidentUtils'
// with a method getOpenCount(priority)
var utils = new IncidentUtils();
var p1Count = [Link](1);
var p2Count = [Link](2);
[Link]('Open P1: ' + p1Count);
[Link]('Open P2: ' + p2Count);
// If Script Include is in another scope:
// var utils = new x_myapp.IncidentUtils();
// [Link]();
// [Link]([Link](1));
■ Tip: Script Includes must have 'Accessible from: All application scopes' to be called cross-scope.
[ Attachment · API ]
16.
Add an Attachment to a Record
Programmatically attach a file (e.g., a generated report or error log) to a ServiceNow record.
var gr = new GlideRecord('incident');
[Link]('number', 'INC0012345');
var content = 'Incident Report\n' +
'Generated: ' +
new GlideDateTime().getDisplayValue() +
'\nStatus: Reviewed';
var sa = new GlideSysAttachment();
var attachSysId = [Link](
gr, // Target GlideRecord
'[Link]', // File name
'text/plain', // Content type
content // File content
);
[Link]('Attachment created: ' + attachSysId);
■ Tip: For binary files use sa.writeBase64(); for text files [Link]() is sufficient.
[ User · Session ]
17.
Get & Impersonate a User
Retrieve current session user details and programmatically set a different user context for testing.
// Get current logged-in user info
[Link]('User: ' + [Link]());
[Link]('Full name:' + [Link]());
[Link]('User ID: ' + [Link]());
// Get user record for any user
var user = new GlideRecord('sys_user');
[Link]('user_name', '[Link]');
[Link]('Email: ' + [Link]);
[Link]('Active: ' + [Link]);
// Impersonate in background script only
// [Link]().impersonate(user.sys_id);
■ Tip: Background scripts run as the current user — be mindful of ACL restrictions during testing.
[ Table · Clone / Copy ]
18.
Copy Fields from One Record to Another
Duplicate field values across records — often used in change management or template-based creation.
// Copy an Incident into a Problem record
var inc = new GlideRecord('incident');
[Link]('number', 'INC0099001');
var prob = new GlideRecord('problem');
[Link]();
prob.short_description = inc.short_description;
[Link] = [Link];
[Link] = [Link];
prob.assigned_to = inc.assigned_to;
// Link back to originating incident
[Link](
'first_reported_by_task', inc.sys_id
);
var probSysId = [Link]();
[Link]('Problem created: ' + probSysId);
■ Tip: Use setValue('ref_field', sysId) for reference fields rather than direct assignment.
[ Scheduled Job · Simulation ]
19.
Simulate a Scheduled Job Logic
Replicate the logic of a Scheduled Script Execution (Scheduled Job) to test it interactively before deploying.
// This mimics what runs inside a Scheduled Job
// Navigate to: System Definition > Scheduled Jobs
function runNightlyCleanup() {
var cutoff = new GlideDateTime();
[Link](-90);
var gr = new GlideRecord('incident');
[Link]('state', 7); // Closed
[Link](
'resolved_at', '<', [Link]()
);
[Link]();
while ([Link]()) {
[Link]('active', false);
[Link]();
}
[Link]('Cleanup done. Processed: '
+ [Link]() + ' records.');
}
runNightlyCleanup();
■ Tip: Always test Scheduled Job logic in a Background Script first — it's instant, reversible, and safe.
[ Error Handling · [Link] ]
20.
Structured Logging & Error Handling
Use try/catch with [Link], [Link], and [Link] for robust, debuggable background scripts.
function processIncidents() {
var gr = new GlideRecord('incident');
[Link]('state', 1);
[Link](100);
[Link]();
var success = 0, failed = 0;
while ([Link]()) {
try {
// Your processing logic here
if (!gr.short_description) {
throw new Error(
'Missing short_description'
);
}
gr.work_notes = 'Processed by script';
[Link]();
success++;
} catch(e) {
[Link](
'Failed on ' + [Link] +
': ' + [Link],
'BackgroundScript'
);
failed++;
}
}
[Link]('Done. Success: ' + success +
' | Failed: ' + failed);
}
processIncidents();
■ Tip: [Link]() = info, [Link]() = warning, [Link]() = error. All appear in System Log.