ServiceNow interview questions & answers
ServiceNow admin and developer interviews: the platform and ITSM, tables and CMDB, business rules and client scripts, GlideRecord, Flow Designer, ACLs and integrations.
Top 43 ServiceNow interview questions most asked first
1.What is ServiceNow, and what do people mean by the Now Platform and its "single data model"?easy
ServiceNow is a cloud platform for digital workflows. It started with IT Service Management, but today it runs HR, customer service, security operations, asset management and custom apps, all on one underlying platform, which ServiceNow markets as the Now Platform (recent docs call it the ServiceNow AI Platform).
Every customer gets their own instance: a dedicated application and database stack reached at a URL like
company.service-now.com. Typically you have separate dev, test and prod instances.The single data model means every app shares the same database, tables and services: users, groups, the CMDB, the Task table, workflows, notifications, reporting and security. So an incident, an HR case and a change can all point at the same user and the same CI, and one ACL model, one flow engine and one reporting engine work across all of them. That avoids integrating a dozen separate tools.
What interviewers listen for- Cloud platform for digital workflows, ITSM and beyond
- Each customer has dedicated instances (dev, test, prod)
- All apps share one database and common services
- Shared users, groups, CMDB and Task table across apps
Likely follow-up: What instances would you expect in a typical landscape, and how do they get refreshed?
2.What are the types of business rules (before, after, async, display), and when would you use each?easy
A business rule is a server-side script that runs when a record is displayed, inserted, updated, deleted or queried. The When field decides the timing:
- before: after the user submits but before the database write. Use it to validate data or set fields on
current; changes save automatically, noupdate()needed. Can abort withcurrent.setAbortAction(true). - after: after the database write. Use it to update related records, such as child tasks, because the current record is already saved.
- async: runs later, in the background, as a scheduled job. Use it for slow work like integrations or heavy recalculation, so the user isn't waiting.
previousisn't available. - display: runs when a form is loaded, before it reaches the browser. Mainly used to fill
g_scratchpadwith server data for client scripts.
There is also the Query operation, normally paired with before, which adds conditions to every query on the table, for example to hide records.
What interviewers listen for- before: validate or modify current, saves automatically
- after: update related records once current is saved
- async: background work, no previous object
- display: populate g_scratchpad for client scripts
- before query rules restrict which rows are returned
Likely follow-up: Why is
current.update()in a business rule a bad idea? · Where do business rules sit in the order of execution relative to engines?- before: after the user submits but before the database write. Use it to validate data or set fields on
3.What types of client scripts are there, and what parameters does an
onChangescript receive?easyClient scripts run JavaScript in the browser on forms. There are four types:
- onLoad: when the form first renders, before the user can type. Good for setting defaults or hiding fields.
- onChange: when a specific field's value changes. It receives
control,oldValue(the value when the record loaded),newValue,isLoadingandisTemplate. - onSubmit: when the form is submitted. Return
falseto cancel the submission, typically after validation fails. - onCellEdit: the only type that runs in list editing. It gets
sysIDs,table,oldValues,newValueand acallback; callingcallback(false)stops the change.
The
isLoadingguard matters because onChange also fires while the form loads. And client scripts are for user experience, not security: anything enforced only in the browser can be bypassed through lists, imports or APIs.function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; // don't run while the form is loading or when cleared } if (newValue == '1') { g_form.setMandatory('work_notes', true); g_form.showFieldMsg('priority', 'P1: add work notes', 'info'); } }What interviewers listen for- onLoad, onChange, onSubmit, onCellEdit
- onChange gets control, oldValue, newValue, isLoading, isTemplate
- onSubmit returns false to cancel
- onCellEdit is the only one for list editing
- Client scripts are UX, not security
Likely follow-up: How would you stop the same logic from being bypassed through list editing?
4.What is the difference between a UI policy, a client script and a data policy? When would you choose each?mid
- UI policy: client-side, mostly no-code. Based on a condition it makes fields mandatory, read-only or visible, and can reverse the actions when the condition is false. It can also run a script when true or false. First choice for simple form behaviour.
- Client script: client-side JavaScript for anything a UI policy can't do: calling the server with GlideAjax, setting values, changing choice options, validating on submit.
- Data policy: server-side enforcement of mandatory and read-only. It applies no matter how data arrives: forms, imports, web services. It can't hide fields.
On load, client scripts run before UI policies, and when they conflict on mandatory or read-only, the UI policy generally wins. The key interview point: UI policies and client scripts only protect the form. If a rule must hold for imports, APIs and list edits too, use a data policy (a data policy can also be applied as a UI policy on the client), a business rule, or an ACL.
What interviewers listen for- UI policy: no-code mandatory, read-only, visible on forms
- Client script: custom logic, GlideAjax, values, options
- Data policy: server-side, applies to all data sources
- Data policies cannot hide fields
- UI policies run after onLoad client scripts
Likely follow-up: A field is mandatory via UI policy, but records arrive via REST without it. Why, and how do you fix it?
5.What is the difference between an incident and a problem, and where does a known error fit in?easy
An incident is an unplanned interruption to a service, or a reduction in its quality. The goal of Incident Management is to restore service as quickly as possible, even with a workaround. Example: email is down for the sales team, so the service desk restarts the mail service.
A problem is the cause, or potential cause, of one or more incidents. Problem Management looks for the root cause and a permanent fix, and it can be reactive (after repeated incidents) or proactive (spotting trends before users are hit).
A known error is a problem whose root cause is understood and that has a documented workaround, often published to the knowledge base so the service desk can resolve related incidents faster. The permanent fix usually goes through Change Management.
In ServiceNow, incidents link to a problem through the
problem_idfield, and a problem can raise a change request to deliver the fix.What interviewers listen for- Incident: restore service fast, workaround acceptable
- Problem: underlying cause of one or more incidents
- Known error: root cause known, workaround documented
- Permanent fix is delivered through a change
Likely follow-up: How is incident priority calculated in ServiceNow? · What is a major incident and how is it handled differently?
6.How do you query records with GlideRecord? Walk me through
addQuery,addEncodedQuery,setLimitandget.easyGlideRecord is the server-side API for database operations. You create it for a table, add conditions, call
query(), then iterate withnext().addQuery(field, value)oraddQuery(field, operator, value)adds a condition; multiple calls are ANDed.addEncodedQuery(string)takes the encoded query you can copy from a list filter, likeactive=true^priority=1.^is AND,^ORis OR.setLimit(n)caps the number of rows fetched, which is important for performance.get(sys_id)orget(field, value)loads a single record and returnstrueif it was found, so always check the return value.
Prefer
getValue()for reading a field as a string andsetValue()for writing. And test encoded queries carefully: an invalid field name is dropped from the query, which can silently return every record in the table.var inc = new GlideRecord('incident'); inc.addQuery('active', true); inc.addQuery('priority', '<=', 2); inc.addEncodedQuery('assignment_groupISEMPTY'); inc.orderByDesc('sys_created_on'); inc.setLimit(10); inc.query(); while (inc.next()) { gs.info(inc.getValue('number') + ' ' + inc.getDisplayValue('caller_id')); } var one = new GlideRecord('incident'); if (one.get('number', 'INC0010001')) { gs.info(one.getUniqueValue()); }What interviewers listen for- Create, add conditions, query(), loop with next()
- addQuery supports an operator as the middle argument
- addEncodedQuery takes list-filter syntax (^, ^OR)
- setLimit caps rows; get() returns a boolean
- Invalid query parts are dropped, not rejected
Likely follow-up: How would you express an OR condition without an encoded query? · What is the difference between
getValue()and dot-walkinggr.field?7.How do you call server-side code from a client script? Show a GlideAjax call and the matching script include.mid
The standard way is GlideAjax calling a client-callable script include.
On the server, the script include extends
AbstractAjaxProcessor, its name matches the class name, and the Client callable (Glide AJAX enabled) box is checked. Methods read inputs withthis.getParameter()and return a value.On the client, you create
new GlideAjax('ClassName'), setsysparm_nameto the method name, add your own parameters (they must start withsysparm_), and send it asynchronously.getXMLAnswer()hands the callback the answer string directly;getXML()gives you the XML response to parse.Avoid
getXMLWait(): it's synchronous, freezes the browser and isn't available in scoped apps. Methods whose names start with an underscore aren't callable from the client, and recent releases protect client-callable script includes with ACLs, so check access when a call silently returns nothing.// Client script var ga = new GlideAjax('UserUtilsAjax'); ga.addParam('sysparm_name', 'getDepartment'); ga.addParam('sysparm_user', g_form.getValue('caller_id')); ga.getXMLAnswer(function (answer) { g_form.setValue('u_department', answer); }); // Script include "UserUtilsAjax", Client callable checked var UserUtilsAjax = Class.create(); UserUtilsAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, { getDepartment: function () { var user = new GlideRecord('sys_user'); return user.get(this.getParameter('sysparm_user')) ? user.getValue('department') : ''; }, type: 'UserUtilsAjax' });What interviewers listen for- Script include extends AbstractAjaxProcessor, Client callable
sysparm_nameselects the method; params start with sysparm_- Use async getXML or getXMLAnswer with a callback
- Avoid getXMLWait: blocks the browser, not in scoped apps
- Underscore-prefixed methods are private
Likely follow-up: When would you use a display business rule with
g_scratchpadinstead of GlideAjax?8.What are
currentandpreviousin a business rule, and when ispreviousnot available?easycurrentis a GlideRecord holding the record being processed, including the values the user just submitted. In a before rule you change it directly and the changes are saved automatically.previousholds the record as it was before this update, so you can compare old and new values. The GlideElement helperschanges(),changesFrom()andchangesTo()use it under the hood, e.g.current.state.changesTo(6).previousis only meaningful on update and delete. It's not available in async business rules, because they run later as a scheduled job after the transaction has finished; that's why the default template saysprevious /*null when async*/, and whychanges()doesn't work inside an async script. You can still use change conditions in the async rule's condition builder, because that's evaluated during the original transaction.(function executeRule(current, previous /*null when async*/) { if (current.state.changesTo(6)) { // 6 = Resolved on incident current.resolved_by = gs.getUserID(); } if (previous && previous.getValue('priority') != current.getValue('priority')) { gs.info('Priority changed on ' + current.getValue('number')); } })(current, previous);What interviewers listen for- current: record with the new, submitted values
- previous: values before this update
- changes(), changesFrom(), changesTo() compare them
- previous is null in async rules
- Async rules can still use change conditions in the filter
9.How are ACLs evaluated in ServiceNow? Explain table versus field ACLs and the order of matching.mid
To access a record, a user must pass both the table-level ACL and the field-level ACL for the operation (read, write, create, delete).
Table ACLs match from most specific to most general: the table (
incident), then its parent (task), then*.Field ACLs, per the docs:
incident.number,task.number,*.number, thenincident.*,task.*,*.*.The first matching ACL the user passes grants access and stops processing at that level; if the user passes none of the matching ACLs, access is denied. If several ACLs match at the same point in the order, passing any one is enough. Within a single ACL, the user must meet all of its requirements: one of the required roles, the data condition and the script (
answer = true).Recent releases add Deny-Unless ACLs, which are evaluated before the normal Allow-If ones: fail a Deny-Unless and you're denied, whatever the Allow-If rules say. Admins can view and debug ACLs, but creating or editing them requires elevating to
security_admin. The security debug output shows which rule passed or failed.What interviewers listen for- Must pass table-level and field-level ACLs
- Specific to general: table, parent, then *
- Any one passing ACL at the same point is enough
- Within an ACL: role AND condition AND script
- Deny-Unless ACLs evaluated before Allow-If
Likely follow-up: A user with the right role still can't see a field. How do you debug it?
10.What is an update set, and how do you move one from dev to test to production?easy
An update set is a named group of configuration changes (customer updates in
sys_update_xml) that you move between instances as a unit.The flow:
- In dev, create an update set and make it current, then build. Each change to a tracked record is captured; multiple edits to the same record keep only the latest version.
- Mark it Complete.
- In the target instance, retrieve it, either from a remote update source or by importing the exported XML.
- Preview it. The preview flags problems such as collisions with newer local changes or missing referenced records. You resolve each one by accepting or skipping the remote update.
- Commit it.
Good habits: never build in the Default update set, keep sets small and focused, preview in test before prod, and use batching (parent/child sets) for large releases. Update sets are per application scope, so scoped app changes go into that app's update set.
What interviewers listen for- Captures configuration, not data, in sys_update_xml
- Complete, retrieve, preview, commit
- Preview catches collisions and missing references
- Only the latest version of each record is kept
- Update sets are application-scope specific
Likely follow-up: What isn't captured in an update set? · How would you back out an update set?
11.What are the three change types in ServiceNow, and what role does the CAB play?easy
ServiceNow follows the ITIL change types:
- Standard: pre-authorized, low risk, repeatable, with a proven history. Requested from a catalog of standard change templates. No peer approval or CAB step; it goes from New straight to Scheduled.
- Normal: anything that isn't standard or emergency. It goes through the full lifecycle: New, Assess, Authorize, Scheduled, Implement, Review, Closed, with technical or peer approval and CAB authorization.
- Emergency: must be done as soon as possible, for example to fix a major incident or apply an urgent security patch. It skips Assess and goes straight to Authorize, where the emergency CAB approves it quickly.
The Change Advisory Board is the group that reviews and authorizes changes, weighing risk, impact, schedule conflicts and blackout windows. ServiceNow supports it with CAB meetings, agendas and the CAB Workbench. Good practice is to push more low-risk work into standard changes so the CAB spends time on genuinely risky ones.
What interviewers listen for- Standard: pre-approved template, no CAB
- Normal: full assessment and CAB authorization
- Emergency: urgent, skips Assess, fast CAB approval
- CAB weighs risk, impact and scheduling conflicts
Likely follow-up: How would you decide that a change should become a standard change?
12.What is a script include, what kinds are there, and why prefer one over a global business rule?mid
A script include is a reusable server-side library: a class or function you call from business rules, flows, UI actions, other script includes or, if client callable, from the browser via GlideAjax.
The common shapes:
- Class-based:
Class.create()with a prototype, aninitializeconstructor and atypeproperty. Called withnew IncidentUtils().countOpenForCaller(id). - Classless (on-demand): a single function whose name matches the script include name.
- Extending:
Object.extendsObject(Parent, {...}), most often extendingAbstractAjaxProcessorfor GlideAjax.
The name must match the class or function name. Accessible from controls whether other scopes may call it, and callers from another scope use the scope prefix, like
new global.IncidentUtils().They beat global business rules because script includes load only when called, while global business rules load on every transaction. They're also easier to test and reuse.
var IncidentUtils = Class.create(); IncidentUtils.prototype = { initialize: function () {}, countOpenForCaller: function (callerId) { var ga = new GlideAggregate('incident'); ga.addQuery('caller_id', callerId); ga.addActiveQuery(); ga.addAggregate('COUNT'); ga.query(); return ga.next() ? parseInt(ga.getAggregate('COUNT'), 10) : 0; }, type: 'IncidentUtils' };What interviewers listen for- Reusable server-side class or function
- Class-based, classless, or extending another class
- Name must match the class or function
- Loaded on demand, unlike global business rules
- Accessible from controls cross-scope use
Likely follow-up: How do you call a scoped script include from another scope?
- Class-based:
13.What are
g_formandg_user? Name the methods you use most often.easyBoth are client-side globals available in form scripts.
g_form(GlideForm) controls the current form:getValueandsetValue; pass the display value as the third argument ofsetValuefor reference fields to save a server round tripsetMandatory,setReadOnly,setDisplay(removes the space) versussetVisible(leaves a gap)showFieldMsg/hideFieldMsg,addInfoMessage/addErrorMessageaddOption,removeOption,clearOptionsfor choice listsisNewRecord,getUniqueValue,getTableName,save,submit
g_user(GlideUser) describes the logged-in user:userID,userName,firstName,lastName,getFullName(),hasRole(),hasRoleExactly()andhasRoleFromList().Watch out:
g_user.hasRole('itil')also returns true for admins, whilehasRoleExactly()doesn't. And checking roles in the browser is for UX only; real enforcement is ACLs.function onLoad() { if (!g_user.hasRole('itil')) { g_form.setReadOnly('assignment_group', true); g_form.setDisplay('work_notes', false); } if (g_form.isNewRecord()) { g_form.setValue('caller_id', g_user.userID, g_user.getFullName()); g_form.addInfoMessage('Hi ' + g_user.firstName + ', describe the issue.'); } }What interviewers listen for- g_form manipulates the form, g_user describes the user
- setDisplay removes space; setVisible leaves a gap
- setValue with display value avoids a server call
- hasRole returns true for admin; hasRoleExactly does not
- Client-side role checks are UX, not security
14.A client script needs data that isn't on the form. What are your options, and which is most efficient?mid
There are four ways to get server data into the browser:
- Display business rule +
g_scratchpad: the rule runs when the form is built and puts values intog_scratchpad, which ships with the form. No extra round trip, so it's the most efficient choice when you know what you need at load time. - Asynchronous GlideAjax: calls a client-callable script include on demand, for example in an onChange. Best when the data depends on what the user just picked.
g_form.getReference(field, callback): fetches the whole referenced record. Easy, but it pulls every field; the docs no longer recommend it.- Client-side GlideRecord: also not recommended, for the same performance reasons.
A related anti-pattern is adding a field to the form only to hide it so a script can read it. It works, but it's slower than a scratchpad value. So: scratchpad for load-time data, async GlideAjax for dynamic data, and never a synchronous call.
// Display business rule on incident (function executeRule(current, previous) { g_scratchpad.isVip = current.caller_id.vip == true; g_scratchpad.managerName = current.caller_id.manager.getDisplayValue(); })(current, previous); // onLoad client script function onLoad() { if (g_scratchpad.isVip) { g_form.addInfoMessage('VIP caller. Manager: ' + g_scratchpad.managerName); } }What interviewers listen for- Display rule fills g_scratchpad, sent with the form
- Scratchpad only works for load-time data
- Async GlideAjax for data that depends on user input
- getReference and client GlideRecord fetch whole records
- Avoid synchronous calls and hidden helper fields
- Display business rule +
15.How would you count records efficiently? Compare
getRowCount()with GlideAggregate.midgetRowCount()is a GlideRecord method: you run a normal query, which retrieves the matching records, and then ask how many came back. That's fine if you're going to loop over the records anyway, but wasteful if all you need is the number.GlideAggregate extends GlideRecord and pushes the aggregation to the database:
COUNT,SUM,MIN,MAX,AVGand a few others. You add the aggregate beforequery(), then read it withgetAggregate(). Pass a field name, or callgroupBy(), to get one row per group, like a SQLGROUP BY.addHaving()filters on the aggregate, andorderByAggregate()sorts by it.So the rule of thumb from the docs: if you only need a count, use GlideAggregate; use
getRowCount()only when you also need the records. Note thatgetAggregate()returns a string, so convert it before doing arithmetic.// Count active incidents per category var ga = new GlideAggregate('incident'); ga.addActiveQuery(); ga.addAggregate('COUNT', 'category'); ga.orderByAggregate('COUNT', 'category'); ga.query(); while (ga.next()) { gs.info(ga.getValue('category') + ': ' + ga.getAggregate('COUNT', 'category')); }What interviewers listen for- getRowCount retrieves the records, then counts
- GlideAggregate counts in the database
- Add the aggregate before query()
- groupBy or a field argument gives per-group rows
- getAggregate returns a string
16.What is not captured by update sets, and how do you move those things between instances?mid
Update sets track configuration on tables that have the
update_synchattribute: forms, fields, business rules, client scripts, ACLs, catalog item definitions and so on. Things that commonly catch people out:- Data: incidents, requests, submitted catalog orders, CIs, users, group memberships and role grants. These are data, not configuration.
- Home pages and content pages: not added by default; you have to unload them into the current update set.
- Table deletions: removing a table isn't tracked, so you delete it manually on the target.
- Data-losing dictionary changes: a type change that would lose data is skipped on commit and logged.
- Changes made in another scope or another update set, which is why picking the right current update set matters.
To move data, use Export XML / Import XML for a few records (keeps sys_ids), or import sets, including instance-to-instance imports, for larger volumes. Don't add the
update_synchattribute yourself: the docs warn it can cause serious performance problems.What interviewers listen for- Only tables with update_synch are tracked
- Transactional data, users and memberships are not
- Home pages must be unloaded manually
- Table deletions are not tracked
- Move data with XML export/import or import sets
17.How do tables, the dictionary and table extension work in ServiceNow? Why is the Task table important?easy
Tables are defined in
sys_db_objectand their columns in the dictionary,sys_dictionary, which stores each field's type, length, default value, reference target and attributes. Every record has system fields likesys_id(a 32-character GUID),sys_created_onandsys_updated_by.A table can extend another, and only at creation time. The child inherits all the parent's fields and adds its own;
sys_class_nametells you which class a record belongs to. Custom global tables and fields get au_prefix; scoped ones get the app'sx_prefix.Task is the key base table. Incident, Problem, Change Request, Requested Item, Catalog Task and many others extend it, so they share fields like number, state, assignment group, assigned to, priority, work notes and comments, plus features built on Task: approvals, SLAs, assignment rules and the activity stream. That's why you extend Task for any new work-tracking app.
When a child needs different behaviour for an inherited field, such as a different default or reference qualifier, you use a dictionary override rather than changing the parent.
What interviewers listen for- sys_db_object holds tables, sys_dictionary holds fields
- Child tables inherit parent fields; sys_class_name identifies class
- Extension is chosen when the table is created
- Task gives SLAs, approvals, assignment, activity stream
- Dictionary overrides change inherited fields per child
Likely follow-up: What would you gain by extending Task for a new custom app?
18.What is a reference field, and what is dot-walking? Where can and can't you dot-walk?easy
A reference field stores the
sys_idof a record in another table, like a foreign key, and shows that record's display value.caller_idon Incident referencessys_user.Dot-walking follows references with dots to reach fields on related records:
current.caller_id.manager.email. You can use it:- in server scripts (business rules, script includes)
- in condition builders and filters, by expanding related fields
- in list and form layouts, to show related-record fields
- in encoded queries, like
caller_id.vip=true
On the client you can't dot-walk with
g_form:g_form.getValue('caller_id')only gives you the sys_id. Use a display rule withg_scratchpad, GlideAjax, or, less ideally,g_form.getReference().Two tips: dot-walked values are GlideElement objects, so call
toString()orgetDisplayValue()when you need a string; and each hop can mean another database lookup, so avoid long dot-walks inside big loops.var inc = new GlideRecord('incident'); if (inc.get('number', 'INC0010001')) { gs.info(inc.caller_id.getDisplayValue()); // caller's name gs.info(inc.caller_id.manager.email.toString()); // dot-walk two levels var caller = inc.caller_id.getRefRecord(); // full sys_user GlideRecord gs.info(caller.getValue('department')); }What interviewers listen for- Reference field stores a sys_id of another record
- Dot-walking follows references: caller_id.manager.email
- Works in scripts, filters, layouts, encoded queries
- No dot-walking through g_form on the client
- Each hop may cost a query; watch loops
19.Explain how import sets, transform maps and coalesce work. How do you stop duplicates being created?mid
An import has three parts:
- A data source (file, JDBC, LDAP, REST and others) loads rows into an import set staging table.
- A transform map maps staging fields to a target table, such as
sys_userorcmdb_ci_server, through field maps and optional scripts. - The transform writes each row to the target and records the outcome (inserted, updated, ignored, error) in the import log.
Coalesce decides insert versus update. Mark one or more field maps as coalesce: if a target record matches on all coalesce fields, it's updated; if not, a new record is inserted. With no coalesce, every row is inserted, which is the classic cause of duplicates. Coalesce on fields that are genuinely unique, like email or employee number; if several target records match, only the first is updated.
For custom logic, transform event scripts run at
onStart,onBefore,onAfterandonComplete, among others, withsource,target,map,logandaction. Settingignore = trueinonBeforeskips that row.// onBefore transform script: update existing records only (function runTransformScript(source, map, log, target) { if (action == 'insert') { ignore = true; // skip rows with no coalesce match log.info('Skipped unmatched row: ' + source.u_email); } })(source, map, log, target);What interviewers listen for- Data source to staging table to transform map
- Coalesce match updates; no match inserts
- Multiple coalesce fields must all match
- No coalesce means every row is inserted
- Transform scripts: onStart, onBefore, onAfter, onComplete
Likely follow-up: How would you coalesce on email OR username? · What does "Run business rules" on a transform map control?
20.What is the difference between Flow Designer and the legacy Workflow editor? Which would you use for new work?mid
Flow Designer is ServiceNow's low-code automation tool; in recent releases it lives inside Workflow Studio. A flow has a trigger and a series of actions:
- Triggers: record created or updated, scheduled (daily, weekly, run once, repeat), or application triggers such as Service Catalog, SLA Task and inbound email.
- Actions: reusable building blocks with defined inputs and outputs, from core ones like Create Record and Ask for Approval to spoke actions for third-party systems through IntegrationHub.
- Subflows: reusable sequences you call from other flows.
- Flow logic: If, For Each, Do Until, Wait and so on, with data pills passing values between steps.
The legacy Workflow editor is the older drag-and-drop canvas of activities and transitions, attached to a table. It still runs in many instances, and you'll maintain existing workflows, but the docs now point to Workflow Studio for new process automation, and even suggest it over business rules. Flows are easier to read, reuse and upgrade, and the execution details page makes debugging much simpler.
What interviewers listen for- Flows: trigger plus actions, subflows and flow logic
- Reusable actions with inputs, outputs and data pills
- Spokes add third-party actions via IntegrationHub
- Legacy workflows: maintain, but build new in flows
- Execution details make flows easier to debug
Likely follow-up: When would you still write a business rule instead of a flow?
21.What is a UI action, and how do you make one that runs client-side validation and then server-side code?mid
A UI action adds a button, link or menu item to forms and lists: form button, form context menu, form link, list button, list choice, list context menu and so on. It has a Condition (evaluated on the server) and optional required roles that control when it appears, and a script that runs when it's clicked.
By default the script runs on the server, with
currentavailable;action.setRedirectURL()controls where the user lands. With the Client box checked, the Onclick field names a function that runs in the browser withg_formavailable.To do both, use the classic pattern from base-system actions: the client function validates, then calls
gsftSubmit(null, g_form.getFormElement(), '<action name>')to submit the form back to the server with that action name. On the server there is nowindowobject, so atypeof window == 'undefined'check calls the server function. The Action name must match the one passed togsftSubmit. Workspaces use a separate Workspace client script.// UI action: Client = true, Onclick = resolveClient(), Action name = resolve_inc function resolveClient() { if (!g_form.getValue('close_notes')) { g_form.showFieldMsg('close_notes', 'Close notes are required', 'error'); return false; } gsftSubmit(null, g_form.getFormElement(), 'resolve_inc'); // re-submit to server } if (typeof window == 'undefined') resolveServer(); function resolveServer() { current.state = 6; current.update(); action.setRedirectURL(current); }What interviewers listen for- Buttons, links and menu items on forms and lists
- Server-side condition and roles control visibility
- Client checkbox plus Onclick runs browser code
- gsftSubmit re-submits using the Action name
- typeof window check runs the server part
22.What is the CMDB? Explain CIs, CI classes and relationships.easy
The Configuration Management Database stores the things you manage, configuration items (CIs), and how they relate, so you can understand the impact of an incident or change.
- CI classes form a table hierarchy under
cmdb_ci: for examplecmdb_ci_hardwareextends tocmdb_ci_computer, which extends tocmdb_ci_serverand then tocmdb_ci_linux_server. Application and service classes sit alongside them. The CI Class Manager is where you view and extend the hierarchy. - Relationships live in
cmdb_rel_ci, each with a parent, a child and a type such as Depends on::Used by or Runs on::Runs. They let you trace from a business service down to the servers it relies on. - Population: Discovery, Service Mapping and Service Graph Connectors fill the CMDB automatically; the Identification and Reconciliation Engine (IRE) uses identification rules to avoid duplicate CIs and reconciliation rules to decide which data source may update which attributes.
A healthy CMDB is what makes impact analysis, change risk and event correlation trustworthy, and CMDB Health dashboards track completeness, correctness and compliance.
What interviewers listen for- CIs and their relationships, stored under cmdb_ci
- Class hierarchy, e.g. computer to server to linux server
- Relationships in cmdb_rel_ci with typed parent/child
- Discovery and connectors populate; IRE prevents duplicates
- Enables impact analysis for incidents and changes
Likely follow-up: What is CSDM and why does it matter?
- CI classes form a table hierarchy under
23.When a user orders from the Service Catalog, which records get created? Explain REQ, RITM and SCTASK.easy
Submitting a cart creates a three-level structure:
- Request (
sc_request, numbered REQ...): the overall order, like a shopping-cart receipt. It holds who requested it, for whom, and the overall approval and state. - Requested Item (
sc_req_item, RITM...): one per catalog item in the order. Each RITM carries the variable answers the user filled in and runs that item's fulfillment process, a flow or a legacy workflow. - Catalog Task (
sc_task, SCTASK...): the actual work assigned to fulfillment groups, created by the RITM's flow. One RITM can have several tasks, in sequence or in parallel.
All three extend Task, so they get assignment, SLAs, approvals and the activity stream. On the server, you read variables from the RITM with
current.variables.<variable_name>. Interviewers like to ask where approvals sit: typically on the RITM for item-specific approval (for example the user's manager), sometimes on the REQ for the whole order.What interviewers listen for- REQ: the whole order
- RITM: one per item, holds variables, runs fulfillment
- SCTASK: work assigned to fulfillment groups
- All three extend the Task table
- Read variables via current.variables.name
- Request (
24.What are catalog items, variables and variable sets? When would you use a multi-row variable set?easy
A catalog item is something users can order from the Service Catalog, like a laptop or software access. It has a name, description, category, pricing, a fulfillment flow and, most importantly, variables: the questions shown on the order form, such as select boxes, reference fields, checkboxes or dates.
A variable set is a reusable group of variables you attach to many items. The classic example is a "Requested for" block used by dozens of items; change it once and every item picks up the change.
Variable sets come in two kinds:
- Single-row: an ordinary group of questions answered once.
- Multi-row (MRVS): a grid where the user adds several rows, for example one row per new starter or per server. Its value is stored as JSON, so scripts parse it.
Behaviour on the order form comes from catalog client scripts and catalog UI policies, which work like their form counterparts but target variables. They can be defined on an item or on a variable set.
What interviewers listen for- Catalog item: orderable offering with variables
- Variables are the order-form questions
- Variable sets make groups of variables reusable
- Multi-row variable set: grid, stored as JSON
- Catalog client scripts and UI policies act on variables
25.What is a record producer, and how is it different from a regular catalog item?mid
A record producer is a special catalog item that creates a record in a target table, typically a task table like Incident or an HR case, instead of a Request and Requested Item. It gives end users a friendly catalog-style form while producing a normal record behind it.
Ways to populate the target record:
- Name a variable the same as a target field, like
caller_id, and it maps automatically. - Use a template for static values.
- Use the script:
currentis the record being created andproducerholds the user's answers, e.g.producer.issue_type. - Redirect afterwards with
producer.redirectin the platform UI orproducer.portal_redirectin Service Portal.
Don't call
current.update()orsetAbortAction()in the script; the platform inserts the record for you. And the docs say not to use record producers to create Requested Items: if the user is ordering something that needs fulfillment, use a real catalog item so the request process runs.// Record producer script (target table: incident) current.short_description = 'Laptop issue: ' + producer.issue_type.getDisplayValue(); current.urgency = producer.is_blocking == 'true' ? 1 : 3; current.assignment_group.setDisplayValue('Service Desk'); producer.portal_redirect = 'sp?id=ticket&table=incident&sys_id=' + current.sys_id;What interviewers listen for- Creates a record in a target table, not REQ/RITM
- Matching variable names map to fields
- Script uses current and producer
- producer.redirect / portal_redirect after submit
- Do not call current.update() in the script
- Name a variable the same as a target field, like
26.What is an order guide, and how does it decide which items to order?mid
An order guide lets a user fill in one form and submit a single request that generates several requested items. The standard example is new-hire onboarding: enter the starter's department, location and role once, and the guide orders a laptop, phone, badge and software access.
How it works:
- The order guide has its own variables, the initial questions.
- Rule base entries each say "if these conditions on the answers are true, include this catalog item".
- Cascading variables: answers given in the guide can flow down to variables with the same name on the included items, so users don't re-enter them.
- The user reviews the chosen items, fills in item-specific questions and submits once.
A script field can also add or remove items with
guide.add()andguide.remove(). Order guides can even run automatically from a flow or server script, without the user submitting, which suits HR-driven onboarding. The trade-off versus one big catalog item is that each item keeps its own fulfillment process.What interviewers listen for- One form, one request, several requested items
- Rule base conditions decide which items are included
- Cascading variables pass answers to items
- Each item keeps its own fulfillment flow
- Can be run automatically from a flow or script
27.What is the difference between a scoped application and the global scope? Why build scoped apps?mid
A scoped application gets its own namespace, like
x_acme_book_rooms(x_, the instance's company code, then the app ID). Its tables, script includes and other files carry that prefix, and the platform restricts what other scopes can touch.Protections you can configure:
- Table application access: whether other scopes can read, create, update or delete the table, and whether it's reachable from web services.
- Accessible from on script includes: this scope only, or all scopes.
- Cross-scope privileges and runtime access tracking (None, Tracking or Enforcing) control what the app's scripts may do outside its scope.
- Scoped scripts get the scoped API set: for example
gs.info()instead ofgs.log(), and nogetXMLWait().
Global is the legacy shared space where most base-system ITSM configuration lives; global code can reach almost anything, which is exactly the risk. Scoped apps give isolation, clearer ownership, safer upgrades and clean packaging through source control or the app repository, and new scoped apps default to the ES2021 JavaScript mode.
What interviewers listen for- Scoped apps get an x_company_app namespace
- Table access and Accessible from limit other scopes
- Cross-scope privileges and runtime access tracking
- Scoped APIs: gs.info, not gs.log
- Isolation, ownership, easier upgrades and packaging
Likely follow-up: How do you call a global script include from a scoped app?
28.How would an external system create or read incidents through REST, and when would you build a Scripted REST API instead?mid
The out-of-box option is the Table API:
/api/now/table/{tableName}. GET lists or reads a record by sys_id, POST creates, PUT or PATCH updates, DELETE deletes. Useful parameters includesysparm_query(an encoded query),sysparm_fields,sysparm_limit,sysparm_offsetandsysparm_display_value. The REST API Explorer lets you try calls and generates sample code. Requests run as the authenticated user, so ACLs and data policies apply.For inbound data that needs mapping or validation, the Import Set API posts rows into a staging table so a transform map handles coalescing.
Build a Scripted REST API when you want a clean contract that doesn't expose your table structure: combining several tables, business validation, custom status codes, or versioning. It lives at
/api/<namespace>/<version>/<api_id>/<relative_path>, each resource has a script that receivesrequestandresponse, and you can require ACLs on the API or individual resources.What interviewers listen for- Table API: /api/now/table/{table} with CRUD verbs
- sysparm_query, sysparm_fields, sysparm_limit parameters
- Import Set API when data needs transform and coalesce
- Scripted REST API for custom contracts and logic
- Calls run as the user, so ACLs apply
29.Write the script for a Scripted REST API POST resource that creates an incident. What do
requestandresponsegive you?midEach resource script is an IIFE receiving a
RESTAPIRequestand aRESTAPIResponse.From request you get:
request.body.data: the parsed request body;dataStringgives the raw textrequest.pathParams: values from the relative path, like{id}in/incident/{id}request.queryParams: query-string valuesrequest.getHeader(name), plusuriandurl
With response you call
setStatus()andsetBody(), which serializes an object to the requested format (JSON or XML), or simply return an object from the script. You can also set headers.Good practice: validate input and return proper status codes (400 for bad input, 201 for created, 404 when a record isn't found), keep business logic in a script include so the REST script stays thin, version the API so you can change it without breaking consumers, and rely on authentication plus ACLs rather than hand-rolled checks.
(function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) { var body = request.body.data; if (!body.short_description) { response.setStatus(400); response.setBody({ error: 'short_description is required' }); return; } var inc = new GlideRecord('incident'); inc.initialize(); inc.setValue('short_description', body.short_description); inc.setValue('caller_id', gs.getUserID()); var id = inc.insert(); response.setStatus(201); response.setBody({ sys_id: id, number: inc.getValue('number') }); })(request, response);What interviewers listen for- request.body.data, pathParams, queryParams, getHeader
- response.setStatus and response.setBody
- Return meaningful HTTP status codes
- Keep logic in script includes
- Version the API to protect consumers
30.How do events and notifications work together? Show how you would fire a custom event from a business rule.mid
An event is a record in the event queue (
sysevent) saying "something happened". You register the name in the Event Registry, then queue it from a script withgs.eventQueue(name, record, parm1, parm2). Scheduled jobs process the queue and hand each event to whatever listens:- Email notifications set to Event is fired; they can take recipients from parm1 or parm2
- Script actions, which run server-side code for the event
- legacy workflow activities and inactivity monitors
A notification can instead be sent when a record is inserted or updated (with conditions), or be triggered from a flow's notification step. Each defines who receives it, what it contains (subject, body, email scripts for dynamic content), and a weight: when duplicate notifications target the same record and recipients, only the highest weight is sent; weight 0 is always sent.
Why use events? They decouple when something happens from what reacts, and processing is asynchronous, so the user's transaction isn't slowed.
// After update business rule on incident (function executeRule(current, previous) { if (current.priority.changesTo(1)) { // name, record, parm1, parm2 gs.eventQueue('x_acme_ops.incident.escalated', current, current.getValue('assignment_group'), gs.getUserName()); } })(current, previous);What interviewers listen for- gs.eventQueue(name, record, parm1, parm2)
- Register events in the Event Registry
- Notifications, script actions listen to events
- Send when: event fired, insert/update, or flow
- Highest weight wins among duplicates; 0 always sends
Likely follow-up: A user says they never got an email. How do you troubleshoot it?
31.How do SLAs work in ServiceNow? Explain SLA definitions, task SLAs and their conditions.mid
An SLA definition (
contract_sla) describes a target: the table (for example Incident), the duration (say 4 hours), the schedule (such as 8-5 weekdays, which decides business time) and a set of conditions. The type can be SLA (with a customer), OLA (between internal teams) or underpinning contract (with a vendor).When a task matches, the SLA engine attaches a task SLA (
task_sla) record that tracks timing: start time, breach time, actual and business elapsed time and percentage, time left, and whether it has breached.Conditions:
- Start: when the SLA attaches
- Pause / Resume: stop and restart the clock, typically while On Hold awaiting the caller
- Stop: when the SLA completes, for example when the incident is resolved
- Cancel and Reset: cancel it, or complete it and attach a fresh one
Task SLA stages are In progress, Paused, Completed and Cancelled. Notifications and escalations at thresholds such as 50% or 75% are usually driven by the SLA's flow. A common gotcha: if the pause condition overlaps the start condition badly, the SLA stays paused forever or gets cancelled.
What interviewers listen for- Definition: table, duration, schedule, conditions
- Task SLA record tracks timing per task
- Start, pause/resume, stop, cancel, reset conditions
- Schedule decides business elapsed time
- SLA vs OLA vs underpinning contract
32.What is
gs, and which GlideSystem methods do you use most?easygsis the GlideSystem object, available in server-side scripts. The methods you reach for most:- User and session:
getUserID(),getUserName(),getUserDisplayName(),getUser()(a GlideUser object),hasRole(),getSession(),isInteractive() - Logging:
gs.info(),gs.warn(),gs.error(),gs.debug(), which accept{0}-style parameters.gs.log()exists only in global scope. - Messages to the user:
addInfoMessage(),addErrorMessage();getMessage()for translated text - Properties:
getProperty(name, default),setProperty() - Events:
eventQueue()andeventQueueScheduled() - Date helpers:
beginningOfToday(),daysAgo()and friends, often used in queries - Utility:
nil(),generateGUID(),tableExists()
Like
g_user.hasRole(),gs.hasRole()returns true for admin users. And session messages don't make sense in async code:addInfoMessage()isn't supported in async business rules.var me = gs.getUserID(); if (gs.hasRole('itil')) { gs.info('Agent {0} opened the record', gs.getUserName()); } var limit = parseInt(gs.getProperty('x_acme_app.max_rows', '100'), 10); gs.addInfoMessage(gs.getMessage('Record saved')); gs.eventQueue('x_acme_app.record.saved', current, me, '');What interviewers listen for- gs = GlideSystem, server-side only
- getUserID, getUserName, hasRole, getUser
- gs.info/warn/error/debug; gs.log is global only
- getProperty with a default value
- eventQueue fires events
- User and session:
33.How do users, groups and roles relate, and what is the best practice for granting access?easy
- Users (
sys_user) are people or integration accounts. - Groups (
sys_user_group) are teams, such as a Service Desk or a Network group. They're used for assignment, approvals and notifications. - Roles (
sys_user_role) grant permissions, likeitilfor fulfillers orcatalog_admin. ACLs, modules, UI actions and scripts check roles.
Roles can contain other roles, so granting one role can bring several others. A user gets roles directly or, preferably, by inheriting them from group membership.
Best practice: assign roles to groups, not individual users. When someone joins the Network team they're added to the group and get the right access automatically; when they leave the group it's removed. That's easier to audit and matches how HR and identity systems provision people. Keep
adminto a small set of people, and remember that some roles, likesecurity_admin, are elevated: an admin must explicitly elevate to use them. Also, roles that grant fulfiller rights often have licensing implications, so check before handing them out.What interviewers listen for- Users, groups for teams, roles for permissions
- Roles can contain other roles
- Assign roles to groups, not directly to users
- Limit admin; security_admin requires elevation
- Fulfiller roles can affect licensing
- Users (
34.Why shouldn't you call
current.update()in a business rule, and what should you do instead?midcurrent.update()saves the record again, which triggers the insert/update business rules on that table again, which may callupdate()again. At best you get a double save with duplicate audit entries and notifications; at worst a recursion that the platform has to detect and stop. The docs say it's never necessary.What to do instead:
- Before rules: just set fields on
current. Changes are written automatically once all before rules finish. - After rules: use them to update related records, such as the parent problem or child tasks, not the current one.
- Async rules: good for slow work; if you really must write back to the same record, keep it minimal and deliberate.
In the rare case where you must update the current record from an after or async rule,
setWorkflow(false)beforeupdate()suppresses business rules, but it also skips engines, notifications and audit-driven logic, so treat it as a last resort and document why.// Before update rule: just set the field, no update() needed (function executeRule(current, previous) { if (current.priority.changesTo(1)) { current.setValue('u_escalated', true); } })(current, previous); // After update rule: update a RELATED record, not current (function executeRule(current, previous) { var prb = current.problem_id.getRefRecord(); if (prb.isValidRecord()) { prb.work_notes = 'Linked incident ' + current.getValue('number') + ' was escalated'; prb.update(); } })(current, previous);What interviewers listen for- update() re-triggers business rules: recursion risk
- Before rules: set fields, they save automatically
- After rules: update related records only
- setWorkflow(false) skips BRs, engines and more
- Docs: current.update() is never necessary
- Before rules: just set fields on
35.This script is meant to collect the sys_ids of active P1 incidents, but every entry in the array is the same. Why, and how do you fix it?hard
inc.sys_idisn't a string. Dot-walking a field on a GlideRecord returns a GlideElement object, and the GlideRecord reuses its element objects asnext()moves from row to row. So you push the same object reference on every iteration, and when you read the array afterwards every entry reflects the last row the record pointed at.The fix is to push a primitive string:
ids.push(inc.getValue('sys_id'))ids.push(inc.getUniqueValue())ids.push(inc.sys_id.toString())orString(inc.sys_id)
The same bug shows up with any field, for example building an object like
{ number: inc.number }inside a loop. That's one reason experienced developers usegetValue()by default. Remember thatgetValue()returnsnullfor empty fields and"0"or"1"for booleans, so compare accordingly.var ids = []; var inc = new GlideRecord('incident'); inc.addQuery('active', true); inc.addQuery('priority', 1); inc.query(); while (inc.next()) { ids.push(inc.sys_id); // bug } gs.info(ids.join(','));What interviewers listen for- gr.field returns a GlideElement, not a string
- Elements are reused as next() advances
- Array ends up with references to the same element
- Use getValue(), getUniqueValue() or toString()
- getValue returns null for empty, "0"/"1" for booleans
36.When a record is saved, in what order do business rules, engines and notifications run? Why does the business rule Order value matter?hard
Per the docs, when a record is inserted or updated:
- Before business rules with order below 1000
- Before engines: approval, assignment rules, data policy, data lookup, escalation, field normalization and others, in no guaranteed order
- Before business rules with order 1000 or higher
- The database operation
- After business rules with order below 1000
- After engines: including the table notifications engine and the trigger engine that starts record-triggered flows
- Email notifications, by weight
- After business rules with order 1000 or higher
Async rules run later, from a scheduled job created during the transaction. Display rules run only when a form loads, and query rules run before queries.
So Order is more than tie-breaking. If you need a value that assignment rules or a data lookup will set, your before rule must be order 1000 or above. If you want to adjust something before the data policy engine checks mandatory fields, keep it below 1000. Client-side scripts always run before the form is submitted, so they come before all of this.
What interviewers listen for- Before BRs under 1000, before engines, before BRs 1000+
- Then the database operation
- After BRs under 1000, after engines, notifications, after BRs 1000+
- Async rules run later via a scheduled job
- Order 1000 is the boundary around the engines
37.What is a before query business rule, and when would you use one instead of an ACL?hard
A before query rule runs before the platform queries a table, and it can add conditions to
currentso that some rows are never returned. The base system's rule that limits which incidents users without theitilrole can see is a well-known example.Compared with read ACLs:
- The filtering is silent: users don't get the "rows removed by security constraints" message they get when ACLs strip rows from a list, and counts and pagination stay correct.
- It happens in the database query, which usually performs better than evaluating ACLs row by row.
- But it's a blunt instrument: it only restricts reads, it applies to every query on that table, including your own scripts, and it's easy to lock out integrations or reports. Always add a condition or early return for admins and system contexts.
The docs also warn to use them sparingly, keep conditions on indexed fields and avoid lots of OR clauses. Security still belongs in ACLs; use query rules for data segregation that ACLs handle badly, not as a replacement for domain separation.
// Before, Query checked, table: x_acme_vendor_case (function executeRule(current, previous) { if (gs.hasRole('x_acme.case_admin') || !gs.isInteractive()) { return; } // vendors only ever see their own company's cases current.addQuery('company', gs.getUser().getCompanyID()); })(current, previous);What interviewers listen for- Adds conditions before the query runs
- Silently hides rows; no "removed by security" message
- Usually faster than row-by-row ACL evaluation
- Applies to every query, including scripts: add exits
- Keep conditions simple and indexed; use sparingly
38.How do you call an external REST API from ServiceNow in script? Where should that call run?mid
Use RESTMessageV2 from the
sn_wsnamespace. Two styles:- Named REST Message records (System Web Services > Outbound > REST Message): store the endpoint, HTTP methods, headers, authentication and variable substitutions like
${title}. Scripts callnew sn_ws.RESTMessageV2('Ticketing API', 'Create ticket')and fill variables withsetStringParameter()orsetStringParameterNoEscape(). - Ad hoc:
new sn_ws.RESTMessageV2()withsetEndpoint(),setHttpMethod(),setRequestHeader(),setRequestBody().
execute()returns a RESTResponseV2 withgetStatusCode(),getBody()andhaveError(). Authentication goes throughsetBasicAuth()or, better, an authentication profile such as OAuth 2.0 viasetAuthenticationProfile(). For systems inside the corporate network,setMIDServer()routes the call through a MID Server.Where to run it: never in a before rule that blocks the user's save. Use an async business rule, an event with a script action, or a flow with an IntegrationHub REST step, and handle timeouts, errors and retries explicitly.
var rm = new sn_ws.RESTMessageV2('Ticketing API', 'Create ticket'); rm.setStringParameterNoEscape('title', current.getValue('short_description')); rm.setRequestHeader('Content-Type', 'application/json'); rm.setHttpTimeout(10000); try { var resp = rm.execute(); if (resp.getStatusCode() == 201) { var body = JSON.parse(resp.getBody()); gs.info('Created external ticket ' + body.id); } else { gs.error('Ticketing API failed: ' + resp.getStatusCode() + ' ' + resp.getBody()); } } catch (e) { gs.error('Ticketing API error: ' + e.message); }What interviewers listen for- sn_ws.RESTMessageV2, named or ad hoc
- execute() returns status code and body
- Use auth profiles (OAuth) rather than hard-coded creds
- setMIDServer for endpoints behind the firewall
- Run asynchronously and handle errors and timeouts
- Named REST Message records (System Web Services > Outbound > REST Message): store the endpoint, HTTP methods, headers, authentication and variable substitutions like
39.What is IntegrationHub, and what is a spoke?mid
IntegrationHub extends Flow Designer (Workflow Studio) with integration capabilities, so integrations are built as flow actions rather than scripts.
- A spoke is a scoped application bundling Workflow Studio content (actions, sometimes subflows and triggers) for one system or domain: for example Slack, Microsoft Teams, Jira or Azure AD. You drop an action like "Create Jira issue" into a flow and fill in its inputs with data pills.
- Some spokes come with the platform or the parent application, such as the ITSM spoke; many third-party spokes require an IntegrationHub subscription, and usage is licensed.
- When there's no spoke, you build a custom action using steps such as the REST step, SOAP step or JSON parser; higher tiers add steps like PowerShell, SSH and JDBC, which typically run through a MID Server.
- Connection and credential aliases keep endpoints and secrets out of the action, so the same flow works in dev, test and prod with different connections.
Why interviewers care: it's the low-code, upgrade-friendly alternative to hand-written RESTMessageV2 scripts, and it gives you reusable, testable, observable integrations.
What interviewers listen for- Adds integration actions to flows
- Spoke: scoped app of actions for one system
- Custom actions with REST, SOAP and other steps
- Connection and credential aliases per environment
- Many spokes need an IntegrationHub subscription
40.What is a MID Server, how does it communicate with the instance, and what is it used for?mid
A Management, Instrumentation and Discovery (MID) Server is a Java application you install on a Windows or Linux host inside your network. It lets a cloud instance reach systems it can't reach directly.
How it talks to the instance: the MID Server always initiates the connection outbound over HTTPS, so you don't open inbound firewall ports. Work is exchanged through the ECC Queue (
ecc_queue): the instance writes output records for a MID Server, the MID Server picks them up (it's notified through the message bus and also polls periodically), does the work, and writes input records with the results.Common uses:
- Discovery and Service Mapping, scanning the network to populate the CMDB
- Integrations with internal systems: JDBC or LDAP imports, REST or SOAP calls via
setMIDServer(), IntegrationHub PowerShell and SSH steps - Event Management and metric collection
Operational points: validate a new MID Server before use, run several for capacity and failover (clusters), give each only the capabilities and IP ranges it needs, and note that MID Servers upgrade automatically to match the instance.
What interviewers listen for- Java app on a host inside the customer network
- Outbound HTTPS only; no inbound ports needed
- Work exchanged via ECC Queue output and input records
- Discovery, Service Mapping, internal integrations
- Validate, cluster for failover, auto-upgrades
41.How do you run a script on a schedule in ServiceNow, and what should you watch out for?easy
The classic tool is a Scheduled Script Execution (a scheduled job,
sysauto_script), under System Definition > Scheduled Jobs. You pick when it runs (daily, weekly, monthly, periodically, once or on demand), a Run as user and optionally a condition script, then write the server-side script. Behind the scenes the scheduler createssys_triggerentries.Alternatives: a flow with a scheduled trigger for low-code logic, scheduled data imports for integrations, and scheduled reports.
Things to watch:
- It runs non-interactively: no
gs.addInfoMessage(), no form, no user session context, and the Script Debugger can't pause it. Log withgs.info(). - Choose Run as carefully; the job gets that user's access.
- Process large volumes in batches with
setLimit()and indexed queries, rather than one giant transaction. - Schedule heavy jobs outside business hours, and make them safe to re-run if they fail halfway.
- Test in sub-production; "Execute Now" on a big job in prod is a classic outage.
// Scheduled Script Execution, runs daily at 02:00 as a system user (function () { var stale = new GlideRecord('incident'); stale.addEncodedQuery('state=6^resolved_at<javascript:gs.daysAgoStart(7)'); stale.setLimit(500); // process in batches stale.query(); while (stale.next()) { stale.setValue('state', 7); // Closed stale.update(); } })();What interviewers listen for- Scheduled Script Execution with a schedule and Run as
- Flows with scheduled triggers as a low-code option
- Non-interactive: no session messages, log instead
- Batch with setLimit and indexed queries
- Run heavy jobs off-hours and make them re-runnable
- It runs non-interactively: no
42.What is the Automated Test Framework (ATF), and how do you use it in an upgrade or release process?mid
ATF lets you build automated tests on the instance itself, without external tools.
- A test is a sequence of steps: impersonate a user, open a new form, set field values, click a UI action, submit, then assert field values, visibility or mandatory state. There are server steps too, like record queries and Run Server Side Script (which supports Jasmine), plus Service Catalog, Service Portal, REST and custom UI steps.
- Test suites group tests, can be nested, and can be scheduled. Browser-based steps run in a Client Test Runner tab; server steps run on the instance.
- ATF rolls back the data a test creates when it finishes, and tests can be parameterized to run with multiple data sets.
In a process: build tests for critical flows (incident creation, catalog ordering, approvals, key business rules), run the suite after every upgrade in dev and test, before promoting update sets, and in CI pipelines.
Design tips from the docs: create your own test users and records instead of relying on existing data, avoid changing system tables, and don't run ATF in production; execution is disabled by default there through a system property.
What interviewers listen for- Tests are steps: impersonate, form actions, asserts
- Server-side script steps support Jasmine
- Suites can be scheduled; client runner for UI steps
- Test data is rolled back after the run
- Create own test data; do not run in production
43.How is a Service Portal widget structured, and how do the client and server parts talk to each other?mid
A portal is made of pages, pages have containers, rows and columns, and columns hold widget instances. A widget has:
- HTML template: AngularJS markup bound to the controller, e.g.
{{c.data.items}} - Client script: an AngularJS controller; by convention
var c = this - Server script: runs on load, fills the
dataobject, reads instanceoptionsand, on later calls, theinputobject sent from the client - Optional link function (DOM work), option schema (configurable instance options), CSS/SCSS and Angular providers (shared directives or services)
Communication: the server script runs when the widget loads and
dataarrives asc.data. The client callsc.server.update()to sendc.databack asinputand rerun the server script, orc.server.get({ action: ... })to send a custom payload.$spgives server helpers like$sp.getParameter();spUtilgives client helpers likerecordWatch()andaddInfoMessage().Best practices: clone base-system widgets instead of editing them, or embed them; always
setLimit()queries; avoid unfiltered record watchers and auto-refresh loops.// Server script (function () { data.limit = options.limit || 5; if (input && input.action == 'refresh') { data.refreshedAt = new GlideDateTime().getDisplayValue(); } data.items = []; var inc = new GlideRecord('incident'); inc.addQuery('caller_id', gs.getUserID()); inc.addActiveQuery(); inc.setLimit(data.limit); inc.query(); while (inc.next()) data.items.push({ number: inc.getValue('number'), sys_id: inc.getUniqueValue() }); })(); // Client controller api.controller = function () { var c = this; c.refresh = function () { c.server.get({ action: 'refresh' }).then(function (r) { c.data = r.data; }); }; };What interviewers listen for- HTML template, client controller, server script
- Server fills data; client reads c.data
- c.server.update() / get() send input back
- Option schema makes widgets configurable
- Clone or embed base widgets; limit queries
- HTML template: AngularJS markup bound to the controller, e.g.
No questions match that filter.