ServiceNow REST & SOAP
Integrations
Complete End-to-End Expert Guide + Interview Q&A
OUTBOUND: ServiceNow calling external systems (REST Messages, OAuth,
SOAP, IntegrationHub) with Jira/Salesforce/Slack real examples.
INBOUND: External systems calling ServiceNow (Table API, Scripted REST,
Webhooks) with full end-to-end working examples + 15 expert Q&A.
For ServiceNow Professionals with 6+ Years Experience
ServiceNow REST & SOAP Integrations — Complete End-to-End Guide + Interview Q&A Page 2
PART 1 — Integration Architecture Overview
1. ServiceNow integration landscape
Direction What it means Methods available
OUTBOUND ServiceNow CALLS an external system REST Message, SOAP Message, IntegrationHub
Spoke, Action Designer
INBOUND External system CALLS ServiceNow Table API, Scripted REST API, Import Set REST API,
SOAP Web Service
BIDIRECTIONAL Both directions — true sync Combination of outbound + inbound endpoints with
reconciliation logic
EVENT-DRIVEN ServiceNow publishes events; external Outbound webhook from Flow Designer / Business
subscribes Rule
Golden rule: always prefer IntegrationHub spokes over raw REST Messages for supported systems
If a pre-built Spoke exists (Jira, Slack, Salesforce, GitHub, AWS, Teams, PagerDuty):
-> USE THE SPOKE. You get: auth handling, error management, version updates,
and a drag-droppable action that non-developers can use in Flow Designer.
If no spoke exists or you need fine-grained control:
-> Build a REST Message with proper auth, error handling, and retry logic.
Then optionally wrap it in a custom Action Designer action for reusability.
Never use Business Rules for synchronous REST calls to external APIs.
-> Use Async Business Rules or Flow Designer — never block user saves with external calls.
2. Authentication methods — complete reference
Auth method Use case Where configured in ServiceNow
Basic Auth Simple user:password — dev/test environments REST Message > HTTP Request > Auth Type
ONLY (never production) = Basic
OAuth 2.0 Client Server-to-server M2M auth — most common for System OAuth > Application Registry
Credentials production
OAuth 2.0 Authorization User-delegated access — rare in integrations System OAuth > Application Registry
Code
API Key (header) External system issues a key — add as custom REST Message > HTTP Header param
HTTP header
Bearer Token (static) Long-lived token — less secure, avoid if OAuth REST Message > HTTP Header: Authorization
available
Mutual TLS (mTLS) Certificate-based — highest security, often Instance SSL certificates + REST Message
required for banks/gov cert config
HMAC Signature Sign request body with shared secret — common Custom script in before-send hook of REST
in webhooks Message
[Link] | [Link] | [Link] REST SOAP Integration Expert Reference
ServiceNow REST & SOAP Integrations — Complete End-to-End Guide + Interview Q&A Page 3
PART 2 — OUTBOUND: ServiceNow Calls External Systems
3. Real-world Example A — ServiceNow → Jira: Create a Jira Issue when a
Change Request is approved
Scenario: Your DevOps team works in Jira. When IT approves a Change Request in ServiceNow, a corresponding Jira ticket
must be auto-created in the ITOPS project so the DevOps team can track implementation.
Complete end-to-end: Change approved in SN -> Jira issue created
■■■■ STEP 1: Create Jira OAuth App (done in Jira) ■■■■
1. Jira Cloud > Settings > Developer Console > Create App
2. Name: 'ServiceNow Integration'
3. Permissions: Write Issues in ITOPS project
4. OAuth 2.0 (3LO) > Get Client ID and Client Secret
5. Redirect URL: [Link]
■■■■ STEP 2: Register OAuth in ServiceNow ■■■■
Navigate: System OAuth > Application Registry > New
Name: 'Jira Cloud OAuth'
Client ID: [from Jira] | Client Secret: [from Jira]
Token URL: [Link]
Auth URL: [Link]
Scope: read:jira-work write:jira-work
Click 'Get OAuth Token' -> login to Jira to authorize
■■■■ STEP 3: Create REST Message ■■■■
Navigate: System Web Services > Outbound > REST Messages > New
Name: 'Jira Cloud API'
Endpoint: [Link]
Auth Type: OAuth 2.0 | OAuth Profile: 'Jira Cloud OAuth'
HTTP Method > New:
Name: 'create_issue' | HTTP Method: POST
Relative URL: /issue
Content-Type: application/json
Request body:
{"fields":{"project":{"key":"ITOPS"},"summary":"${summary}",
"description":{"type":"doc","version":1,"content":[
{"type":"paragraph","content":[{"type":"text","text":"${description}"}]}]},
"issuetype":{"name":"Task"},"priority":{"name":"${priority}"}}}
[Link] | [Link] | [Link] REST SOAP Integration Expert Reference
ServiceNow REST & SOAP Integrations — Complete End-to-End Guide + Interview Q&A Page 4
■■■■ STEP 4: Business Rule — fires when Change is approved ■■■■
Table: change_request | When: After | Update: true
Condition: [Link] == '3' && [Link]('3')
(State 3 = Authorize/Approved in Change workflow)
Script (Async Business Rule to not block user save):
var rm = new sn_ws.RESTMessageV2('Jira Cloud API', 'create_issue');
[Link]('summary',
[Link] + ': ' + current.short_description);
[Link]('description',
'Change: ' + [Link] +
'\nRisk: ' + [Link]('risk') +
'\nScheduled: ' + [Link]('start_date') +
'\nServiceNow link: ' +
[Link]('[Link]') + [Link](true));
[Link]('priority',
[Link] == '1' ? 'High' : 'Medium');
var response = [Link]();
if ([Link]() == 201) {
var body = [Link]([Link]());
current.u_jira_key = [Link];
current.work_notes = 'Jira issue created: ' + [Link] +
' | ' + '[Link] + [Link];
[Link]();
} else {
[Link]('Jira create failed: ' + [Link]() +
' | ' + [Link]());
4. Real-world Example B — ServiceNow → Slack: Notify #p1-alerts when a P1
Incident is created
// ■■ SETUP: Create Slack Incoming Webhook ■■■■■■■■■■■■■■■■■■■■■■■■■■
// 1. Slack App Directory > Create a Slack App > Incoming Webhooks > ON
// 2. Add Webhook to workspace > copy webhook URL
// 3. Store URL as sys_property: [Link] (more secure than hardcoding)
// ■■ REST Message config ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// Name: 'Slack Incoming Webhook'
[Link] | [Link] | [Link] REST SOAP Integration Expert Reference
ServiceNow REST & SOAP Integrations — Complete End-to-End Guide + Interview Q&A Page 5
// Endpoint: ${webhook_url} (variable so URL comes from the script)
// Auth Type: No Authentication (Slack webhook URL IS the authentication)
// HTTP Method: POST | Content-Type: application/json
// Body: { 'text': '${message}' }
// ■■ Business Rule: After Insert on Incident ■■■■■■■■■■■■■■■■■■■■■■■
// Condition: [Link] == '1' (P1 only)
// When: After | Insert: true
// Type: ASYNC (don't block the incident save)
(function executeRule(current, previous) {
var webhookUrl = [Link]('[Link]');
if (!webhookUrl) {
[Link]('Slack webhook URL not configured');
return;
var incLink = [Link]('[Link]') + [Link](true);
var payload = {
blocks: [
type: 'header',
text: { type: 'plain_text',
text: ':rotating_light: P1 INCIDENT: ' + [Link] }
},
type: 'section',
fields: [
{ type: 'mrkdwn',
text: '*Summary:*\n' + current.short_description },
{ type: 'mrkdwn',
text: '*Assigned to:*\n' +
current.assignment_group.getDisplayValue() },
{ type: 'mrkdwn',
text: '*Caller:*\n' + current.caller_id.getDisplayValue() },
{ type: 'mrkdwn',
text: '*Opened:*\n' + [Link]('opened_at') }
},
type: 'actions',
elements: [
{ type: 'button', style: 'danger',
text: { type: 'plain_text', text: 'View in ServiceNow' },
url: incLink }
[Link] | [Link] | [Link] REST SOAP Integration Expert Reference
ServiceNow REST & SOAP Integrations — Complete End-to-End Guide + Interview Q&A Page 6
};
var rm = new sn_ws.RESTMessageV2();
[Link](webhookUrl);
[Link]('POST');
[Link]('Content-Type', 'application/json');
[Link]([Link](payload));
[Link](10000);
var response = [Link]();
if ([Link]() != 200) {
[Link]('Slack P1 notification failed: ' + [Link]());
})(current, previous);
5. Real-world Example C — ServiceNow → Salesforce: Sync Account data via
OAuth
// ■■ OAuth setup (System OAuth > Application Registry) ■■■■■■■■■■■■
// Name: Salesforce Prod OAuth
// Client ID/Secret: from Salesforce Connected App
// Token URL: [Link]
// Grant type: client_credentials (server-to-server)
// ■■ Script Include: SalesforceSync ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
var SalesforceSync = [Link]();
[Link] = {
initialize: function() {
[Link] = [Link]('[Link]');
},
// Upsert a customer account to Salesforce
syncAccount: function(accountSysId) {
var acct = new GlideRecord('customer_account');
if () return false;
var payload = {
Name: [Link]('name'),
Phone: [Link]('phone'),
Website: [Link]('website'),
ServiceNow_ID__c: accountSysId // custom field in SF
};
// Use Salesforce Composite API upsert by ServiceNow_ID__c
var rm = new sn_ws.RESTMessageV2();
[Link]([Link] +
'/services/data/v58.0/sobjects/Account/ServiceNow_ID__c/' +
accountSysId);
[Link]('PATCH'); // PATCH = upsert in Salesforce
[Link]('Content-Type', 'application/json');
[Link] | [Link] | [Link] REST SOAP Integration Expert Reference
ServiceNow REST & SOAP Integrations — Complete End-to-End Guide + Interview Q&A Page 7
[Link]('Salesforce Prod OAuth Profile', '');
[Link]([Link](payload));
try {
var resp = [Link]();
// 200 = updated, 201 = created, 204 = updated (no body)
if ([200, 201, 204].indexOf([Link]()) != -1) {
[Link]('SF sync OK for account: ' + [Link]('name'));
return true;
[Link]('SF sync failed: ' + [Link]() +
' | ' + [Link]());
return false;
} catch(e) {
[Link]('SF sync exception: ' + [Link]());
return false;
},
type: 'SalesforceSync'
};
// Called from Async Business Rule on customer_account update:
var sf = new SalesforceSync();
[Link]([Link]());
[Link] | [Link] | [Link] REST SOAP Integration Expert Reference
ServiceNow REST & SOAP Integrations — Complete End-to-End Guide + Interview Q&A Page 8
PART 3 — INBOUND: External Systems Call ServiceNow
6. ServiceNow out-of-the-box REST APIs — use without any configuration
API Base path What it does
Table API GET/POST/PUT/PATCH/DELETE Full CRUD on any table — most commonly used
/api/now/table/{tableName}
Aggregate API GET /api/now/stats/{tableName} COUNT, SUM, AVG on table data with filters
Attachment API POST /api/now/attachment/file Upload attachments to records
Import Set API POST /api/now/import/{stagingTable} Push rows directly into an Import Set staging table
CI Relationship API GET Get all CI relationships for a specific CI
/api/now/cmdb/instance/{sys_id}/relationship
Identity API GET /api/now/identity Get info about the authenticated user
UI Action API POST /api/now/ui_action/{action_name} Execute a server-side UI Action via REST
7. Real-world Example D — External system → ServiceNow Table API: Create an
incident
Scenario: Your monitoring platform (Dynatrace, Datadog, custom tool) detects an issue and needs to create a ServiceNow
Incident via REST API — without any ServiceNow plugin, just standard HTTP calls.
// ■■ From the EXTERNAL SYSTEM (any language) ■■■■■■■■■■■■■■■■■■■■■■■
// This is what the external caller sends to ServiceNow:
POST [Link]
Headers:
Content-Type: application/json
Accept: application/json
Authorization: Basic base64('sn_api_user:SecurePassword123!')
// OR: Authorization: Bearer <oauth_access_token>
Body:
"short_description": "Production database slow - response time > 30s",
"description": "Dynatrace detected: avg response time 31,450ms on DB-PROD-01",
"category": "database",
"priority": "1",
"impact": "1",
"urgency": "1",
"assignment_group": "ca2e8e1c3b1234567890abcd1234ef56",
// sys_id of group — or use setDisplayValue workaround:
"sysparm_fields": "number,sys_id,state,assignment_group"
// Only return these fields in response
// ServiceNow Response (HTTP 201 Created):
[Link] | [Link] | [Link] REST SOAP Integration Expert Reference
ServiceNow REST & SOAP Integrations — Complete End-to-End Guide + Interview Q&A Page 9
"result": {
"number": "INC0045678",
"sys_id": "abc123def456789...",
"state": { "display_value": "New", "value": "1" },
"assignment_group": {
"display_value": "Database Team",
"link": "[Link]
"value": "ca2e8e1c..."
// ■■ Query the created incident (GET) ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
GET /api/now/table/incident?
sysparm_query=number%3DINC0045678
&sysparm_fields=number,state,priority,assigned_to
&sysparm_limit=1
// ■■ Update the incident (PATCH) ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
PATCH /api/now/table/incident/{sys_id}
Body: { "state": "6", "close_notes": "Fixed by DBA team - index rebuilt" }
// ■■ Table API filter operators ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// Use encoded queries in sysparm_query:
// priority=1 -> priority equals 1
// priority=1^state=2 -> priority=1 AND state=2
// priority=1^ORpriority=2 -> priority=1 OR priority=2
// opened_atONToday@javascript:[Link]()@javascript:[Link]()
// assignment_groupIN<sys_id1>,<sys_id2>
8. Real-world Example E — Build a Scripted REST API for Jira to call back into
ServiceNow
Scenario: When a Jira issue is resolved, Jira sends a webhook to ServiceNow to automatically close the linked Change
Request. You build a custom endpoint ServiceNow exposes specifically for Jira.
Complete Scripted REST API: /api/x_myco/jira_callback/change_resolved
■■■■ STEP 1: Create the API container ■■■■
Navigate: System Web Services > Scripted REST APIs > New
Name: 'Jira Callback API' | API ID: jira_callback
Namespace: x_myco
Base path auto-generated: /api/x_myco/jira_callback
■■■■ STEP 2: Add a resource ■■■■
Resources tab > New:
Name: 'Change Resolved' | HTTP Method: POST
Relative path: /change_resolved
[Link] | [Link] | [Link] REST SOAP Integration Expert Reference
ServiceNow REST & SOAP Integrations — Complete End-to-End Guide + Interview Q&A Page 10
Full URL: POST /api/x_myco/jira_callback/change_resolved
Requires authentication: true | Required roles: jira_integration
■■■■ STEP 3: Resource script ■■■■
(function process(request, response) {
// Validate the request has a body
if (![Link] || ![Link]) {
[Link](400);
[Link]({ error: 'Empty request body' });
return;
var payload;
try {
payload = [Link]([Link]);
} catch(e) {
[Link](400);
[Link]({ error: 'Invalid JSON body' });
return;
// Jira sends: { issue: { key: 'ITOPS-123', fields: { status: 'Done' } } }
var jiraKey = [Link] && [Link];
if (!jiraKey) {
[Link](422);
[Link]({ error: 'Missing [Link] in payload' });
return;
// Find the linked ServiceNow Change Request
var cr = new GlideRecord('change_request');
[Link]('u_jira_key', jiraKey);
[Link]('state', '!=', '3'); // not already closed
[Link]();
if (![Link]()) {
[Link](404);
[Link]({ error: 'No active Change found for Jira key: ' + jiraKey });
return;
[Link] | [Link] | [Link] REST SOAP Integration Expert Reference
ServiceNow REST & SOAP Integrations — Complete End-to-End Guide + Interview Q&A Page 11
// Close the Change Request
[Link] = '3'; // Closed
cr.close_notes = 'Auto-closed: Jira issue ' + jiraKey + ' resolved';
cr.work_notes = 'Jira callback received at ' + [Link]() +
'. Jira status: ' + ([Link] || 'Done');
[Link]();
[Link](200);
[Link]({
status: 'success',
change_number: [Link]('number'),
message: 'Change closed successfully'
});
})(request, response);
■■■■ STEP 4: Create a dedicated service account for Jira ■■■■
1. Create sys_user: jira_sn_integration
2. Assign role: jira_integration (custom role with write on change_request)
3. Give Jira this user's credentials OR set up OAuth 2.0
4. In Jira: Automation > When issue resolved > Send webhook to SN endpoint
URL: [Link]
Auth: Basic <jira_sn_integration credentials>
9. SOAP Integration — legacy systems deep dive
SOAP (Simple Object Access Protocol) is an XML-based web service protocol used heavily in legacy enterprise systems
(SAP, older Oracle EBS, banking systems). ServiceNow supports both inbound SOAP (expose SN data via SOAP) and
outbound SOAP (call external SOAP services).
// ■■ OUTBOUND SOAP call to a legacy HR system ■■■■■■■■■■■■■■■■■■■■■
// Navigate: System Web Services > Outbound > SOAP Messages > New
// Name: 'Legacy HR SOAP Service'
// WSDL URL: [Link]
// Click 'Get WSDL' — ServiceNow auto-generates methods from the WSDL
// Business Rule script using the SOAP Message:
var soapMsg = new sn_ws.SOAPMessageV2('Legacy HR SOAP Service', 'getEmployee');
[Link]('employeeId', current.u_employee_id);
[Link](15000); // 15 second timeout
try {
var response = [Link]();
[Link] | [Link] | [Link] REST SOAP Integration Expert Reference
ServiceNow REST & SOAP Integrations — Complete End-to-End Guide + Interview Q&A Page 12
var body = [Link]();
// SOAP responses are XML — use GlideXMLUtil to parse
var xmlDoc = new XMLDocument2();
[Link](body);
var salary = [Link]('//getEmployeeResponse/salary');
var dept = [Link]('//getEmployeeResponse/department');
current.u_salary_band = salary;
[Link](dept);
[Link]();
} catch(e) {
[Link]('HR SOAP call failed: ' + [Link]());
// ■■ INBOUND SOAP — expose a ServiceNow Web Service ■■■■■■■■■■■■■■■
// Navigate: System Web Services > Create a Scripted Web Service
// This exposes a SOAP endpoint other systems can call
// Generally AVOID for new integrations — use REST instead
// Only needed for: legacy SOAP clients that can't do REST
// or strict WS-Security environments
10. Import Set REST API — bulk inbound data push
// External system pushes employee data directly to SN Import Set staging
// No file needed — pure REST push
// Single record push:
POST /api/now/import/u_hr_employee_import
Auth: Basic sn_import_user:password
Body:
"u_employee_id": "EMP12345",
"u_first_name": "Sarah",
"u_last_name": "Connor",
"u_email": "[Link]@[Link]",
"u_department": "Engineering",
"u_status": "ACTIVE"
// ServiceNow immediately runs the associated Transform Map
// Response includes transform result:
"result": {
"status": "updated",
"sys_id": "user_sys_id_here",
"display_value": "[Link]"
[Link] | [Link] | [Link] REST SOAP Integration Expert Reference
ServiceNow REST & SOAP Integrations — Complete End-to-End Guide + Interview Q&A Page 13
// Batch push (multiple records in one call):
POST /api/now/import/u_hr_employee_import
Body: [
{ "u_employee_id": "EMP001", "u_first_name": "Alice", ... },
{ "u_employee_id": "EMP002", "u_first_name": "Bob", ... }
// Each record is transformed independently — one error doesn't block others
[Link] | [Link] | [Link] REST SOAP Integration Expert Reference
ServiceNow REST & SOAP Integrations — Complete End-to-End Guide + Interview Q&A Page 14
PART 4 — Interview Q&A (15 Questions)
What is the difference between the Table API, Scripted REST API, and Import Set
Q1 Mid
API in ServiceNow?
Table API: out-of-the-box, no config needed. Full CRUD on any table.
GET/POST/PATCH/DELETE /api/now/table/{table}. Best for simple integrations
where the external system can directly map to ServiceNow table fields.
Scripted REST API: custom endpoints YOU build. Full control over request
parsing, response structure, and business logic. Best when you need complex
validation, multiple table writes per call, or a cleaner API contract for
consumers. Import Set API: push raw data to a staging table, have Transform
Map handle all the mapping and coalescing. Best for bulk data loads where
field mapping logic is complex or maintained separately from the API contract.
Walk me through setting up OAuth 2.0 for an outbound REST integration with
Q2 Senior
Salesforce from ServiceNow.
1. In Salesforce: create a Connected App (Setup > App Manager > New),
enable OAuth settings, set callback URL to ServiceNow's oauth_redirect.do,
select required scopes (api, refresh_token), get Client ID and Secret.
2. In ServiceNow: System OAuth > Application Registry > New > 'Connect to
a third party OAuth provider'. Name: Salesforce | Client ID/Secret from
step 1 | Token URL: [Link] |
Auth URL: [Link]
3. Click 'Get OAuth Token' — ServiceNow browser redirects to Salesforce
login, you authorize, token is stored in ServiceNow's OAuth token table.
4. Create REST Message: Auth Type = OAuth 2.0, select the OAuth profile.
5. ServiceNow auto-handles token refresh — when access token expires (1h),
it uses the refresh token to get a new one transparently.
CODING: Write a Scripted REST API endpoint that accepts a JSON payload from
Q3 Coding
an external monitoring tool and creates a ServiceNow Incident.
// Scripted REST API Resource: POST /api/x_myco/monitoring/alert
(function process(request, response) {
// ■■ Input validation ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
var payload;
[Link] | [Link] | [Link] REST SOAP Integration Expert Reference
ServiceNow REST & SOAP Integrations — Complete End-to-End Guide + Interview Q&A Page 15
try {
payload = [Link]([Link]);
} catch(e) {
[Link](400);
[Link]({ error: 'Invalid JSON' });
return;
var required = ['summary', 'severity', 'source_system'];
for (var i = 0; i < [Link]; i++) {
if (!payload[required[i]]) {
[Link](422);
[Link]({ error: 'Missing field: ' + required[i] });
return;
// ■■ Map severity to ServiceNow priority ■■■■■■■■■■■■■■■■■■■■■■■■
var priorityMap = {
'critical': '1', 'high': '2', 'medium': '3', 'low': '4'
};
var priority = priorityMap[[Link]()] || '3';
// ■■ Find assignment group from source system ■■■■■■■■■■■■■■■■■■■
var groupName = [Link](
'[Link].' + payload.source_system, 'Service Desk');
// ■■ Deduplicate: check for existing open incident ■■■■■■■■■■■■■■
var existing = new GlideRecord('incident');
if (payload.alert_id) {
[Link]('u_external_alert_id', payload.alert_id);
[Link]('state', 'IN', '1,2,3');
[Link]();
if ([Link]()) {
[Link](200);
[Link]({
status: 'existing',
[Link] | [Link] | [Link] REST SOAP Integration Expert Reference
ServiceNow REST & SOAP Integrations — Complete End-to-End Guide + Interview Q&A Page 16
number: [Link]('number'),
sys_id: [Link]()
});
return;
// ■■ Create the Incident ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
var inc = new GlideRecord('incident');
[Link]();
inc.short_description = '[' + payload.source_system + '] ' + [Link];
[Link] = [Link] || '';
[Link] = priority;
[Link] = 'monitoring';
inc.u_external_alert_id = payload.alert_id || '';
inc.u_source_system = payload.source_system;
inc.assignment_group.setDisplayValue(groupName);
var sysId = [Link]();
[Link](201);
[Link]({
status: 'created',
number: [Link]('number'),
sys_id: sysId
});
})(request, response);
How do you secure a Scripted REST API endpoint that receives webhooks from
Q4 Senior
external systems?
Multiple layers: 1) Basic Auth or OAuth 2.0 on the endpoint — the external
system must authenticate. Create a dedicated service account with minimum
required role (only the role needed to write to the target table). Never
use admin credentials for integration service accounts. 2) HMAC signature
validation — many webhook providers (GitHub, Stripe, Jira) sign the request
body with a shared secret. In your Scripted REST script, recompute the
[Link] | [Link] | [Link] REST SOAP Integration Expert Reference
ServiceNow REST & SOAP Integrations — Complete End-to-End Guide + Interview Q&A Page 17
HMAC signature from the raw body and compare it to the X-Signature header.
If they don't match, return 401 immediately. 3) IP allowlisting — in
System Properties or via a custom validation, check [Link]()
against an allowlist of the external system's egress IPs. 4) Rate limiting
— track calls from the integration user in a custom table and return 429
if they exceed your defined threshold in a time window.
What is the difference between synchronous and asynchronous outbound REST
Q5 Senior
calls and when do you use each?
Synchronous ([Link]()): the script WAITS for the external API to respond
before continuing. The calling user's browser is blocked during this wait.
Use ONLY in: Async Business Rules (background thread, user already returned)
Scheduled Jobs, Script Includes called from background scripts. NEVER in
Before/After Business Rules triggered by user saves — if the API takes 5
seconds, the user waits 5 seconds for their save to complete.
Asynchronous ([Link]()): script fires the request and returns
immediately. The response is processed in a callback or ignored.
Use in: After Business Rules, non-blocking notification calls, fire-and-forget
integrations. The golden rule: if you're in a Business Rule triggered by a user
save, use either Async Business Rule type OR [Link]() to avoid
degrading the user experience with external API latency.
CODING: Write a complete OAuth 2.0 token refresh handler for a Scripted REST
Q6 Coding
API outbound call.
function callWithTokenRefresh(restMsgName, methodName, params) {
var rm = new sn_ws.RESTMessageV2(restMsgName, methodName);
// Apply params
for (var key in params) {
[Link](key, params[key]);
var response = [Link]();
// If 401, token expired — force refresh and retry once
if ([Link]() == 401) {
[Link]('Token expired — refreshing OAuth token for ' + restMsgName);
[Link] | [Link] | [Link] REST SOAP Integration Expert Reference
ServiceNow REST & SOAP Integrations — Complete End-to-End Guide + Interview Q&A Page 18
// Clear cached token to force re-authentication
var tokenGr = new GlideRecord('oauth_credential');
[Link]('credential_store.name', restMsgName + ' OAuth');
[Link]();
if ([Link]()) {
tokenGr.access_token = '';
tokenGr.expire_time = '';
[Link]();
// Retry — new token fetched automatically
rm = new sn_ws.RESTMessageV2(restMsgName, methodName);
for (var k in params) [Link](k, params[k]);
response = [Link]();
return response;
How do you handle bidirectional sync between ServiceNow and Jira without
Q7 Senior
creating infinite loops?
The key is a 'source system' flag to prevent echoes. Pattern:
1. Add a field u_jira_key to the SN record and u_servicenow_id to the Jira issue.
2. Add a field u_syncing_from_jira (boolean) on the SN record.
3. Outbound (SN -> Jira): Business Rule fires on SN change.
Condition: current.u_syncing_from_jira == false (don't sync if update
came FROM Jira — prevents echo). Set Jira issue via REST call.
4. Inbound (Jira -> SN): Jira webhook calls your Scripted REST API.
Script sets u_syncing_from_jira = true before updating, then
calls update(), then sets it back to false after. The Business Rule
condition prevents the outbound call from firing during this update.
5. Alternatively: check if the changing fields are ONLY ones that Jira
updates — if so, skip the outbound call. More elegant but harder to maintain.
[Link] | [Link] | [Link] REST SOAP Integration Expert Reference