Parsing inbound emails to create records

Though most ServiceNow users and administrators are aware that ServiceNow is capable of generating outbound email notifications, some organizations don’t take advantage of ServiceNow’s ability to receive and process inbound emails. If they do, they may not fully capitalize on the ability to precisely filter and parse incoming emails to determine what, if anything, should be done when an email is received. In this article, I will outline a set of examples of advanced methods for taking advantage of inbound email actions within ServiceNow.

Scenario

For our hypothetical scenario, we want to capture alert emails generated by an external monitoring system, which we will call “Alert Pro,” (sent from address “email.alerts@alertpro.com”) and, depending on the content of the generated email, conditionally create an incident. (We only care about alerts with a “High” criticality.) We also want to capture information from the body of the email to help create the potential incident.

Base Configuration

To start, we need to create our new Inbound Email Action [sysevent_in_email_action] record. Target table should be “Incident,” and we’ll want to make sure both the “Active” and “Stop Processing” flags are checked. (Active ensures the action is triggered (pending any conditions) and Stop Processing prevents additional inbound actions from triggering after this one.) We’ll also want to set a lower Execution Order as we don’t want other Inbound Actions to preempt this one. I chose an execution order of 20, but you’ll need to consider the other active Inbound Actions in your system when determining this value.

For our condition, we will ignore the condition builder and instead make use of the condition field that accepts a string. In this field we will set:
email.from.indexOf(“email.alerts@alertpro.com”) != -1

This will make use of an “email” object that is available to scripts on this record. It has attributes that correspond to values existing on the incoming email. We’ll check the “from” value on the email and see if it matches the Alert Pro email address we want to process messages from. With this condition, emails not from this source will “fall through” and be considered for processing by Inbound Actions with greater execution order values, like the baseline Create Incident action.

The form should now look something like this:

Scripting

Below is the script that would be used in our scenario. Note the if statement that checks for “Alert: High” in the email’s subject. The condition on the inbound action itself, which we set above, ensures that Alert Pro alerts are caught by this action, but we only create incidents if the incoming alerts are of a high priority.

Additionally, take note of the parseBody function. It’ll allow us to capture values from the incoming email and use them to set values on the incident. For example, the incoming email might have text that reads:

Assignment Group: Service Desk
Category: Database
Severity: Moderate

The parseBody function can be used to capture the values after the respective colons.

(function runAction( /*GlideRecord*/ current, /*GlideRecord*/ event, /*EmailWrapper*/ email, /*ScopedEmailLogger*/ logger, /*EmailClassifier*/ classifier) {

    var subject = email.subject.toString();
    if (subject.indexOf("Alert: High") != -1) { // Only create an incident if the incoming email's subject contains "Alert: High"
        
        // This block of code looks for an already-existing incident with the same short description. If found, coalesce to that one.
        var foundInc = false;
        var grInc = new GlideRecord("incident");
        grInc.addQuery("short_description", subject);
        grInc.addEncodedQuery("stateIN1,2,3,6");
        grInc.query();
        if (grInc.next()) {
            foundInc = true;
            current = grInc;
        }

        //Setting assignment group
        var assignmentGroup = "";
        var foundGroup = parseBody('Assignment Group:'); // See this function definition below
        var grAssign = new GlideRecord("sys_user_group");
        if(grAssign.get("name", foundGroup)){
            assignmentGroup = grAssign.getValue("sys_id");
        }
        if(assignmentGroup == ""){
            assignmentGroup = "pa89109a1b211cd06a1f7518dc4bcb01"; // A fallback group; should be stored in a system property in a real scenario
        }
        current.assignment_group = assignmentGroup;
        
        //Setting category
        var category = "";
        var foundCategory = parseBody("Category:");
        var grCat = new GlideRecord("sys_choice");
        grCat.addQuery("name", "incident");
        grCat.addQuery("element", "category");
        grCat.addQuery("label", foundCategory);
        grCat.query();
        if(grCat.next()){
            category = grCat.value;
        }
        current.category = category;
        
        //Setting severity
        var impact = 2;
        var urgency = 1;
        var foundSeverity = parseBody("Severity:");
        if(foundSeverity != ""){
            if(foundSeverity == "Low"){
                impact = 3;
                urgency = 3;
            }else if(foundSeverity == "Moderate"){
                impact = 2;
                urgency = 2;
            }else if(foundSeverity == "High"){
                impact = 2;
                urgency = 1;
            }else if(foundSeverity == "Critical"){
                impact = 1;
                urgency = 1;
            }
        }
        current.impact = impact;
        current.urgency = urgency;

        current.short_description = subject;
        current.description = email.body_text;
        current.contact_type = "Alert";
        
        current.caller_id = "byx8800b1b879010c634ca262a4bcb31"; // Hardcoded service account user called "Alerts Pro User"; use system property in real scenario
        current.u_affected_user = "byz8800b1b879010c634ca262a4bcb31"; // See above
        
        if(foundInc){
            current.update();
        }else{
            current.insert();
        }

    } else {
        gs.log("Inbound email ignored; not high criticality");
    }

    /* Grabs the text from a line in the email body after the first incidence of a provided parseString
       For example, if parseString is "Assignment Group:", and a line of text consists of
       Assignment Group: Service Desk
       the value returned will be "Service Desk" (white space is trimmed)
    */
    function parseBody(parseString){
        var parsedGroup = "";
        var bodySplit = email.body_text.split("\n");
        for(var i = 0; i < bodySplit.length; i++){
            if(bodySplit[i].indexOf(parseString) != -1){
                parsedGroup = grabContent(bodySplit[i], parseString);
                return parsedGroup;
            }
        }
    }

    // Used above
    function grabContent(str, start) {   
        var startLen = start.length;   
        var s = str.indexOf(start);    
        var scrape = str.substring(s+startLen);   
        return scrape.trim();
    } 

    
})(current, event, email, logger, classifier);

Using the above code and configuration as a base, you can customize an inbound email action to capture any sort of information you may need!