Ryan Smith3
ServiceNow Employee

The Problem

If you’ve ever inherited a ServiceNow instance or started a platform rationalization project, you’ve hit this question: what is creating incidents, and where is it coming from?

 

Incidents can arrive through REST APIs, inbound email, import sets, scripted REST endpoints, Flow Designer, MID Server integrations, or business rules triggered on entirely different tables. Some are documented but most aren't. Some are even reportable from a dashboard but not granular enough. And the longer an instance has been running, the more ghost integrations accumulate.

 

This toolkit gives you 11 copy-paste background scripts and 12 encoded queries to map every incident creation pathway on your instance. Run them in Scripts - Background, export the results from syslog, and you’ll have a complete integration inventory in under an hour.

 

Gotchas this toolkit accounts for

A few ServiceNow-specific issues can silently break background scripts. These were all tested on Zurich. They do not account for custom fields that may be used in place of OOB fields referenced. They’re all handled in this toolkit, but worth knowing about:

Issue

Impact

How It’s Handled

orderByAggregate('COUNT', 'DESC')

Second parameter expects a field name, not a sort direction. Query silently returns zero rows.

All scripts use array sort instead.

groupBy('from') on sys_email

'from' is a SQL reserved keyword. GlideAggregate silently skips the groupBy.

Script 4 uses GlideRecord with manual aggregation.

web_service_access_only flag

Many integration accounts don’t have this flag set.

Script 2 classifies from incident creators inward using multiple signals.

addNullQuery('opened_by')

Business rules populate opened_by on virtually every record.

Script 2 Step C groups by created_by + opened_by and filters to mismatches.

Object.keys() in Rhino

Not supported on older ServiceNow JS engines. Silently fails.

All scripts use for-in loops and array patterns.

 

How output capture works

Important: 

 

All scripts use gs.log with source tag INTG_AUDIT so output is stored in syslog for export. After running, navigate to System Logs > System Log > All, filter by Source = INTG_AUDIT, then right-click the column header and select Export > CSV to download the full audit trail.

 

Script index

Script

Purpose

What It Answers

1

Quick scan: incident volume by creator

Who is creating incidents and how many?

2

Classification + impersonation analysis

Which creators are integrations vs humans? Are they impersonating users?

3

Transaction log - API evidence

What actual HTTP calls hit the incident table? (Authoritative source)

4

Inbound email audit

Are emails creating incidents? From which senders?

5

Config audit: Scripted REST, transforms, OAuth

What’s configured to create incidents? Which integrations are registered?

6

Connection & credential inventory

What outbound connections exist? Which classes and endpoints?

7

Outbound HTTP logging (enable + query)

What payloads are being sent/received on outbound calls?

8

ECC Queue payloads

What data flows through MID Server integrations?

9

Import set rows

What inbound file/JDBC feeds write to incident?

10

Flow Designer executions

Which flows ran recently and what did they process?

11

REST message HTTP log

Which REST Messages are active and what did they send?

Tip: Recommended order: Run Script 1 first for the 80/20 view, then Script 2 for classification, then Script 3 for authoritative API evidence. Scripts 4-5 audit configuration. Scripts 6-11 are for deep payload inspection - run as needed.

 

Part 1: Rationalization Scripts

Run these in Scripts - Background (System Definition > Scripts - Background). All scripts use gs.log with source tag INTG_AUDIT so output is both printed to screen and stored in the syslog table for export.

 

Tip: Run Scripts 1-5 in order for a complete rationalization pass. Script 1 gives the quick view, Script 2 classifies the creators, Script 3 validates with API evidence, Script 4 checks email, and Script 5 audits what’s configured. All output is tagged INTG_AUDIT in syslog - filter by Source = INTG_AUDIT and export to CSV for a clean audit file. Then move to Part 3 (connections) and Part 4 (payloads) to go deeper.

 

Script 1 - Quick scan: incident volume by creator

Groups all incidents by sys_created_by to surface service accounts vs. human users. Sorted by count descending. Run this first to see the lay of the land.

// Script 1: Incident creation volume by sys_created_by (last 90 days)
// Sorted by count descending via array sort
// Output tagged with source INTG_AUDIT for syslog filtering
function log(msg) { gs.log(msg, 'INTG_AUDIT'); }

var results = [];
var gr = new GlideAggregate('incident');
gr.addQuery('sys_created_on', '>', gs.daysAgoStart(90));
gr.addAggregate('COUNT');
gr.groupBy('sys_created_by');
gr.query();

while (gr.next()) {
    results.push({
        user: '' + (gr.getValue('sys_created_by') || '(empty)'),
        count: parseInt(gr.getAggregate('COUNT'), 10)
    });
}

results.sort(function(a, b) { return b.count - a.count; });

log('=== INCIDENT CREATION BY SOURCE (LAST 90 DAYS) ===');
log('Created By | Count');
log('----------------------------');
for (var i = 0; i < results.length; i++) {
    log(results[i].user + ' | ' + results[i].count);
}

log('=== DONE ===');

 

Script 2 - Classification + impersonation analysis

Classifies every incident creator against sys_user (service account flag, last login, naming convention), then shows created_by vs opened_by mismatches to surface API impersonation. Run as one script - Step B builds a userMap that Step C reuses for zero additional lookups.

// =============================================================
// Script 2: Classification + Impersonation Analysis (last 90 days)
// Run as one script - Step C reuses Step B’s userMap
// =============================================================
// Output tagged with source INTG_AUDIT for syslog filtering
function log(msg) { gs.log(msg, 'INTG_AUDIT'); }

// ----- STEP B: Classify all incident creators -----

// Phase 1: Collect all creators and counts
var creators = [];
var usernames = [];
var ga = new GlideAggregate('incident');
ga.addQuery('sys_created_on', '>', gs.daysAgoStart(90));
ga.addAggregate('COUNT');
ga.groupBy('sys_created_by');
ga.query();

while (ga.next()) {
    var uname = '' + (ga.getValue('sys_created_by') || '(empty)');
    creators.push({ user: uname, count: parseInt(ga.getAggregate('COUNT'), 10) });
    usernames.push(uname);
}

// Phase 2: Batch lookup all users in one query (replaces N individual lookups)
var userMap = {};
if (usernames.length > 0) {
    var usr = new GlideRecord('sys_user');
    usr.addQuery('user_name', 'IN', usernames.join(','));
    usr.query();
    while (usr.next()) {
        userMap['' + usr.getValue('user_name')] = {
            wsOnly: (usr.getValue('web_service_access_only') == 'true') ? 'YES' : 'NO',
            lastLogin: usr.getValue('last_login_time') || 'NEVER',
            displayName: '' + usr.getDisplayValue('name')
        };
    }
}

// Phase 3: Classify, sort, and print
for (var i = 0; i < creators.length; i++) {
    var u = userMap[creators[i].user];
    if (u) {
        creators[i].wsOnly = u.wsOnly;
        creators[i].lastLogin = u.lastLogin;
        creators[i].displayName = u.displayName;
        if (u.wsOnly == 'YES') {
            creators[i].type = 'SERVICE_ACCOUNT';
        } else if (u.lastLogin == 'NEVER') {
            creators[i].type = 'LIKELY_INTEGRATION';
        } else if (
            creators[i].user.indexOf('.integration') > -1 ||
            creators[i].user.indexOf('.api') > -1 ||
            creators[i].user.indexOf('.bot') > -1 ||
            creators[i].user.indexOf('svc') > -1 ||
            creators[i].user.indexOf('SVC') > -1
        ) {
            creators[i].type = 'LIKELY_INTEGRATION';
        } else {
            creators[i].type = 'HUMAN';
        }
    } else {
        creators[i].wsOnly = 'N/A';
        creators[i].lastLogin = 'N/A';
        creators[i].displayName = '';
        creators[i].type = 'NO_USER_RECORD';
    }
}

creators.sort(function(a, b) { return b.count - a.count; });

log('=== STEP B: INCIDENT CREATORS CLASSIFIED (LAST 90 DAYS) ===');
log('Created By | Count | Classification | web_svc_only | Last Login');
log('-------------------------------------------------------------');
for (var j = 0; j < creators.length; j++) {
    var c = creators[j];
    log(c.user + ' | ' + c.count + ' | ' + c.type + ' | ' + c.wsOnly + ' | ' + c.lastLogin);
}

// ----- STEP C: Created_by vs Opened_by (impersonation signal) -----
// Reuses userMap from Step B above - zero additional user lookups

var mismatches = [];
var ga2 = new GlideAggregate('incident');
ga2.addQuery('sys_created_on', '>', gs.daysAgoStart(90));
ga2.addAggregate('COUNT');
ga2.groupBy('sys_created_by');
ga2.groupBy('opened_by');
ga2.query();

while (ga2.next()) {
    var createdBy = '' + (ga2.getValue('sys_created_by') || '(empty)');
    var openedBy = '' + (ga2.getDisplayValue('opened_by') || '(empty)');
    var cnt = parseInt(ga2.getAggregate('COUNT'), 10);

    var createdByDisplay = userMap[createdBy] ? userMap[createdBy].displayName : '';

    var match = (createdByDisplay == openedBy) ? 'SAME' : 'DIFFERENT';
    if (openedBy == '(empty)') { match = 'NO_OPENER'; }

    if (match != 'SAME') {
        mismatches.push({ createdBy: createdBy, openedBy: openedBy, count: cnt, match: match });
    }
}

mismatches.sort(function(a, b) { return b.count - a.count; });

log('');
log('=== STEP C: CREATED_BY vs OPENED_BY BREAKDOWN (LAST 90 DAYS) ===');
log('Created By | Opened By | Count | Match');
log('-------------------------------------------------------------');
for (var k = 0; k < mismatches.length; k++) {
    var m = mismatches[k];
    log(m.createdBy + ' | ' + m.openedBy + ' | ' + m.count + ' | ' + m.match);
}

log('=== DONE ===');

 

Script 3 - Transaction log: authoritative API evidence

Queries syslog_transaction for all transactions touching the incident table. This is the definitive source - it logs every HTTP transaction with the authenticated user, method, URI, and status code. Cross-reference with Script 2 to validate classification results.

// Script 3: Transactions touching the incident table (last 7 days)
// syslog_transaction is the authoritative record of every transaction
// NOTE: Use a short window (7 days) - this table is high-volume
// Output tagged with source INTG_AUDIT for syslog filtering
function log(msg) { gs.log(msg, 'INTG_AUDIT'); }

var txnCounts = {};
var gr = new GlideRecord('syslog_transaction');
gr.addQuery('table', 'incident');
gr.addQuery('sys_created_on', '>', gs.daysAgoStart(7));
gr.query();

while (gr.next()) {
    var key = '' + (gr.getValue('sys_created_by') || '(empty)') + ' || ' + gr.getValue('type');
    if (!txnCounts[key]) {
        txnCounts[key] = {
            user: '' + (gr.getValue('sys_created_by') || '(empty)'),
            url: '' + gr.getValue('url'),
            status: '' + gr.getValue('status'),
            count: 0
        };
    }
    txnCounts[key].count = txnCounts[key].count + 1;
}

var txnResults = [];
for (var k in txnCounts) {
    txnResults.push(txnCounts[k]);
}
txnResults.sort(function(a, b) { return b.count - a.count; });

log('=== TRANSACTIONS TOUCHING INCIDENT TABLE (LAST 7 DAYS) ===');
log('User | Type | Sample URL | Count');
log('-------------------------------------------------------------');
for (var t = 0; t < txnResults.length; t++) {
    var r = txnResults[t];
    log(r.user + ' | ' + r.type + ' | ' + r.url + ' | ' + r.count);
}

log('=== DONE ===');

 

Script 4 - Inbound email audit

Checks the email log for inbound messages that resulted in an incident record. If this returns no results, the instance has no email-created incidents in the time window.

// Script 4: Inbound emails that created incidents (last 90 days)
// 'from' is a SQL reserved word - GlideAggregate.groupBy('from') fails
// silently, so we use GlideRecord with manual aggregation
// Output tagged with source INTG_AUDIT for syslog filtering
function log(msg) { gs.log(msg, 'INTG_AUDIT'); }

var emailCounts = {};
var gr = new GlideRecord('sys_email');
gr.addQuery('type', 'received');
gr.addQuery('target_table', 'incident');
gr.addQuery('sys_created_on', '>', gs.daysAgoStart(90));
gr.query();

while (gr.next()) {
    var sender = '' + (gr.getValue('from') || '(empty)');
    if (!emailCounts[sender]) {
        emailCounts[sender] = 0;
    }
    emailCounts[sender] = emailCounts[sender] + 1;
}

var emailResults = [];
for (var addr in emailCounts) {
    emailResults.push({ from: addr, count: emailCounts[addr] });
}
emailResults.sort(function(a, b) { return b.count - a.count; });

log('=== INBOUND EMAILS CREATING INCIDENTS (LAST 90 DAYS) ===');
log('From Address | Count');
log('----------------------------');
for (var e = 0; e < emailResults.length; e++) {
    log(emailResults[e].from + ' | ' + emailResults[e].count);
}

log('=== DONE ===');

 

Script 5 - Configuration audit: Scripted REST, transforms, OAuth

Runs three lightweight config queries in one script: (A) Scripted REST API resources referencing incident, (B) Transform maps targeting incident, (C) Active OAuth applications. Shows what’s configured to interact with the incident table - cross-reference with Scripts 1-3 to see which are actually active.

// =============================================================
// Script 5: Configuration Audit (Scripted REST + Transforms + OAuth)
// =============================================================
// Output tagged with source INTG_AUDIT for syslog filtering
function log(msg) { gs.log(msg, 'INTG_AUDIT'); }

// ----- 5A: Scripted REST APIs referencing incident -----
var gr = new GlideRecord('sys_ws_operation');
gr.addQuery('operation_script', 'CONTAINS', 'incident');
gr.query();

log('=== 5A: SCRIPTED REST RESOURCES REFERENCING INCIDENT ===');
log('API Name | Resource | HTTP Method | Active');
log('------------------------------------------');
while (gr.next()) {
    log(
        gr.getDisplayValue('web_service_definition') + ' | ' +
        gr.getValue('name') + ' | ' +
        gr.getValue('http_method') + ' | ' +
        gr.getValue('active')
    );
}

// ----- 5B: Transform maps targeting incident -----
var gr2 = new GlideRecord('sys_transform_map');
gr2.addQuery('target_table', 'incident');
gr2.query();

log('');
log('=== 5B: TRANSFORM MAPS TARGETING INCIDENT ===');
log('Name | Source Table | Active | Run Order');
log('------------------------------------------');
while (gr2.next()) {
    log(
        gr2.getValue('name') + ' | ' +
        gr2.getValue('source_table') + ' | ' +
        gr2.getValue('active') + ' | ' +
        gr2.getValue('order')
    );
}

// ----- 5C: Active OAuth applications -----
var gr3 = new GlideRecord('oauth_entity');
gr3.addQuery('active', true);
gr3.query();

log('');
log('=== 5C: ACTIVE OAUTH APPLICATIONS ===');
log('Name | Client ID | Type | Active');
log('------------------------------------------');
while (gr3.next()) {
    log(
        gr3.getValue('name') + ' | ' +
        gr3.getValue('client_id') + ' | ' +
        gr3.getValue('type') + ' | ' +
        gr3.getValue('active')
    );
}

log('=== DONE ===');

 

Part 2: Encoded Queries

Paste these into the list view filter bar, or append to the URL: https://<instance>.service-now.com/<table>_list.do?sysparm_query=<encoded_query>

 

Tip: Navigate to the target table’s list view, click the filter icon, right-click the breadcrumb, and select ‘Insert Encoded Query’. Or append to the URL directly.

 

Query 1 Example: https://<instance>.service-now.com/incident_list.do?sysparm_query=sys_created_onRELATIVEGT@dayofweek@ago@90^ORDERBYDESCsys_created_by

#

Query Name

Target Table

Encoded Query

1

Incidents by creator (90 days)

incident

sys_created_onRELATIVEGT@dayofweek@ago@90^ORDERBYDESCsys_created_by

2

Incidents by specific account

incident

sys_created_by=YOUR_SERVICE_ACCOUNT^sys_created_onRELATIVEGT@dayofweek@ago@90^ORDERBYDESCsys_created_on

3

Transactions touching incident (7 days)

syslog_transaction

table=incident^sys_created_onRELATIVEGT@dayofweek@ago@7^ORDERBYDESCsys_created_on

4

Inbound emails targeting incident

sys_email

type=received^target_table=incident^sys_created_onRELATIVEGT@dayofweek@ago@90^ORDERBYDESCsys_created_on

5

Active inbound email actions for incident

sysevent_in_email_action

table=incident^active=true^ORDERBYname

6

Scripted REST resources referencing incident

sys_ws_operation

operation_scriptCONTAINSincident^ORDERBYname

7

Transform maps targeting incident

sys_transform_map

target_table=incident^ORDERBYname

8

Active OAuth applications

oauth_entity

active=true^ORDERBYname

9

Service accounts (web_service_access_only)

sys_user

web_service_access_only=true^active=true^ORDERBYuser_name

10

Active connections by class

sys_connection

active=true^ORDERBYsys_class_name

11

Connection & credential aliases

sys_alias

type=connection^ORDERBYname

12

All INTG_AUDIT script output (for export)

syslog

source=INTG_AUDIT^sys_created_onRELATIVEGT@dayofweek@ago@7^ORDERBYDESCsys_created_on

 

Query details

1  Incidents by creator (90 days)

Paste into the Incident list filter. Group by sys_created_by column to see volume per source. Companion to Script 1.

Table: incident

sys_created_onRELATIVEGT@dayofweek@ago@90^ORDERBYDESCsys_created_by

2  Incidents by specific account

Replace YOUR_SERVICE_ACCOUNT with a username from Script 1 or Script 2 output. Drill into individual integration activity.

Table: incident

sys_created_by=YOUR_SERVICE_ACCOUNT^sys_created_onRELATIVEGT@dayofweek@ago@90^ORDERBYDESCsys_created_on

3  Transactions touching incident (7 days)

Authoritative API transaction log. Companion to Script 3. Use a short window - syslog_transaction is high-volume.

Table: syslog_transaction

table=incident^sys_created_onRELATIVEGT@dayofweek@ago@7^ORDERBYDESCsys_created_on

4  Inbound emails targeting incident

Shows every received email that created or updated an incident. Companion to Script 4.

Table: sys_email

type=received^target_table=incident^sys_created_onRELATIVEGT@dayofweek@ago@90^ORDERBYDESCsys_created_on

5  Active inbound email actions for incident

Lists the rules that govern how inbound emails become incidents.

Table: sysevent_in_email_action

table=incident^active=true^ORDERBYname

6  Scripted REST resources referencing incident

Companion to Script 5A. Lists custom API endpoints that interact with incident. Note; this may include other incident related operations (ex: major incident, security incident). Modify filter as needed to refine.

Table: sys_ws_operation

operation_scriptCONTAINSincident^ORDERBYname

7  Transform maps targeting incident

Companion to Script 5B. Shows import-set-based integrations writing to incident.

Table: sys_transform_map

target_table=incident^ORDERBYname

8  Active OAuth applications

Companion to Script 5C. Lists external systems registered for API access.

Table: oauth_entity

active=true^ORDERBYname

9  Service accounts (web_service_access_only)

Lists active service accounts. NOTE: Script 2 showed this flag is not comprehensive - many integration accounts lack it. Use this as a starting point, not a definitive list.

Table: sys_user

web_service_access_only=true^active=true^ORDERBYuser_name

10  Active connections by class

Companion to Script 6. Group by sys_class_name column for connection type breakdown.

Table: sys_connection

active=true^ORDERBYsys_class_name

11  Connection & credential aliases

Lists every alias configured for Integration Hub spokes and Flow Designer actions.

Table: sys_alias

type=connection^ORDERBYname

12  All INTG_AUDIT script output (for export)

Output from Scripts 1-11 in the last 7 days. Adjust the window if you ran scripts earlier. Right-click column header > Export > CSV to download the full audit trail. Filter further by Message contains '=== Script' to see section headers only. Warning: syslog is large so modify filter as neeeded

Table: syslog

source=INTG_AUDIT^sys_created_onRELATIVEGT@dayofweek@ago@7^ORDERBYDESCsys_created_on

 

Part 3: Connection & Credential Audit

Audit outbound connections and credential aliases. Navigate via Connections & Credentials > Connections, or use the list views below. Cross-reference with Script 3 (transaction log) to see which connections are actively creating incidents.

Table

Navigation

What It Shows

sys_connection

sys_connection.do

All configured connections (REST, JDBC, SOAP, etc.) - the primary list of outbound integration endpoints.

sys_alias

sys_alias.do

Connection & Credential Aliases used by Flow Designer actions and Integration Hub spokes.

Tip: Use sys_class_name instead of the type field for accurate grouping. Cross-reference 6A results with Script 3 (transaction log) to see which connection types are actively creating incidents.

 

Script 6 - Connection inventory: grouped + detailed

Runs two queries in one script: (A) active connections grouped by class with count, sorted descending, and (B) detailed inventory with name, class, host, and credential alias.

// =============================================================
// Script 6: Connection Inventory (grouped + detailed)
// =============================================================
// Output tagged with source INTG_AUDIT for syslog filtering
function log(msg) { gs.log(msg, 'INTG_AUDIT'); }

// ----- 6A: Active connections grouped by class -----

var connResults = [];
var gr = new GlideAggregate('sys_connection');
gr.addQuery('active', true);
gr.addAggregate('COUNT');
gr.groupBy('sys_class_name');
gr.query();

while (gr.next()) {
    connResults.push({
        cls: '' + gr.getValue('sys_class_name'),
        count: parseInt(gr.getAggregate('COUNT'), 10)
    });
}

connResults.sort(function(a, b) { return b.count - a.count; });

log('=== 6A: ACTIVE CONNECTIONS BY CLASS ===');
log('Class | Count');
log('----------------------------');
for (var i = 0; i < connResults.length; i++) {
    log(connResults[i].cls + ' | ' + connResults[i].count);
}


// ----- 6B: Detailed connection inventory -----

var gr2 = new GlideRecord('sys_connection');
gr2.addQuery('active', true);
gr2.orderBy('sys_class_name');
gr2.query();

log('');
log('=== 6B: DETAILED CONNECTION INVENTORY ===');
log('Name | Class | Host/URL | Credential Alias');
log('------------------------------------------');
while (gr2.next()) {
    var host = gr2.getValue('host') || gr2.getValue('connection_url') || 'N/A';
    var cred = gr2.getDisplayValue('credential') || 'N/A';
    log(
        gr2.getValue('name') + ' | ' +
        gr2.getValue('sys_class_name') + ' | ' +
        host + ' | ' +
        cred
    );
}

log('=== DONE ===');

Part 4: Payload Inspection – Use as Needed

Scripts 1-6 answer the question who is creating incidents. Part 4 answers what they’re sending. These scripts are only necessary when you need to inspect actual payloads - for example, to audit field mappings, debug malformed data, or verify that integrations are sending the correct content.

 

WARNING: Script 7 enables outbound HTTP logging at the instance level. This generates significant log volume and can degrade instance performance if left on. Enable it temporarily (on large instances, 1-2 hours is sufficient; you’ll capture thousands of calls. On smaller instances, 4-8 hours covers scheduled job cycles), run your inspection, then immediately disable with Script 7C. Never leave outbound HTTP logging enabled in production.

Method

Direction

What It Captures

Outbound HTTP Log (Script 7)

Outbound

Full HTTP request/response bodies for all outbound REST and SOAP calls. Enable temporarily via system property.

ECC Queue (Script 8

Both

Raw XML/JSON payloads for MID Server integrations (SOAP, JDBC, Orchestration, Discovery). Retained ~7 days.

Import Set Rows (Script 9)

Inbound

Raw inbound data before transformation. Retained based on import set cleanup schedule.

Flow Executions (Script 10)

Both

Input/output data for each Flow Designer action step. Already captured per execution.

REST Message Log (Script 11)

Outbound

Request/response per configured REST Message. Enable HTTP Log checkbox on individual records.

Tip: Recommended sequence:

(1) Run Script 7A to enable outbound HTTP logging, wait 2 hrs (large instances) or up to 8 hours (small instances, then run 7B.

(2) Check ECC Queue (Script 8 MID-routed data.

(3) Check import sets (Script 9).

(4) Use Flow Designer UI with Script 10 sys_ids for spoke payloads.

(5) Run Script 11 for REST Message details. Disable logging with Script 7C when done.

 

Script 7 - Outbound HTTP logging: enable + query

Enables full request/response logging for all outbound HTTP calls, then queries the results. WARNING: generates significant log volume - enable temporarily, investigate, then run the disable block.

(1) Run Script 7A to enable outbound HTTP logging, wait 2 hrs (large instances) or up to 8 hours (small instances, then run 7B.

// =============================================================
// Script 7: Outbound HTTP Logging (enable + query)
// WARNING: Enable temporarily only - high log volume
// =============================================================
// Output tagged with source INTG_AUDIT for syslog filtering
function log(msg) { gs.log(msg, 'INTG_AUDIT'); }

// ----- 7A: Enable logging -----
gs.setProperty('glide.outbound_http.log.enabled', 'true');
log('Outbound HTTP logging ENABLED');

log('=== DONE ===');

7B Includes logic to Group by URL + method, counts, and sorts by high volume

// ----- 7B: Query results (run after 1-2 hrs of logging) -----
// Output tagged with source INTG_AUDIT for syslog filtering
function log(msg) { gs.log(msg, 'INTG_AUDIT'); }

// Adjust time window to match how long you left logging enabled
// Adjust setLimit to control how many records display on screen
var urlCounts = {};
var gr = new GlideRecord('sys_outbound_http_log');
gr.addQuery('sys_created_on', '>', gs.hoursAgoStart(2));
gr.query();

while (gr.next()) {
    var key = '' + (gr.getValue('url') || '(empty)') + ' || ' + (gr.getValue('request_method') || '');
    if (!urlCounts[key]) {
        urlCounts[key] = {
            url: '' + (gr.getValue('url') || '(empty)'),
            method: '' + (gr.getValue('request_method') || ''),
            status: '' + (gr.getValue('status_code') || ''),
            count: 0
        };
    }
    urlCounts[key].count = urlCounts[key].count + 1;
}

var urlResults = [];
for (var k in urlCounts) { urlResults.push(urlCounts[k]); }
urlResults.sort(function(a, b) { return b.count - a.count; });

log('=== OUTBOUND HTTP CALLS GROUPED BY URL (LAST 2 HRS) ===');
log('Count | Method | Status | URL');
log('-------------------------------------------------------------');
for (var i = 0; i < urlResults.length; i++) {
    var r = urlResults[i];
    log(r.count + ' | ' + r.method + ' | ' + r.status + ' | ' + r.url);
}

log('=== DONE ===');

(2) Check ECC Queue (Script 8 for MID-routed data.

(3) Check import sets (Script 9).

(4) Use Flow Designer UI with Script 10 sys_ids for spoke payloads.

(5) Run Script 11 for REST Message details. Disable logging with Script 7C when done.

// ----- 7C: Disable logging (run after investigation) -----
// Output tagged with source INTG_AUDIT for syslog filtering
function log(msg) { gs.log(msg, 'INTG_AUDIT'); }

gs.setProperty('glide.outbound_http.log.enabled', 'false');
log('Outbound HTTP logging DISABLED');

log('=== DONE ===');

 

Script 8 - ECC Queue payloads

Inspects raw payloads flowing through the ECC Queue - covers JDBC, SOAP, Discovery, Orchestration, and other MID-routed integrations.

// Script 8: ECC Queue payloads (last 7 days)
// Output tagged with source INTG_AUDIT for syslog filtering
function log(msg) { gs.log(msg, 'INTG_AUDIT'); }

var gr = new GlideRecord('ecc_queue');
gr.addQuery('sys_created_on', '>', gs.daysAgoStart(7));
gr.orderByDesc('sys_created_on');
gr.setLimit(50);
gr.query();

log('=== ECC QUEUE PAYLOADS (LAST 7 DAYS) ===');
log('Timestamp | Queue | Topic | Name | Agent');
log('------------------------------------------');
while (gr.next()) {
    log(
        gr.getValue('sys_created_on') + ' | ' +
        gr.getValue('queue') + ' | ' +
        gr.getValue('topic') + ' | ' +
        gr.getValue('name') + ' | ' +
        gr.getValue('agent')
    );
    log('  PAYLOAD: ' + (gr.getValue('payload') || '(empty)').substring(0, 500));
    log('---');
}

log('=== DONE ===');

 

Script 9 - Import set rows

Lists recent import sets targeting the incident table and shows the raw row data before transformation. Cross-reference with Script 5B (transform maps).

// Script 9: Recent import sets targeting incident (last 30 days)
// Output tagged with source INTG_AUDIT for syslog filtering
function log(msg) { gs.log(msg, 'INTG_AUDIT'); }

var gr = new GlideRecord('sys_import_set');
gr.addQuery('sys_created_on', '>', gs.daysAgoStart(30));
gr.addQuery('table_name', 'CONTAINS', 'incident');
gr.orderByDesc('sys_created_on');
gr.setLimit(20);
gr.query();

log('=== IMPORT SETS TARGETING INCIDENT (LAST 30 DAYS) ===');
log('Set Name | Table | State | Row Count | Created');
log('------------------------------------------');
while (gr.next()) {
    log(
        gr.getValue('number') + ' | ' +
        gr.getValue('table_name') + ' | ' +
        gr.getDisplayValue('state') + ' | ' +
        gr.getValue('row_count') + ' | ' +
        gr.getValue('sys_created_on')
    );
}

// To inspect actual row data for a specific import set:
// var rows = new GlideRecord('<staging_table_name>');
// rows.addQuery('sys_import_set', '<import_set_sys_id>');
// rows.setLimit(5);
// rows.query();
// while (rows.next()) {
//     log(JSON.stringify(rows.getFields()));
// }

log('=== DONE ===');

 

Script 10 - Flow Designer executions

Lists recent Flow Designer executions and their context - use the sys_id to drill into step-level input/output in the Flow Designer UI.

// Script 10: Recent flow executions (last 7 days)
// Output tagged with source INTG_AUDIT for syslog filtering
function log(msg) { gs.log(msg, 'INTG_AUDIT'); }

var gr = new GlideRecord('sys_flow_context');
gr.addQuery('sys_created_on', '>', gs.daysAgoStart(7));
gr.addQuery('state', 'COMPLETE');
gr.orderByDesc('sys_created_on');
gr.setLimit(50);
gr.query();

log('=== FLOW DESIGNER EXECUTIONS (LAST 7 DAYS) ===');
log('Flow Name | State | Started | Source Table | Source Record');
log('------------------------------------------');
while (gr.next()) {
    log(
        gr.getDisplayValue('flow') + ' | ' +
        gr.getDisplayValue('state') + ' | ' +
        gr.getValue('sys_created_on') + ' | ' +
        gr.getValue('source_table') + ' | ' +
        gr.getValue('source_record')
    );
}

log('=== DONE ===');

 

Script 11 - REST message HTTP log

Queries the REST Message function log for outbound REST call details. Also audits which REST Messages have HTTP logging enabled so you can turn it on for the ones you need to inspect.

// =============================================================
// Script 11: REST Message HTTP Log + Logging Audit
// =============================================================
// Output tagged with source INTG_AUDIT for syslog filtering
function log(msg) { gs.log(msg, 'INTG_AUDIT'); }

// ----- 11A: Which REST Messages have HTTP logging enabled? -----
var rm = new GlideRecord('sys_rest_message');
rm.query();

log('=== 11A: REST MESSAGES - HTTP LOG STATUS ===');
log('Name | HTTP Log Enabled | Endpoint');
log('------------------------------------------');
while (rm.next()) {
    log(
        rm.getValue('name') + ' | ' +
        rm.getValue('use_http_log') + ' | ' +
        rm.getValue('rest_endpoint')
    );
}


// ----- 11B: Query the function log for recent executions -----
var gr = new GlideRecord('sys_rest_message_fn_log');
if (gr.isValid()) {
    gr.addQuery('sys_created_on', '>', gs.daysAgoStart(7));
    gr.orderByDesc('sys_created_on');
    gr.setLimit(30);
    gr.query();

    log('');
    log('=== 11B: REST MESSAGE FUNCTION LOG (LAST 7 DAYS) ===');
    log('Timestamp | Function | HTTP Method | Endpoint | Status');
    log('------------------------------------------');
    while (gr.next()) {
        log(
            gr.getValue('sys_created_on') + ' | ' +
            gr.getDisplayValue('rest_message_fn') + ' | ' +
            gr.getValue('http_method') + ' | ' +
            gr.getValue('endpoint') + ' | ' +
            gr.getValue('http_status')
        );
        log('  REQ: ' + (gr.getValue('request_body') || '(empty)').substring(0, 500));
        log('  RES: ' + (gr.getValue('response_body') || '(empty)').substring(0, 500));
        log('---');
    }
} else {
    log('sys_rest_message_fn_log table not found on this instance.');
    log('Use Script 7 (outbound HTTP log) instead.');
}

// To enable HTTP logging on a specific REST Message:
// System Web Services > Outbound > REST Message
// Open the message > check 'HTTP Log' > Save

log('=== DONE ===');

 

Putting It All Together

After running all 11 scripts, you’ll have a complete picture of incident integration activity on your instance:

Layer

Scripts

What You Learn

Who is creating incidents?

1, 2

Volume per creator, human vs integration classification, impersonation patterns

Proof it happened

3

Authoritative HTTP transaction evidence from syslog_transaction

What channels are active?

4, 5

Email, Scripted REST, transform maps, OAuth registrations

What connections exist?

6

Connection types, endpoints, credential aliases

What data is flowing?

7-11

Outbound payloads, ECC Queue, import sets, flows, REST message logs

 

To export your full audit: paste encoded query #12 into the syslog list view (source=INTG_AUDIT), then export to CSV. You’ll have a single file with every script’s output, timestamped and ready for your rationalization report.

 

These scripts are designed for rationalization, not production use. Adjust the time windows (gs.daysAgoStart values) to match your instance’s data retention. If you hit performance issues on a large instance, start with Scripts 1 and 3 - they give you the highest signal with the lowest cost.

 

Questions or improvements? Drop them in the comments.