ServiceNow SOAP integration with Service-Flow

Access from Service-Flow to ServiceNow

S-F accesses ServiceNow with standard direct web services calls. For example, to send inserts and updates to incident table, the URL has to be in the form: https://company.service-now.com/incident.do?SOAP where the incident -part needs is changed according to the table used. Also a valid user for the integration needs to be created to ServiceNow.

Service-Flow currently expects to get both sys_id and number as return values from direct web service calls. In ServiceNow, the insertResponse always returns sys_id, but number is only returned when number is configured to be the display value of the table in question (like incident, for example). Usually number is the display value, but we've seen installations where description was the default display value.

Access from ServiceNow to Service-Flow

ServiceNow accesses S-F with the same kind of web service call as S-F accesses ServiceNow. With ServiceNow concepts, this is called an outbound SOAP message. The following chapter explains how to configure the needed SOAP message definitions and business rules to trigger calling the web services when inserting and/or updating incidents in ServiceNow.

Create Integration user to ServiceNow

Add integration user to ServiceNow. This user is used to send updates and creates to ServiceNow through the SOAP interface. The credentials have to match those configured to the ServiceNow adapter configuration in Service-Flow, so share the credentials with Service-Flow support.

    • Add user
      • username "serviceflow" (you can choose a different name also, but remember to change it to used business rule conditions)
      • Check "web service access only"
      • Add roles: soap_create, soap_update, soap_ecc, soap_script, itil

Create an update set

It is essential to use update sets when configuring all the Service-Flow related functions to development environment. That way you ensure the fluent deployment of the integration into test & production environments.



Incident integration with SOAP

This paragraph describes the needed functions for setting up communication for Incident table. If other tables are needed to be integrated, make changes accordingly (highlighted definitions). Note also, that the amount of fields used in integrations might vary. The following update set includes the features in this section. Download the update set HERE.

Create new SOAP Message definition

You need to create a separate SOAP Message for every table used in the integration. 

    • Navigate to "System Web Services" -> "Outbound" -> SOAP Message
    • Fetch WSDL (e.g. https://[your-snc-instance].service-now.com/incident.do?wsdl)

      How to?
      1. Copy the link above to your browser
      2. Replace [your-snc-instance] with the instance name you are using -> Your browser is showing the xml needed below.
      3. Right click on page and select "View source" (or similar)
      4. Copy-Paste the xml to "WSDL XML:" in bullet 3c
    1. Create new SOAP Message
      1. Name: "Send Incident to Service-Flow"
      2. uncheck "Download WSDL

      3. WSDL XML: The fetched WSDL 
        (Note. If you use copy-paste from some browser, you might run into a situation where pressing Submit returns "Error at line (2) The processing instruction target matching "[xX][mM][lL]" is not allowed." This is because importing cannot handle the empty line in the first row of the imported string.)
      4. Press SAVE
      5. Click "Generate sample SOAP-messages" in the SOAP Message page
      6. Remove all functions except update functions. Rename update as send.
    2. Edit send message
      1. Click send function link
      2. Select "Authentication type": Basic
      3. Add an auth profile with the credentials you get from Service-Flow UI
      4. SOAP endpoint:
        • test: https://soap-receiver-test.service-flow.com/api
        • production: https://soap-receiver.service-flow.com/api
      5. Add the table name to function element namespace (for. ex. <update xmlns="http://www.service-now.com/incident">)
      6. In case sample message creation has included the message name in the template, remove all of the mesasage names valiable place holders. (for.ex. <active>${update.active}</active> → <active>${active}</active>)
      7. Check that <sys_id>${sys_id}</sys_id> and <number>${number}</number> parameters are included in the soap envelope inside update -element 
      8. Add <attachments>${attachments}</attachments> parameter to the soap envelope inside update -element 
Example SOAP Template
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:inc="http://www.service-now.com/incident">
   <soapenv:Header/>
   <soapenv:Body>
      <inc:update>
         <!--Optional:-->
         <active>${active}</active>
         <!--Optional:-->
         <activity_due>${activity_due}</activity_due>
         <!--Optional:-->
         <additional_assignee_list>${additional_assignee_list}</additional_assignee_list>
         <!--Optional:-->
         <approval>${approval}</approval>
         <!--Optional:-->
         <approval_history>${approval_history}</approval_history>
         <!--Optional:-->
         <approval_set>${approval_set}</approval_set>
         <!--Optional:-->
         <assigned_to>${assigned_to}</assigned_to>
         <!--Optional:-->
         <assignment_group>${assignment_group}</assignment_group>
         <!--Optional:-->
         <business_duration>${business_duration}</business_duration>
         <!--Optional:-->
         <business_service>${business_service}</business_service>
         <!--Optional:-->
         <business_stc>${business_stc}</business_stc>
         <!--Optional:-->
         <calendar_duration>${calendar_duration}</calendar_duration>
         <!--Optional:-->
         <calendar_stc>${calendar_stc}</calendar_stc>
         <!--Optional:-->
         <caller_id>${caller_id}</caller_id>
         <caller_id.fn>${caller_id.fn}</caller_id.fn>
         <caller_id.ln>${caller_id.ln}</caller_id.ln>
         <caller_id.email>${caller_id.email}</caller_id.email>
         <!--Optional:-->
         <category>${category}</category>
         <!--Optional:-->
         <caused_by>${caused_by}</caused_by>
         <!--Optional:-->
         <child_incidents>${child_incidents}</child_incidents>
         <!--Optional:-->
         <close_code>${close_code}</close_code>
         <!--Optional:-->
         <close_notes>${close_notes}</close_notes>
         <!--Optional:-->
         <closed_at>${closed_at}</closed_at>
         <!--Optional:-->
         <closed_by>${closed_by}</closed_by>
         <!--Optional:-->
         <cmdb_ci>${cmdb_ci}</cmdb_ci>
         <!--Optional:-->
         <comments>${comments}</comments>
         <!--Optional:-->
         <comments_and_work_notes>${comments_and_work_notes}</comments_and_work_notes>
         <!--Optional:-->
         <company>${company}</company>
         <!--Optional:-->
         <contact_type>${contact_type}</contact_type>
         <!--Optional:-->
         <correlation_display>${correlation_display}</correlation_display>
         <!--Optional:-->
         <correlation_id>${correlation_id}</correlation_id>
         <!--Optional:-->
         <delivery_plan>${delivery_plan}</delivery_plan>
         <!--Optional:-->
         <delivery_task>${delivery_task}</delivery_task>
         <!--Optional:-->
         <description>${description}</description>
         <!--Optional:-->
         <due_date>${due_date}</due_date>
         <!--Optional:-->
         <escalation>${escalation}</escalation>
         <!--Optional:-->
         <expected_start>${expected_start}</expected_start>
         <!--Optional:-->
         <follow_up>${follow_up}</follow_up>
         <!--Optional:-->
         <group_list>${group_list}</group_list>
         <!--Optional:-->
         <impact>${impact}</impact>
         <!--Optional:-->
         <incident_state>${incident_state}</incident_state>
         <!--Optional:-->
         <knowledge>${knowledge}</knowledge>
         <!--Optional:-->
         <location>${location}</location>
         <!--Optional:-->
         <made_sla>${made_sla}</made_sla>
         <!--Optional:-->
         <notify>${notify}</notify>
         <!--Optional:-->
         <number>${number}</number>
         <!--Optional:-->
         <opened_at>${opened_at}</opened_at>
         <!--Optional:-->
         <opened_by>${opened_by}</opened_by>
         <!--Optional:-->
         <order>${order}</order>
         <!--Optional:-->
         <parent>${parent}</parent>
         <!--Optional:-->
         <parent_incident>${parent_incident}</parent_incident>
         <!--Optional:-->
         <priority>${priority}</priority>
         <!--Optional:-->
         <problem_id>${problem_id}</problem_id>
         <!--Optional:-->
         <reassignment_count>${reassignment_count}</reassignment_count>
         <!--Optional:-->
         <reopen_count>${reopen_count}</reopen_count>
         <!--Optional:-->
         <resolved_at>${resolved_at}</resolved_at>
         <!--Optional:-->
         <resolved_by>${resolved_by}</resolved_by>
         <!--Optional:-->
         <rfc>${rfc}</rfc>
         <!--Optional:-->
         <severity>${severity}</severity>
         <!--Optional:-->
         <short_description>${short_description}</short_description>
         <!--Optional:-->
         <sla_due>${sla_due}</sla_due>
         <!--Optional:-->
         <state>${state}</state>
         <!--Optional:-->
         <subcategory>${subcategory}</subcategory>
         <sys_id>${sys_id}</sys_id>
         <!--Optional:-->
         <time_worked>${time_worked}</time_worked>
         <!--Optional:-->
         <upon_approval>${upon_approval}</upon_approval>
         <!--Optional:-->
         <upon_reject>${upon_reject}</upon_reject>
         <!--Optional:-->
         <urgency>${urgency}</urgency>
         <!--Optional:-->
         <user_input>${user_input}</user_input>
         <!--Optional:-->
         <watch_list>${watch_list}</watch_list>
         <!--Optional:-->
         <work_end>${work_end}</work_end>
         <!--Optional:-->
         <work_notes>${work_notes}</work_notes>
         <!--Optional:-->
         <work_notes_list>${work_notes_list}</work_notes_list>
         <!--Optional:-->
         <work_start>${work_start}</work_start>
         <attachments>${attachments}</attachments>
      </inc:update>
   </soapenv:Body>
</soapenv:Envelope>

Service-Flow will send a SOAP with an empty element in the body as a syncronous response depending on the used function.

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/>

<SOAP-ENV:Body>

<insertResponse>

<status>success</status>

<transaction_id>[Service-Flow's transaction id]</transaction_id>

</insertResponse>

</SOAP-ENV:Body>


Add scripted web service "RetrieveAttachment" for retrieving attachments from ServiceNow

This function can be used with all the tables used in the integration.

        1. Navigate to System Web Services -> Scripted Web Services
        2. Click New
        3. Give the name RetrieveAttachment
        4. Give function name getData
        5. Add the following script to the script editor:


RetrieveAttachment script
// Given a sys_id of an attachment (attachment_sys_id), sends back its content as base64 encoded value (attachment_data)

if (typeof GlideStringUtil != 'undefined')
    var StringUtil = GlideStringUtil;
else
    var StringUtil = Packages.com.glide.util.StringUtil;

if (typeof GlideSysAttachment != 'undefined')
    var sysAttachment = new GlideSysAttachment();
else
    var sysAttachment = new Packages.com.glide.ui.SysAttachment();

 
var gr = new GlideRecord("sys_attachment");
gr.addQuery("sys_id", request.attachment_sys_id);
gr.query();

if (gr.hasNext()) {
    while (gr.next()) {
        var binData = sysAttachment.getBytes(gr);
        var encData = StringUtil.base64Encode(binData);
        response.attachment_data = encData;
    }
} else {
    gs.log("No attachment found with sys_id " + request.attachment_sys_id);
}

 

Then, submit/save the scripted web service and take it to edit mode again and do the following:

    • Add input parameter attachment_sys_id
    • Add output parameter attachment_data

Sending attachments to ServiceNow

SF sends attachments to ServiceNow through the standard AttachmentCreator SOAP web service. I.e. the inbound attachments are sent to ecc queue.

Limitations

All the attachments are currently sent with content type of application/octet-stream. SF doesn't get content type from all the senders, but some do send it. The functionality might change in the future to add more specific content type when it is known.

Sending attachments to Service-Flow

Attachments are sent from ServiceNow to Service-Flow the following way. The update SOAP messages from ServiceNow to Service-Flow can contain attachments element that contains metadata about attachments that the incident has. The following excerpt shows example of the XML structure:

Example of attachment XML structure
<attachments>
	<attachment>
		<content_type>application/octet-stream</content_type>
        <file_name>RE MG Extra Prod Crawl.msg</file_name>
        <size_bytes>78336</size_bytes>
        <sys_id>4c1b801bc84811008316ac5c40bfbeb2</sys_id>
	</attachment>
	<attachment>
		<content_type>image/jpeg</content_type>
        <file_name>image.jpg</file_name>
        <size_bytes>78336</size_bytes>
        <sys_id>5c2b611bc94812016316aa5c40bfbeb5</sys_id>
	</attachment>
</attachments>

Using this information, Service-Flow then takes the sys_id of the attachment and retrieves it from ServiceNow using the RetrieveAttachment scripted web service.





Triggering messages out

To send messages out from ServiceNow you need to create business rules that match the pre-defined workflows. These are usually defined in process workshops where all parties are present.

Create business rule for incident

  1. Navigate to System Definition -> Business Rules
    • Click "New"
    • Name: "Service-Flow: after Inc insert/update"
    • Table: "Incident [incident]"
    • When: "after"
    • Check: "update" and "insert". (Condition example: current.assignment_group.name == '3rd Party Vendor' || current.correlation_id != '')
      • Copy the following example code to the Script field

Business rule for incident update
if (current.sys_updated_by != 'serviceflow') {
    var s = new sn_ws.SOAPMessageV2('Send Incident to Service-Flow', 'send');
    s.setStringParameter('correlation_id', current.correlation_id);
    s.setStringParameter('active', current.active);
    s.setStringParameter('activity_due', current.activity_due);
    s.setStringParameter('approval', current.approval);
    s.setStringParameter('approval_history', current.approval_history);
    s.setStringParameter('approval_set', current.approval_set);
    s.setStringParameter('assigned_to', current.assigned_to.user_name);
    s.setStringParameter('assignment_group', current.assignment_group.name);
    s.setStringParameter('business_duration', current.business_duration);
    s.setStringParameter('business_stc', current.business_stc);
    s.setStringParameter('calendar_duration', current.calendar_duration);
    s.setStringParameter('calendar_stc', current.calendar_stc);
    s.setStringParameter('caller_id', current.caller_id);
    s.setStringParameter('category', current.category);
    s.setStringParameter('caused_by', current.caused_bycurrent.caused_by.number);
    s.setStringParameter('child_incidents', current.child_incidents);
    s.setStringParameter('close_code', current.close_code);
    s.setStringParameter('close_notes', current.close_notes);
    s.setStringParameter('closed_at', current.closed_at);
    s.setStringParameter('closed_by', current.closed_by.name);
    s.setStringParameter('cmdb_ci', current.cmdb_ci.name);
    if (current.comments.changes()) {
        s.setStringParameter('comments', current.comments.getJournalEntry(1));
    }
    s.setStringParameter('comments_and_work_notes', current.comments);
    s.setStringParameter('company', current.company.name);
    s.setStringParameter('contact_type', current.contact_type);
    s.setStringParameter('contract', current.contract.vendor_contract);
    s.setStringParameter('correlation_display', current.correlation_display);
    s.setStringParameter('correlation_id', current.correlation_id);
    s.setStringParameter('delivery_plan', current.delivery_plan.name);
    s.setStringParameter('delivery_task', current.delivery_task.name);
    s.setStringParameter('description', current.description);
    s.setStringParameter('due_date', current.due_date);
    s.setStringParameter('escalation', current.escalation);
    s.setStringParameter('expected_start', current.expected_start);
    s.setStringParameter('follow_up', current.follow_up);
    s.setStringParameter('group_list', current.group_list);
    s.setStringParameter('impact', current.impact);
    s.setStringParameter('incident_state', current.incident_state);
    s.setStringParameter('knowledge', current.knowledge);
    s.setStringParameter('location', current.location.name);
    s.setStringParameter('made_sla', current.made_sla);
    s.setStringParameter('notify', current.notify);
    s.setStringParameter('number', current.number);
    s.setStringParameter('opened_at', current.opened_at);
    s.setStringParameter('opened_by', current.opened_by.email);
    s.setStringParameter('order', current.order);
    s.setStringParameter('parent', current.parent.sys_id);
    s.setStringParameter('parent_incident', current.parent_incident.number);
    s.setStringParameter('priority', current.priority);
    s.setStringParameter('problem_id', current.problem_id);
    s.setStringParameter('reassignment_count', current.reassignment_count);
    s.setStringParameter('reopen_count', current.reopen_count);
    s.setStringParameter('resolved_at', current.resolved_at);
    s.setStringParameter('resolved_by', current.resolved_by.email);
    s.setStringParameter('rfc', '');
    s.setStringParameter('service_offering', current.service_offering.name);
    s.setStringParameter('severity', current.severity);
    s.setStringParameter('short_description', current.short_description);
    s.setStringParameter('skills', current.skills);
    s.setStringParameter('sla_due', current.sla_due);
    s.setStringParameter('state', current.state);
    s.setStringParameter('subcategory', current.subcategory);
    s.setStringParameter('sys_id', current.sys_id);
    s.setStringParameter('task_for', current.task_for.email);
    s.setStringParameter('time_worked', current.time_worked);
    s.setStringParameter('upon_approval', current.upon_approval);
    s.setStringParameter('upon_reject', current.upon_reject);
    s.setStringParameter('urgency', current.urgency);
    s.setStringParameter('user_input', current.user_input);
    s.setStringParameter('watch_list', current.watch_list);
    s.setStringParameter('work_end', current.work_end);
    if (current.work_notes.changes()) {
        s.setStringParameter('work_notes', current.work_notes.getJournalEntry(1));
    }
    s.setStringParameter('work_notes_list', current.work_notes_list);
    s.setStringParameter('work_start', current.work_start);

	// add your custom fields here
	// s.setStringParameter('u_custom_field', current.u_custom_field);
    var response = s.execute();
}

Create business rule for triggering out attachments

Here's an example how to create a business rule that send out an attachment when it is uploaded to a incident that has an external vendor's id.

Navigate to System Definition -> Business Rules

      • Click "New"
        • Name: "Service-Flow: attachment insert Incident"
        • Table: "Attachment[sys_attachment]"
        • When: "after"
        • Check: "Insert"
        • Condition: current.sys_updated_by != 'serviceflow'
      • Copy the following example code to the Script field



Business rule for triggering out attachments
if (typeof GlideStringUtil != 'undefined')
    var StringUtil = GlideStringUtil;
else
    var StringUtil = Packages.com.glide.util.StringUtil;

var gr = new GlideRecord('incident');
gr.addQuery('sys_id', current.table_sys_id);
gr.query();

if (gr.next()) {
    if (gr.correlation_id!= '') {
        var s = new sn_ws.SOAPMessageV2('Send Incident to Service-Flow', 'send');
        s.setStringParameter('number', gr.number);
        s.setStringParameter('priority', gr.priority);
        s.setStringParameter('comments', 'Attachment uploaded via Service-Flow');
        s.setStringParameter('sys_id', gr.sys_id);
        s.setStringParameter('correlation_id', gr.correlation_id);
        var attachmentsXml = "";
        attachmentsXml += "<attachment><content_type>" + current.content_type + "</content_type>";
		attachmentsXml += "<file_name>" + JSUtil.escapeText(current.file_name) + "</file_name>";
        attachmentsXml += "<size_bytes>" + current.size_bytes + "</size_bytes>";
        attachmentsXml += "<sys_id>" + current.sys_id + "</sys_id>";
        attachmentsXml += "</attachment>\n";
        s.setStringParameterNoEscape('attachments', attachmentsXml);
        var response = s.execute();
    }
}

OPTIONAL - Create business rule for triggering out all attachments from a first integration message

Here's an example how to create a business rule that sends out all attachments that are uploaded to an incident. This is usually used in a case where a ticket is assigned to an external vendor and it has had attachment earlier uploaded to it. The ack message that contains the external vendors id is updated back to the incident. After that update we send out an update containing the information about possible included attachments, so Service-Flow can fetch them and send them forward.

Navigate to System Definition -> Business Rules

      • Click "New"
        • Name: "Service-Flow: after Incident id ack"
        • Table: "Incident [incident]"
        • When: "after"
        • Check: "update"
        • Condition: current.correlation_id != '' && previous.correlation_id == ''
      • Copy the following example code to the Script field

Business rule to trigger out all attachments
var s = new sn_ws.SOAPMessageV2('Send Incident to Service-Flow', 'send');
s.setStringParameter('correlation_id', current.correlation_id);
s.setStringParameter('number', current.number);
s.setStringParameter('sys_id', current.sys_id);
s.setStringParameter('priority', current.priority);
s.setStringParameter('comments', 'Attachment(s) uploaded via Service-Flow');

// send metadata about attachments that have been inserted since the ticket was last time updated
if (typeof GlideStringUtil != 'undefined')
    var StringUtil = GlideStringUtil;
else
    var StringUtil = Packages.com.glide.util.StringUtil;

var  gr = new GlideRecord('sys_attachment');
gr.addQuery('table_sys_id', current.sys_id);
gr.addQuery('table_name', current.getTableName());
gr.query();

var attachmentsXml = "";
while (gr.next()){
    attachmentsXml += "<attachment><content_type>" + gr.content_type + "</content_type>";
    attachmentsXml += "<file_name>"+ JSUtil.escapeText(gr.file_name) + "</file_name>";
    attachmentsXml += "<size_bytes>"+ gr.size_bytes + "</size_bytes>";
    attachmentsXml += "<sys_id>"+ gr.sys_id + "</sys_id>";
    attachmentsXml += "</attachment>\n";
}
s.setStringParameterNoEscape('attachments', attachmentsXml);

if (attachmentsXml !== '') {
    var response = s.execute();
}

OPTIONAL - Using Scripted Web Services or Web Service Import Sets with Service-Flow

If direct web services cannot be used, Scripted Web Services can be written or Web Service Import Sets used. This might be needed if there's some additional processing needed that cannot be triggered by the direct web services.

Service-Flow requirements

Path to the Scripted Web Service

Service-Flow needs to know the path where to send messages. When using direct web services, the path are like this:

https://servicenowinstance/tablename.do

For scripted web services, they are like this:

https://servicenowinstance/scriptname.do

The script name has to be configured as an entity type into Service-Flow system configuration. Contact experience@service-flow.com to add the needed entity type.

Path to the Web Service Import Set

For Web Service Import Sets, the path is something like this:

https://servicenowinstance/tablename.do

The table name has to be configured as an entity type into Service-Flow system configuration. Ask SF devops to add the needed entity type.

Service-Flow request format

SF sends same kind of SOAP messages to both the scripted web services and web service import sets as it sends to direct web services, i.e. the SN input format. If the SN internal sys_id is not wanted to be used to identify the ServiceNow incident, internal sys_id processing can be disabled in the SF system specific configurations. If this is wanted or not depends on the script writer. Notice anyhow, that SF needs to know the sys_id to be able to send attachments to ServiceNow, so it should be sent in both inserts and updates.

Expected response format

SF expects to get as a response a SOAP message that has standard SN SOAP message response format. Here's an example of what it looks like:

<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Body>
        <insertResponse xmlns="http://www.service-now.com/scriptname">
            <sys_id>0e605148d563d180945e8842179dc8c1</sys_id>
            <table>incident</table>
            <display_name>number</display_name>
            <display_value>INC0290103</display_value>
            <status>inserted</status>
            <status_message>Some message</status_message>
        </insertResponse>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

The element name that contains the inserted or updated ticket's ID needs to be configured to SF system specific configuration. In this example message, it would be display_value.