ServiceNow Glide API Cheat Sheet

Live

A searchable reference for GlideRecord, gs, GlideDateTime, GlideAjax and g_form, with copy-ready examples checked by the Script Checker.

GlideRecord Server

GlideRecord reads and writes records on the server. Build the query, run it, then loop over the results. It ignores ACLs, so use GlideRecordSecure when the data goes back to a user.

GlideRecord page

new GlideRecord('table')

#

Creates a GlideRecord for a table. Nothing is read until you call query() or get().

var inc = new GlideRecord('incident');
inc.addActiveQuery();
inc.query();
while (inc.next()) {
    gs.info(inc.getValue('number'));
}

addQuery(field, value)

#

Adds a condition: the field equals the value. Several addQuery() calls are joined with AND.

var inc = new GlideRecord('incident');
inc.addQuery('priority', 1);
inc.addQuery('state', '!=', 7);
inc.query();

addQuery(field, operator, value)

#

Adds a condition with an operator: =, !=, >, >=, <, <=, IN, NOT IN, STARTSWITH, ENDSWITH, CONTAINS, DOES NOT CONTAIN, INSTANCEOF.

var inc = new GlideRecord('incident');
inc.addQuery('short_description', 'CONTAINS', 'vpn');
inc.addQuery('priority', 'IN', '1,2');
inc.addQuery('sys_created_on', '>=', gs.daysAgoStart(7));
inc.query();

Operators are uppercase. IN takes a comma-separated string or an array.

addOrCondition(field, operator, value)

#

Adds an OR to a condition. Call it on the object that addQuery() returns.

var inc = new GlideRecord('incident');
var cond = inc.addQuery('priority', 1);
cond.addOrCondition('priority', 2);
inc.addActiveQuery();
inc.query();

This reads as (priority is 1 OR 2) AND active.

addEncodedQuery(query)

#

Adds conditions written as an encoded query, exactly as copied from a list filter.

var inc = new GlideRecord('incident');
inc.addEncodedQuery('active=true^priorityIN1,2^assigned_toISEMPTY');
inc.query();

Build the filter in a list, right-click the breadcrumb and choose Copy query. Field names it does not recognise are ignored silently.

addActiveQuery()

#

Shortcut for addQuery("active", true).

var task = new GlideRecord('task');
task.addActiveQuery();
task.query();

addNullQuery(field) / addNotNullQuery(field)

#

Matches records where the field is empty, or is not empty.

var inc = new GlideRecord('incident');
inc.addActiveQuery();
inc.addNullQuery('assigned_to');
inc.query();

orderBy(field) / orderByDesc(field)

#

Sorts the results, ascending or descending.

var inc = new GlideRecord('incident');
inc.addActiveQuery();
inc.orderByDesc('sys_created_on');
inc.setLimit(10);
inc.query();

setLimit(n)

#

Returns at most n records. Use setLimit(1) when you only need to know whether one exists.

var dup = new GlideRecord('incident');
dup.addQuery('correlation_id', correlationId);
dup.setLimit(1);
dup.query();
var exists = dup.hasNext();

query() and next()

#

query() runs the query; next() moves to the next record and returns false when there are no more.

var inc = new GlideRecord('incident');
inc.addActiveQuery();
inc.query();
while (inc.next()) {
    gs.info(inc.getValue('number'));
}

If the table has a field called next, use _query() and _next() instead.

hasNext()

#

Returns true when there is at least one more record, without moving to it.

var gr = new GlideRecord('sys_user_group');
gr.addQuery('name', 'Service Desk');
gr.setLimit(1);
gr.query();
if (gr.hasNext()) {
    gs.info('Group exists');
}

get(sysId) / get(field, value)

#

Loads one record by sys_id, or by any field. Returns true if a record was found.

var user = new GlideRecord('sys_user');
if (user.get('user_name', 'abel.tuter')) {
    gs.info(user.getValue('email'));
}

Always check the return value; on false the object is empty.

getValue(field)

#

Returns the stored value of a field as a string (a sys_id for reference fields), or null when empty.

var inc = new GlideRecord('incident');
if (inc.get(incidentId)) {
    var callerId = inc.getValue('caller_id');
    var state = inc.getValue('state');
}

Prefer getValue() over inc.field: the dot form returns a GlideElement object, not a string.

getDisplayValue(field)

#

Returns what a user sees for a field: the label of a choice, the name of a referenced record.

var inc = new GlideRecord('incident');
if (inc.get(incidentId)) {
    gs.info(inc.getDisplayValue('state'));      // "In Progress"
    gs.info(inc.getDisplayValue('caller_id'));  // "Abel Tuter"
}

setValue(field, value)

#

Sets a field value. For reference fields, pass the sys_id.

var inc = new GlideRecord('incident');
if (inc.get(incidentId)) {
    inc.setValue('state', 2);
    inc.setValue('assigned_to', userId);
    inc.update();
}

getUniqueValue()

#

Returns the sys_id of the current record.

var inc = new GlideRecord('incident');
inc.initialize();
inc.setValue('short_description', 'Printer offline');
inc.insert();
gs.info(inc.getUniqueValue());

initialize() and insert()

#

initialize() prepares a new record with default values; insert() saves it and returns its sys_id.

var inc = new GlideRecord('incident');
inc.initialize();
inc.setValue('short_description', 'Printer on floor 3 is offline');
inc.setValue('caller_id', gs.getUserID());
var incidentId = inc.insert();

Without initialize() (or newRecord()), field defaults are not applied.

update()

#

Saves changes to an existing record and runs business rules.

var inc = new GlideRecord('incident');
if (inc.get(incidentId)) {
    inc.setValue('work_notes', 'Escalated to the network team');
    inc.update();
}

Do not call current.update() in a business rule; set the fields in a before rule instead.

deleteRecord()

#

Deletes the current record.

var att = new GlideRecord('sys_attachment');
if (att.get(attachmentId)) {
    att.deleteRecord();
}

updateMultiple() / deleteMultiple()

#

Applies the same change to, or deletes, every record matching the query, without looping.

var logs = new GlideRecord('u_integration_log');
logs.addQuery('sys_created_on', '<', gs.daysAgoStart(90));
logs.deleteMultiple();

Always add conditions first. Without any, every record in the table is changed or deleted.

getEncodedQuery()

#

Returns the query built so far as an encoded query string. Handy for logging and debugging.

var inc = new GlideRecord('incident');
inc.addActiveQuery();
inc.addQuery('priority', 1);
gs.info(inc.getEncodedQuery()); // active=true^priority=1

isValidField(field) / isValid()

#

isValidField() checks that a field exists on the table; isValid() checks that the table exists.

var gr = new GlideRecord('incident');
if (gr.isValidField('u_vendor')) {
    gr.addQuery('u_vendor', vendorId);
}

setWorkflow(false)

#

Skips business rules, notifications, workflows and audit for the next update.

var inc = new GlideRecord('incident');
if (inc.get(incidentId)) {
    inc.setWorkflow(false); // data fix: skip notifications on purpose
    inc.setValue('u_migrated', true);
    inc.update();
}

Use it only when skipping all of that is intended, such as data fixes.

autoSysFields(false)

#

Updates the record without changing Updated, Updated by and the update count.

var inc = new GlideRecord('incident');
if (inc.get(incidentId)) {
    inc.autoSysFields(false); // data fix: keep the original updated date
    inc.setValue('u_migrated', true);
    inc.update();
}

canRead() / canWrite() / canCreate() / canDelete()

#

Checks the ACLs for the logged-in user on the current record.

var inc = new GlideRecord('incident');
if (inc.get(incidentId) && inc.canRead()) {
    gs.info(inc.getValue('short_description'));
}

new GlideRecordSecure('table')

#

Same API as GlideRecord, but enforces ACLs: records and fields the user cannot read are left out.

var inc = new GlideRecordSecure('incident');
inc.addQuery('caller_id', gs.getUserID());
inc.query();
while (inc.next()) {
    gs.info(inc.getValue('number'));
}

Use it in client-callable script includes and scripted REST APIs.

getRowCount()

#

Avoid: shown so you recognise it

Returns the number of records the query returned.

var inc = new GlideRecord('incident');
inc.addActiveQuery();
inc.query();
var count = inc.getRowCount();

It loads every matching record just to count them. Use GlideAggregate with COUNT instead.

GlideAggregate Server

GlideAggregate runs COUNT, SUM, MIN, MAX and AVG in the database, so you do not load records just to count them. It extends GlideRecord, so addQuery() and friends work the same way.

GlideAggregate page

addAggregate('COUNT')

#

Counts the records that match the query.

var ga = new GlideAggregate('incident');
ga.addActiveQuery();
ga.addAggregate('COUNT');
ga.query();
var count = ga.next() ? parseInt(ga.getAggregate('COUNT'), 10) : 0;

Returns: getAggregate() returns a string, so convert it with parseInt().

groupBy(field)

#

Groups the results by a field, giving one row per value.

var ga = new GlideAggregate('incident');
ga.addActiveQuery();
ga.addAggregate('COUNT');
ga.groupBy('priority');
ga.query();
while (ga.next()) {
    gs.info(ga.getValue('priority') + ': ' + ga.getAggregate('COUNT'));
}

addAggregate('SUM' | 'MIN' | 'MAX' | 'AVG', field)

#

Runs SUM, MIN, MAX or AVG on a numeric field. Here: the average number of reassignments per group.

var ga = new GlideAggregate('incident');
ga.addActiveQuery();
ga.addAggregate('AVG', 'reassignment_count');
ga.groupBy('assignment_group');
ga.query();
while (ga.next()) {
    gs.info(ga.getDisplayValue('assignment_group') + ': ' + ga.getAggregate('AVG', 'reassignment_count'));
}

addAggregate('COUNT', field)

#

Counts per value of a field: a quick way to find duplicates.

var ga = new GlideAggregate('sys_user');
ga.addAggregate('COUNT', 'email');
ga.groupBy('email');
ga.query();
while (ga.next()) {
    if (parseInt(ga.getAggregate('COUNT', 'email'), 10) > 1) {
        gs.info('Duplicate email: ' + ga.getValue('email'));
    }
}

GlideSystem (gs) Server

The gs object is available in every server script: logging, the current user, properties, messages, events and date helpers.

GlideSystem page

gs.info() / gs.warn() / gs.error() / gs.debug()

#

Writes to the system log at a level. Use {0}, {1} placeholders for values.

gs.info('Closed {0} incidents for {1}', count, groupName);
gs.error('Integration failed: ' + errorMessage);

gs.log() and gs.print() are not available in scoped apps; these work everywhere.

gs.getUserID() / gs.getUserName() / gs.getUserDisplayName()

#

The logged-in user's sys_id, user name and display name.

var inc = new GlideRecord('incident');
inc.addQuery('caller_id', gs.getUserID());
inc.addActiveQuery();
inc.query();

gs.getUser()

#

Returns the logged-in user as an object, with hasRole() and isMemberOf().

var user = gs.getUser();
if (user.isMemberOf('Service Desk')) {
    gs.info(user.getName() + ' is on the Service Desk');
}

gs.hasRole('role')

#

True when the logged-in user has the role.

if (!gs.hasRole('itil')) {
    gs.addErrorMessage('Only the service desk can reopen incidents.');
}

Admins pass every hasRole() check.

gs.getProperty('name', default)

#

Reads a system property, with a default when it is not set. Always returns a string.

var limit = parseInt(gs.getProperty('x_acme_itsm.max_items', '50'), 10);
var groupId = gs.getProperty('x_acme_itsm.major_incident.group');

The place for sys_ids and URLs, instead of hard-coding them.

gs.addInfoMessage() / gs.addErrorMessage()

#

Shows a message at the top of the form for the current user.

gs.addInfoMessage('Incident ' + current.getValue('number') + ' was escalated.');

gs.eventQueue('event.name', record, parm1, parm2)

#

Fires an event. Notifications and script actions listen for it.

gs.eventQueue('incident.escalated', current, current.getValue('assigned_to'), gs.getUserID());

Register the event in the Event Registry first.

gs.nil(value)

#

True for null, undefined, an empty string or an empty GlideElement.

if (gs.nil(current.getValue('assignment_group'))) {
    gs.addErrorMessage('Pick an assignment group first.');
}

gs.daysAgoStart(n) / gs.beginningOfToday() / gs.endOfToday()

#

Date helpers that return date-time strings for queries.

var inc = new GlideRecord('incident');
inc.addQuery('sys_created_on', '>=', gs.daysAgoStart(7));
inc.query();

gs.getMessage('key', args)

#

Returns a translated message from the Messages table, with {0} placeholders filled in.

var text = gs.getMessage('Hello {0}', [gs.getUserDisplayName()]);

gs.generateGUID()

#

Returns a new random 32-character id.

var correlationId = gs.generateGUID();

gs.tableExists('table')

#

True when the table exists on this instance.

if (gs.tableExists('u_vendor_contract')) {
    gs.info('Contracts plugin is installed');
}

GlideDateTime Server

GlideDateTime stores date-time values in UTC and shows them in the user’s time zone. Use it instead of JavaScript Date on the server.

GlideDateTime page

new GlideDateTime()

#

The current date and time. Pass a value in UTC ("yyyy-MM-dd HH:mm:ss") to create a specific moment.

var now = new GlideDateTime();
var fixed = new GlideDateTime('2026-10-18 09:00:00'); // UTC

getValue() / getDisplayValue()

#

getValue() gives the UTC value in the internal format; getDisplayValue() gives the user's time zone and date format.

var now = new GlideDateTime();
gs.info(now.getValue());        // UTC, yyyy-MM-dd HH:mm:ss
gs.info(now.getDisplayValue()); // user's time zone and format

new GlideDateTime(record.getValue(field))

#

Reads a date-time field into a GlideDateTime.

var opened = new GlideDateTime(current.getValue('opened_at'));

addDaysLocalTime(n) / addDaysUTC(n) / addSeconds(n)

#

Adds time. LocalTime methods follow the user's time zone (and daylight saving); UTC methods do not.

var due = new GlideDateTime();
due.addDaysLocalTime(3);
due.addSeconds(3600);
current.setValue('due_date', due.getValue());

addDays() is deprecated; use addDaysLocalTime() or addDaysUTC(). Use negative numbers to go back.

before(gdt) / after(gdt) / compareTo(gdt)

#

Compares two date-times. compareTo() returns -1, 0 or 1.

var due = new GlideDateTime(current.getValue('due_date'));
if (due.before(new GlideDateTime())) {
    current.setValue('u_overdue', true);
}

GlideDateTime.subtract(start, end)

#

Returns the time between two date-times as a GlideDuration.

var opened = new GlideDateTime(current.getValue('opened_at'));
var duration = GlideDateTime.subtract(opened, new GlideDateTime());
gs.info('Open for ' + duration.getDisplayValue());

getNumericValue()

#

Milliseconds since 1 January 1970 (UTC). Handy for quick arithmetic.

var start = new GlideDateTime(current.getValue('opened_at'));
var hours = (new GlideDateTime().getNumericValue() - start.getNumericValue()) / 3600000;

Business rules Server

Business rules run on the server when a record is displayed, inserted, updated, deleted or queried. current is the record being saved; previous is how it looked before the update.

Business rules page

(function executeRule(current, previous) { … })

#

The standard template. Keep your code inside the function so variables do not leak.

(function executeRule(current, previous /*null when async*/) {

    current.setValue('u_last_touched', new GlideDateTime().getValue());

})(current, previous);

When: before, after, async, display

#

Before: change current, it is saved anyway. After: update other records. Async: slow work in the background. Display: send data to the form with g_scratchpad.

(function executeRule(current, previous) {
    // Before rule: no update() needed, the change is saved with the record
    if (current.getValue('priority') == '1') {
        current.setValue('u_major_incident', true);
    }
})(current, previous);

current.field.changes() / changesTo(v) / changesFrom(v)

#

Checks whether a field changed in this update.

(function executeRule(current, previous) {
    if (current.state.changesTo(6)) {
        current.setValue('u_resolved_by', gs.getUserID());
    }
})(current, previous);

Often better as the rule condition, so the rule does not run at all otherwise.

previous.field

#

The value before this update, in before and after rules.

(function executeRule(current, previous) {
    if (previous.getValue('assigned_to') != current.getValue('assigned_to')) {
        gs.eventQueue('incident.reassigned', current);
    }
})(current, previous);

previous is not available in async rules.

current.setAbortAction(true)

#

Cancels the insert, update or delete. Use it in a before rule, with a message.

(function executeRule(current, previous) {
    if (gs.nil(current.getValue('close_notes'))) {
        gs.addErrorMessage('Add close notes before resolving.');
        current.setAbortAction(true);
    }
})(current, previous);

In after and async rules the record is already saved, so aborting does nothing.

current.operation() / current.isNewRecord()

#

operation() returns insert, update or delete; isNewRecord() is true while inserting.

(function executeRule(current, previous) {
    if (current.operation() == 'insert') {
        current.setValue('u_source', 'Self-service');
    }
})(current, previous);

g_scratchpad (display rule)

#

In a display business rule, put values on g_scratchpad; client scripts read them when the form loads.

(function executeRule(current, previous) {
    g_scratchpad.isVip = current.caller_id.vip.toString() == 'true';
})(current, previous);

Only display rules send g_scratchpad to the form.

current.update() in a business rule

#

Avoid: shown so you recognise it

Saves the record again inside its own save.

(function executeRule(current, previous) {
    current.setValue('state', 2);
    current.update();
})(current, previous);

In before rules the change is saved anyway; in after rules this re-runs every rule and can loop. Remove it.

GlideAjax Server and client

GlideAjax calls a client-callable script include from the browser and gets an answer back, without loading whole records into the form.

GlideAjax page

Client-callable script include

#

Extend AbstractAjaxProcessor, tick Client callable, read parameters with this.getParameter() and return a string.

var UserUtils = Class.create();
UserUtils.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {

    getManager: function() {
        var user = new GlideRecordSecure('sys_user');
        if (!user.get(this.getParameter('sysparm_user'))) return '';
        return user.getValue('manager');
    },

    type: 'UserUtils'
});

Do not override initialize(), and use GlideRecordSecure so ACLs apply.

new GlideAjax('ScriptInclude') + getXMLAnswer()

#

Calls the method named in sysparm_name and receives the answer in a callback.

function onChange(control, oldValue, newValue, isLoading, isTemplate) {
    if (isLoading || newValue === '') {
        return;
    }
    var ga = new GlideAjax('UserUtils');
    ga.addParam('sysparm_name', 'getManager');
    ga.addParam('sysparm_user', newValue);
    ga.getXMLAnswer(function(answer) {
        g_form.setValue('u_manager', answer);
    });
}

Every parameter name must start with sysparm_; others arrive empty.

Returning several values as JSON

#

GlideAjax answers are strings, so return JSON.stringify() from the script include.

var UserInfo = Class.create();
UserInfo.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {

    getUserInfo: function() {
        var user = new GlideRecordSecure('sys_user');
        if (!user.get(this.getParameter('sysparm_user'))) return '';
        return JSON.stringify({
            email: user.getValue('email'),
            phone: user.getValue('phone')
        });
    },

    type: 'UserInfo'
});

JSON.parse(answer) in the client

#

Turns the JSON answer back into an object in the client script.

function onChange(control, oldValue, newValue, isLoading, isTemplate) {
    if (isLoading || newValue === '') {
        return;
    }
    var ga = new GlideAjax('UserInfo');
    ga.addParam('sysparm_name', 'getUserInfo');
    ga.addParam('sysparm_user', newValue);
    ga.getXMLAnswer(function(answer) {
        if (!answer) return;
        var info = JSON.parse(answer);
        g_form.setValue('u_email', info.email);
        g_form.setValue('u_phone', info.phone);
    });
}

getXML(callback)

#

Older form: the answer is an attribute of the response XML.

function onChange(control, oldValue, newValue, isLoading, isTemplate) {
    if (isLoading || newValue === '') {
        return;
    }
    var ga = new GlideAjax('UserUtils');
    ga.addParam('sysparm_name', 'getManager');
    ga.addParam('sysparm_user', newValue);
    ga.getXML(function(response) {
        var answer = response.responseXML.documentElement.getAttribute('answer');
        g_form.setValue('u_manager', answer);
    });
}

getXMLAnswer() does the same with less code.

getXMLWait()

#

Avoid: shown so you recognise it

Waits for the answer synchronously.

var ga = new GlideAjax('UserUtils');
ga.addParam('sysparm_name', 'getManager');
ga.getXMLWait();
var answer = ga.getAnswer();

It freezes the browser and is not supported in Service Portal. Use getXMLAnswer() with a callback.

g_form Client

g_form controls the form in client scripts, UI policies and client UI actions: read and set fields, show messages, and make fields mandatory, read-only or hidden.

g_form page

g_form.getValue(field)

#

Returns the field value as a string (a sys_id for reference fields).

function onLoad() {
    var category = g_form.getValue('category');
}

Always a string: use getIntValue() or getDecimalValue() for numbers.

g_form.getIntValue(field) / getDecimalValue(field)

#

Returns the field as a number, ready for comparisons.

function onLoad() {
    var quantity = g_form.getIntValue('u_quantity');
    if (quantity > 10) {
        g_form.showFieldMsg('u_quantity', 'Needs approval', 'info');
    }
}

g_form.setValue(field, value, displayValue)

#

Sets a field. For reference fields, pass the display value as well to avoid an extra server call.

function onLoad() {
    g_form.setValue('urgency', 2);
    g_form.setValue('assigned_to', g_user.userID, g_user.getFullName());
}

g_form.clearValue(field)

#

Empties a field.

function onLoad() {
    g_form.clearValue('u_resolution_code');
}

g_form.setMandatory(field, true|false)

#

Makes a field mandatory, or not.

function onLoad() {
    var isMajor = g_form.getValue('priority') == '1';
    g_form.setMandatory('u_business_impact', isMajor);
}

A mandatory empty field cannot be hidden: call setMandatory(field, false) first.

g_form.setReadOnly(field, true|false)

#

Makes a field read-only, or editable.

function onLoad() {
    if (!g_form.isNewRecord()) {
        g_form.setReadOnly('caller_id', true);
    }
}

Read-only in the browser is not security; protect the field with an ACL too.

g_form.setDisplay(field, true|false) / setVisible()

#

Shows or hides a field. setDisplay() closes the gap; setVisible() leaves an empty space.

function onLoad() {
    var isHardware = g_form.getValue('category') == 'hardware';
    g_form.setDisplay('u_asset_tag', isHardware);
}

g_form.setSectionDisplay(section, true|false)

#

Shows or hides a form section. The name is the section label in lowercase with underscores.

function onLoad() {
    var resolved = g_form.getValue('state') == '6';
    g_form.setSectionDisplay('resolution_information', resolved);
}

g_form.addInfoMessage() / addErrorMessage() / clearMessages()

#

Shows a message at the top of the form.

function onLoad() {
    g_form.clearMessages();
    g_form.addInfoMessage('This incident is linked to a major incident.');
}

g_form.showFieldMsg(field, message, 'info'|'error') / hideFieldMsg(field)

#

Shows a message under one field.

function onLoad() {
    g_form.hideFieldMsg('short_description');
    g_form.showFieldMsg('short_description', 'Keep it under 80 characters', 'error');
}

g_form.addOption() / removeOption() / clearOptions()

#

Changes the choices of a choice field.

function onLoad() {
    g_form.clearOptions('u_size');
    g_form.addOption('u_size', '', '-- None --');
    g_form.addOption('u_size', 'm', 'Medium');
    g_form.addOption('u_size', 'l', 'Large');
}

g_form.getReference(field, callback)

#

Loads the record behind a reference field and passes it to the callback.

function onLoad() {
    g_form.getReference('caller_id', function(caller) {
        if (caller.vip == 'true') {
            g_form.addInfoMessage('VIP caller');
        }
    });
}

Always pass a callback. For one field, GlideAjax is lighter: getReference() returns the whole record.

g_form.getUniqueValue() / getTableName() / isNewRecord()

#

The record's sys_id, its table, and whether it has been saved yet.

function onLoad() {
    if (g_form.isNewRecord()) {
        g_form.setValue('contact_type', 'self-service');
    }
}

Client scripts, g_user & g_scratchpad Client

Client scripts run in the browser. These are the four templates, plus g_user (the logged-in user) and g_scratchpad (data sent from a display business rule).

Client scripts, g_user & g_scratchpad page

onLoad()

#

Runs once when the form opens.

function onLoad() {
    if (g_form.isNewRecord()) {
        g_form.setValue('impact', 3);
    }
}

onChange(control, oldValue, newValue, isLoading, isTemplate)

#

Runs when the chosen field changes. Keep the isLoading check: it also runs while the form loads.

function onChange(control, oldValue, newValue, isLoading, isTemplate) {
    if (isLoading || newValue === '') {
        return;
    }
    g_form.setValue('u_previous_category', oldValue);
}

onSubmit()

#

Runs when the form is saved. Return false to stop the save.

function onSubmit() {
    if (g_form.getValue('state') == '6' && g_form.getValue('close_notes') === '') {
        g_form.addErrorMessage('Add close notes before resolving.');
        return false;
    }
}

Do the check with data already on the form: a GlideAjax answer arrives after the form is submitted.

onCellEdit(sysIDs, table, oldValues, newValue, callback)

#

Runs when a list cell is edited. Call callback(true) to save or callback(false) to cancel.

function onCellEdit(sysIDs, table, oldValues, newValue, callback) {
    // State 7 (Closed) can only be set from the form
    callback(newValue != '7');
}

g_form is not available while editing a list.

g_user.userID / userName / getFullName()

#

The logged-in user's sys_id, user name and full name, without a server call.

function onLoad() {
    if (g_form.isNewRecord()) {
        g_form.setValue('caller_id', g_user.userID, g_user.getFullName());
    }
}

g_user.hasRole('role') / hasRoleExactly('role')

#

hasRole() is true for admins as well; hasRoleExactly() ignores the admin role.

function onLoad() {
    if (!g_user.hasRole('itil')) {
        g_form.setReadOnly('priority', true);
    }
}

Fine for showing and hiding, but enforce access with ACLs on the server.

g_scratchpad

#

Reads values that a display business rule put on g_scratchpad: no extra server call.

function onLoad() {
    if (g_scratchpad.isVip) {
        g_form.addInfoMessage('VIP caller: handle with priority.');
    }
}

The values are set when the form loads; they do not change while the user edits.

One page per API

The same methods, one API at a time. Bookmark the one you use most.

Frequently asked questions

What is the Glide API in ServiceNow?

Glide is the set of JavaScript APIs ServiceNow scripts use: GlideRecord and GlideAggregate to read and write data, GlideSystem (gs) for users, properties, logging and events, GlideDateTime for dates, and in the browser g_form, g_user and GlideAjax.

Which methods work in scoped applications?

All methods on this sheet work in scoped apps unless the note says otherwise. The main exceptions are gs.log() and gs.print(), which only exist in global scope; use gs.info() and friends instead.

Are the examples tested?

Every example is run through the NowUtils Script Checker before it is published: it has to parse, work in ES5 mode for global scripts, and raise no high or medium findings. Examples marked Avoid show a common mistake and are caught by the checker. Last reviewed October 2026.

Where is the official ServiceNow API reference?

The full reference is in the ServiceNow developer documentation (API reference). This sheet is a quick, searchable shortcut to the methods developers use every day, with the gotchas that trip people up.