icon-unified.svg
Experience Center

ServiceNow Webhook Configuration Guide for Developers

This guide provides information on configuring webhooks using ServiceNow for alerts in Digital Experience Monitoring. This article provides a sample configuration that you can build upon per user requirements. The ServiceNow webhook sample configuration here uses a ServiceNow developer configuration.

Create a ServiceNow Developer Account

  1. Create a ServiceNow developer account at https://developer.servicenow.com/.
  2. Create a developer instance.

    This instance automatically hibernates, so you should wake it every 24 hours. Also, if you don’t use it for 10 days, it is reclaimed.

  3. In the Filter Navigator search window on the left-side of the navigation menu, enter rest.
  4. Go to Scripted Web Services > Scripted REST APIs.
  5. Click New to create a new resource.

  6. Enter the API name and click Submit.
  7. Click the API name of the newly created resource.
  8. On the bottom tabs of the same page, under Resources, select New.

  9. Create an authentication based on your needs:
    • To use basic authentication for your webhook, you must create a non-admin user on ServiceNow so that admin credentials are shared.

      1. Name: Enter your preferred name.
      2. HTTP method: Select POST.
      3. Select the Active checkbox.
      4. Copy and paste the following script in the Script box.
        • (function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {
          var reqData = request.body.dataString; 
              var jData = new global.JSON().decode(reqData);
              var alertId = jData.alertId;
              var ruleName = jData.ruleName; 
              if ( ruleName.equalsIgnoreCase("test") ) {
          	// test webhook
          	return;
              }
             
              var status = jData.status;
              var zdxurl = jData.zdxUrl;
              var startTime = jData.startTime;
              var endTime = jData.endTime;
              var state;
              if ( status == "STARTED")
                      state = 2; // in progress
                  else if ( status == "ENDED_ON_RULE_DEL" || status == "ENDED_ON_RULE_MODIFY")
                      state = 8; // cancelled
                  else 
                      state = 6; // resolved
              
              var impact = 1;
              if ( jData.severity == "High" ) 
                      impact = 1;
                  else if ( jData.severity == "Medium" ) 
                      impact = 2;
                  else if ( jData.severity == "Low" )
                      impact = 3;
              
              var gdt = new GlideDateTime();
              var gr = new GlideRecord('incident'); 
              gr.addQuery('short_description', 'CONTAINS 1', alertId);
              gr.query();
              
          	if ( jData.alertType == "Incident") {
          		impacted = jData.geolocationCount + " Geolocations\n" + jData.impactedDeviceCount + " Devices\n" + jData.impactedUserCount + " Users\n";
          		context = jData.context;
                  epicenter = context;
              } else if ( jData.alertType == "ZProbe") {
          		impacted = "Zscaler Hosted Locations: " + jData.zscaler_hosted_locations + "\n";
              } else {
          		impacted = jData.geolocationCount + " Geolocations\n" + jData.deptCount + " Departments\n" + jData.osverCount + " OS Versions\n" + jData.impactedDeviceCount + " Devices\n";
              }
              var criteria = jData.criteria;
              var condOp = criteria.op;
              var conditions = criteria.conditions;
              
              var text = "";
              for (i = 0; i < conditions.length; i++) { 
                  if ( i != 0) 
                      text += condOp + " ";
                  text += conditions[i].conditionString;
                  
                  if ( conditions[i].isHit)
                      text += "*";
                  if (conditions[i].stats ) {
                      text += ", min: " + conditions[i].stats.min + ", max: "  + conditions[i].stats.max + ", avg: "  + conditions[i].stats.avg;
                  }
                  
                  text += "\n";
              }
              
              var desc = "";	
          	if ( jData.alertType == "Incident") {
                  if ( status == "STARTED" )
          			desc = "Rule Name: " + ruleName + "\n\nAlert criteria triggers: \n\n" + text + "\nImpacted: \n\n" + impacted + "\n\n * - Condition triggered" + "\n\nEpicenter: " + epicenter.attribute + ", " + epicenter.epicenter;
                  else 
          			desc = "Rule Name: " + ruleName + "\n\nAlert criteria triggers: \n\n" + text + "\n\n * - Condition triggered" + "\n\nEpicenter: " + epicenter.attribute + ", " + epicenter.epicenter;
          	} else if ( jData.alertType == "ZProbe") {
                  if ( status == "STARTED" )
          			desc = "Rule Name: " + ruleName + "\n\nAlert criteria triggers: \n\n" + text + "\nImpacted: \n\n" + impacted + "\n\n * - Condition triggered";
                  else 
          			desc = "Rule Name: " + ruleName + "\n\nAlert criteria triggers: \n\n" + text + "\n\n * - Condition triggered";
          	} else {
                  if ( status == "STARTED" )
          			desc = "Rule Name: " + ruleName + "\n\nAlert criteria triggers: \n\n" + text + "\nImpacted: \n\n" + impacted + "\n\n * - Condition triggered";
                  else 
          			desc = "Rule Name: " + ruleName + "\n\nAlert criteria triggers: \n\n" + text + "\n\n * - Condition triggered";
          	}
              var category = "ZDX Alerts";
              if (gr.next()) {
                  gr.state = state;
                  gr.impact = impact;
                  
                  if ( state == 6 ) {
                      gr.close_code = "Alert resolved";
                  } else if ( state == 8 ) {
                      gr.close_code = "Rule deleted or modified";
                  }
                  gr.close_notes = " ";
                  
                  var sd = gr.short_description;
          		if ( jData.alertType == "ZProbe") {
                      gr.short_description = "ZDX Alert " + alertId + " " + jData.zscaler_hosted_locations + " impacted";
          		} else {
          			gr.short_description = "ZDX Alert " + alertId + " " + jData.impactedDeviceCount + " devices impacted";
          		}
              
                  gr.description = desc;
                  gr.u_linked_incident = zdxurl;
                  gr.u_alert_details = reqData;
                  
                  gdt.setValue(startTime*1000);
                  gr.work_start = gdt.getDisplayValue();
                  
                  if ( status == "STARTED") {
                      gr.work_end = "";
                  } else {
                      gdt.setValue(endTime*1000);
                      gr.work_end = gdt.getDisplayValue();
                  }
                  //gr.caller_id = e9f176e2db54101087f7478239961941;
                  gr.update();
              } 
              else {
                  gr.newRecord(); 
                  if ( state == 2 ) // in progress
                      state = 1; // new
                  gr.state = state;
                  gr.impact = impact;
                  gr.category = category;
                  
                  if ( state == 6 ) {
                      gr.close_code = "Alert resolved";
                  } else if ( state == 8 ) {
                      gr.close_code = "Rule deleted or modified";
                  }
                  gr.close_notes = " ";
                  
          		if ( jData.alertType == "ZProbe") {
                      gr.short_description = "ZDX Alert " + alertId + " " + jData.zscaler_hosted_locations + " impacted";
          		} else {
          			gr.short_description = "ZDX Alert " + alertId + " " + jData.impactedDeviceCount + " devices impacted";
          		}
              
                  gr.description = desc;
                  gr.u_linked_incident = zdxurl;
                  gr.u_alert_details = reqData;
                  
                  gdt.setValue(startTime*1000);
                  gr.work_start = gdt.getDisplayValue();
                  
                  if ( status == "STARTED") {
                      gr.work_end = "";
                  } else {
                      gdt.setValue(endTime*1000);
                      gr.work_end = gdt.getDisplayValue();
                  }
                  
                  //gr.caller_id = e9f176e2db54101087f7478239961941;
                  var result = gr.insert(); 
              }
          })(request, response);
          
          Close
      5. Under the Security tab, select the following checkboxes:

        • Requires authentication
        • Requires ACL authorization

        Click Submit.

      6. Enter Users on the Filter Navigator search box on the left-side of the navigation menu.
      7. Go to Users and Groups > Users > New.
      8. Enter a User ID and password for this user.

        Click Submit.

      Close
      • Name: Enter your preferred name
      • HTTP method: Select POST.
      • Deselect the authentication checkbox.
      • Paste the script under Token Authentication in the Script box.

        Remember to edit the token in your script (12345679 in the following script) to the one your webhook in Digital Experience Monitoring is using.

        • (function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {
              
              var headers = request.headers;
              var authHeader = request.getHeader('authorization');
              var token = authHeader.split(" ");
              
              if (token[1] != "12345679")
                  response.setError(new sn_ws_err.BadRequestError('Bad token'));
              var reqData = request.body.dataString; 
              var jData = new global.JSON().decode(reqData);
              var alertId = jData.alertId;
              var ruleName = jData.ruleName; 
              if ( ruleName.equalsIgnoreCase("test") ) {
              // test webhook
              return;
              }
             
              var status = jData.status;
              var zdxurl = jData.zdxUrl;
              var startTime = jData.startTime;
              var endTime = jData.endTime;
              var state;
              if ( status == "STARTED")
                      state = 2; // in progress
                  else if ( status == "ENDED_ON_RULE_DEL" || status == "ENDED_ON_RULE_MODIFY")
                      state = 8; // cancelled
                  else 
                      state = 6; // resolved
              
              var impact = 1;
              if ( jData.severity == "High" ) 
                      impact = 1;
                  else if ( jData.severity == "Medium" ) 
                      impact = 2;
                  else if ( jData.severity == "Low" )
                      impact = 3;
              
              var gdt = new GlideDateTime();
              var gr = new GlideRecord('incident'); 
              gr.addQuery('short_description', 'CONTAINS 1', alertId);
              gr.query();
              
              if ( jData.alertType == "Incident") {
                  impacted = jData.geolocationCount + " Geolocations\n" + jData.impactedDeviceCount + " Devices\n" + jData.impactedUserCount + " Users\n";
                  context = jData.context;
                  epicenter = context;
              } else if ( jData.alertType == "ZProbe") {
                  impacted = "Zscaler Hosted Locations: " + jData.zscaler_hosted_locations + "\n";
              } else {
                  impacted = jData.geolocationCount + " Geolocations\n" + jData.deptCount + " Departments\n" + jData.osverCount + " OS Versions\n" + jData.impactedDeviceCount + " Devices\n";
              }
              var criteria = jData.criteria;
              var condOp = criteria.op;
              var conditions = criteria.conditions;
              
              var text = "";
              for (i = 0; i < conditions.length; i++) { 
                  if ( i != 0) 
                      text += condOp + " ";
                  text += conditions[i].conditionString;
                  
                  if ( conditions[i].isHit)
                      text += "*";
                  if (conditions[i].stats ) {
                      text += ", min: " + conditions[i].stats.min + ", max: "  + conditions[i].stats.max + ", avg: "  + conditions[i].stats.avg;
                  }
                  
                  text += "\n";
              }
              
              var desc = "";  
              if ( jData.alertType == "Incident") {
                  if ( status == "STARTED" )
                      desc = "Rule Name: " + ruleName + "\n\nAlert criteria triggers: \n\n" + text + "\nImpacted: \n\n" + impacted + "\n\n * - Condition triggered" + "\n\nEpicenter: " + epicenter.attribute + ", " + epicenter.epicenter;
                  else 
                      desc = "Rule Name: " + ruleName + "\n\nAlert criteria triggers: \n\n" + text + "\n\n * - Condition triggered" + "\n\nEpicenter: " + epicenter.attribute + ", " + epicenter.epicenter;
              } else if ( jData.alertType == "ZProbe") {
                  if ( status == "STARTED" )
                      desc = "Rule Name: " + ruleName + "\n\nAlert criteria triggers: \n\n" + text + "\nImpacted: \n\n" + impacted + "\n\n * - Condition triggered";
                  else 
                      desc = "Rule Name: " + ruleName + "\n\nAlert criteria triggers: \n\n" + text + "\n\n * - Condition triggered";
              } else {
                  if ( status == "STARTED" )
                      desc = "Rule Name: " + ruleName + "\n\nAlert criteria triggers: \n\n" + text + "\nImpacted: \n\n" + impacted + "\n\n * - Condition triggered";
                  else 
                      desc = "Rule Name: " + ruleName + "\n\nAlert criteria triggers: \n\n" + text + "\n\n * - Condition triggered";
              }
              var category = "ZDX Alerts";
              if (gr.next()) {
                  gr.state = state;
                  gr.impact = impact;
                  
                  if ( state == 6 ) {
                      gr.close_code = "Alert resolved";
                  } else if ( state == 8 ) {
                      gr.close_code = "Rule deleted or modified";
                  }
                  gr.close_notes = " ";
                  
                  var sd = gr.short_description;
                  if ( jData.alertType == "ZProbe") {
                      gr.short_description = "ZDX Alert " + alertId + " " + jData.zscaler_hosted_locations + " impacted";
                  } else {
                      gr.short_description = "ZDX Alert " + alertId + " " + jData.impactedDeviceCount + " devices impacted";
                  }
              
                  gr.description = desc;
                  gr.u_linked_incident = zdxurl;
                  gr.u_alert_details = reqData;
                  
                  gdt.setValue(startTime*1000);
                  gr.work_start = gdt.getDisplayValue();
                  
                  if ( status == "STARTED") {
                      gr.work_end = "";
                  } else {
                      gdt.setValue(endTime*1000);
                      gr.work_end = gdt.getDisplayValue();
                  }
                  gr.update();
              } 
              else {
                  gr.newRecord(); 
                  if ( state == 2 ) // in progress
                      state = 1; // new
                  gr.state = state;
                  gr.impact = impact;
                  gr.category = category;
                  
                  if ( state == 6 ) {
                      gr.close_code = "Alert resolved";
                  } else if ( state == 8 ) {
                      gr.close_code = "Rule deleted or modified";
                  }
                  gr.close_notes = " ";
                  
                  if ( jData.alertType == "ZProbe") {
                      gr.short_description = "ZDX Alert " + alertId + " " + jData.zscaler_hosted_locations + " impacted";
                  } else {
                      gr.short_description = "ZDX Alert " + alertId + " " + jData.impactedDeviceCount + " devices impacted";
                  }
              
                  gr.description = desc;
                  gr.u_linked_incident = zdxurl;
                  gr.u_alert_details = reqData;
                  
                  gdt.setValue(startTime*1000);
                  gr.work_start = gdt.getDisplayValue();
                  
                  if ( status == "STARTED") {
                      gr.work_end = "";
                  } else {
                      gdt.setValue(endTime*1000);
                      gr.work_end = gdt.getDisplayValue();
                  }
                  
                  var result = gr.insert(); 
              }
          })(request, response);
          Close

      Click Submit.

      Your resource populates on the Scripted REST Service page.

      Close

Create a Webhook in the Admin Portal

To create a webhook in the Admin Portal:

  1. Go to Administration > Alerts > Webhooks.
  2. Click Add Webhook.
  3. In the Add Webhook window:
    • Name: Enter a name for the webhook.
    • Status: Select Enabled to enable the webhook.
    • URL: Enter the ServiceNow developer instance hostname and concatenate it with the API name from the Scripted REST service page.

      For example: https://dev91028.service-now.com/api/516508/alertsincident

    • Authentication Type: Select Basic or Token based on your authentication from the previous step.
      • For basic authentication, enter the credentials from when the user was created.
      • For token authentication, enter the same token as you are using in your script.
  4. Click Test Webhook to verify if it functions correctly.
  5. To view the webhook notification on ServiceNow:
    1. Enter incidents in the Filter Navigator search box on the left-side of the navigation menu.
    2. Go to Service Desk > Incidents.

      You see a list of incidents, and you can double-click any to view individual incident details.

      The webhook output is also viewable here for integration with other third-party providers.

  6. Click Save in the Add Webhook window and activate the changes.

Now, when your alert triggers and sends a webhook notification, an entry is created in the ServiceNow Incident list.

Related Articles
ServiceNow Webhook Configuration GuideServiceNow Webhook Configuration Guide for DevelopersDigital Experience Monitoring Integration on ServiceNowUnderstanding the Digital Experience Monitoring Application Fields on ServiceNow