|
| 1 | +--- |
| 2 | +title: "ServiceNow Sync" |
| 3 | +description: "Sync incidents with ServiceNow Incidents through ServiceNow sync webhook." |
| 4 | +date: "2025-05-19T10:00:00+08:00" |
| 5 | +url: "https://docs.flashcat.cloud/en/flashduty/service-now-sync" |
| 6 | +--- |
| 7 | + |
| 8 | +Through ServiceNow sync webhook, Flashduty incidents are associated and synchronized with ServiceNow Incidents to achieve integration between Flashduty and ServiceNow. |
| 9 | + |
| 10 | +## In ServiceNow |
| 11 | + |
| 12 | +### Create User |
| 13 | + |
| 14 | +1. Log in to the ServiceNow instance console, select `ALL`, enter `USERS` and select `Organization`-`Users`. |
| 15 | +2. Click `New` to create a new user. |
| 16 | +3. In the edit page, enter `flashduty` in the `User ID` field. |
| 17 | +4. Keep `Password needs reset`, `Web service access only`, and `Internal Integration User` unchecked. |
| 18 | +5. Submit and save. |
| 19 | + |
| 20 | +<img alt="drawing" width="600" src="https://download.flashcat.cloud/flashduty/doc/snow/snow-1.png" /> |
| 21 | + |
| 22 | +### Configure User |
| 23 | + |
| 24 | +The user used to access ServiceNow needs to have administrator roles. |
| 25 | + |
| 26 | +1. On the user list page, find the newly created `flashduty` user and go to the configuration page. |
| 27 | +2. In the edit page, click `Set Password` to set a password. |
| 28 | +3. Click `Roles` to add **admin and itil** roles. |
| 29 | +4. Click `Update` to update the configuration. |
| 30 | + |
| 31 | +<img alt="drawing" width="600" src="https://download.flashcat.cloud/flashduty/doc/snow/snow-2.png" /> |
| 32 | + |
| 33 | +## In Flashduty |
| 34 | + |
| 35 | +### Configure Integration |
| 36 | + |
| 37 | +Enter the username/password and instance name configured above into the integration information on the left and click Next to configure. |
| 38 | + |
| 39 | +- **Integration Name:** Define a name for the current integration. |
| 40 | +- **Management Team:** When a management team is selected, only team members and tenant administrators can edit this integration. |
| 41 | +- **Channel:** Select the channel where this integration takes effect. |
| 42 | +- **Sync Direction:** |
| 43 | + - To_ServiceNow: Sync Flashduty incidents to ServiceNow. |
| 44 | + - From_ServiceNow: Sync ServiceNow Incidents to Flashduty. |
| 45 | + - Two-way: Bidirectional sync between Flashduty and ServiceNow. |
| 46 | +- **Trigger Mode**: |
| 47 | + - Auto Trigger: Requires configuration of corresponding conditions, Flashduty will automatically sync incidents that meet the conditions to ServiceNow. |
| 48 | + - Manual Trigger: Requires manual triggering of ServiceNow sync in the More Actions section of the incident details page (the integration configuration name is the trigger name). |
| 49 | +- **Severity Mapping:** ServiceNow's Priority is determined by the combined values of Impact and Urgency, so you can refer to ServiceNow's `Priority Lookup Rules` for configuration. Additionally, note that only when ServiceNow Incident's Urgency changes will it trigger updates to Flashduty incident severity. |
| 50 | +- **Custom Field Mapping:** You can choose to sync certain labels or all labels of incidents as well as custom field content to ServiceNow fields (only text-type fields are supported). |
| 51 | + |
| 52 | +## In ServiceNow |
| 53 | + |
| 54 | +When sync direction is set to From_ServiceNow or Two-way, additional configuration is required in ServiceNow to sync ServiceNow Incidents to Flashduty. When syncing to Flashduty, there are two methods available, choose based on your actual needs. |
| 55 | + |
| 56 | +### Manual Sync |
| 57 | + |
| 58 | +This method relies on ServiceNow's UI Action and Script Include configuration. The effect achieved by following the steps below: When creating or updating an Incident, you can see a button to send sync requests in the function area. Triggering this button will sync the current Incident content to Flashduty. Note that if the request fails when triggered, please retry (retry interval should be greater than 10 seconds). |
| 59 | + |
| 60 | +#### Configure UI Action |
| 61 | + |
| 62 | +1. Log in to the ServiceNow instance console, select `ALL`, enter `UI Actions` and select `System Definition`-`UI Actions`. |
| 63 | +2. Click `New` to create a new Action. |
| 64 | +3. Enter **Send To Flashduty** in `Name`, select **Incident** in `Table`. |
| 65 | +4. Keep `Form button`, `Active`, `Show insert`, `Show update`, `Client`, `List v2/3 Compatible` checked. |
| 66 | +5. Enter **onClick();** in `Onclick`. |
| 67 | +6. Enter the following in `Script`: |
| 68 | + |
| 69 | + ```js |
| 70 | + function onClick() { |
| 71 | + g_form.save(); |
| 72 | + |
| 73 | + var ga = new GlideAjax("IncidentWebhookHelperAjax"); |
| 74 | + ga.addParam("sysparm_name", "sendWebhook"); |
| 75 | + ga.addParam("sysparm_sys_id", g_form.getUniqueValue()); |
| 76 | + |
| 77 | + ga.getXMLAnswer(function (response) { |
| 78 | + alert("Webhook Triggered: " + response); |
| 79 | + }); |
| 80 | + } |
| 81 | + ``` |
| 82 | + |
| 83 | +7. Submit and save. |
| 84 | + |
| 85 | +#### Configure Script Include |
| 86 | + |
| 87 | +1. Log in to the ServiceNow instance console, select `ALL`, enter `Script Includes` and select `System Definition`-`Script Includes`. |
| 88 | +2. Click `New` to create a new Script Include. |
| 89 | +3. Enter **IncidentWebhookHelper** in `Name`, select **All application scopes** in `Accessible from`. |
| 90 | +4. Keep `Client callable` and `Active` checked. |
| 91 | +5. Enter the following content in `Script`, where you need to add the integration push URL in **request.setEndpoint**: |
| 92 | + |
| 93 | +<div class="hide"> |
| 94 | + |
| 95 | +Note: The body configures default receiving fields. If you have custom fields that need to be synced to Flashduty, you need to manually add content to the body. For example, if you want to add a field named: test_001 (this field name can be obtained when adding custom fields in the integration configuration, do not use the field name displayed in the ServiceNow Incident form), then you need to add to the body: test_001: current.getDisplayValue("test_001"). |
| 96 | + |
| 97 | +</div> |
| 98 | + |
| 99 | +```js |
| 100 | +var IncidentWebhookHelper = Class.create(); |
| 101 | +IncidentWebhookHelper.prototype = { |
| 102 | + initialize: function () {}, |
| 103 | + |
| 104 | + sendIncidentWebhook: function (current) { |
| 105 | + function getLastComment(sysId) { |
| 106 | + var journalGR = new GlideRecord("sys_journal_field"); |
| 107 | + journalGR.addQuery("element_id", sysId); |
| 108 | + journalGR.addQuery("element", "comments"); |
| 109 | + journalGR.orderByDesc("sys_created_on"); |
| 110 | + journalGR.setLimit(1); |
| 111 | + journalGR.query(); |
| 112 | + if (journalGR.next()) { |
| 113 | + return journalGR.getValue("value"); |
| 114 | + } |
| 115 | + return ""; |
| 116 | + } |
| 117 | + |
| 118 | + var body = { |
| 119 | + action_type: current.isNewRecord() ? "insert" : "update", |
| 120 | + number: current.getValue("number"), |
| 121 | + sys_id: current.getUniqueValue(), |
| 122 | + short_description: current.getValue("short_description"), |
| 123 | + description: current.getValue("description"), |
| 124 | + state: current.getDisplayValue("state"), |
| 125 | + impact: current.getDisplayValue("impact"), |
| 126 | + urgency: current.getDisplayValue("urgency"), |
| 127 | + comments: getLastComment(current.getUniqueValue()), |
| 128 | +<label name='field_mapping' tab='6'>{original.key}: current.getDisplayValue("{original.key}")</label> |
| 129 | + }; |
| 130 | + |
| 131 | + try { |
| 132 | + var request = new sn_ws.RESTMessageV2(); |
| 133 | + request.setHttpMethod("POST"); |
| 134 | + request.setEndpoint("PUSH URL"); |
| 135 | + request.setRequestHeader("Content-Type", "application/json"); |
| 136 | + request.setRequestBody(JSON.stringify(body)); |
| 137 | + request.executeAsync(); |
| 138 | + } catch (ex) { |
| 139 | + gs.error("Webhook Call failed: " + ex.message); |
| 140 | + } |
| 141 | + }, |
| 142 | + |
| 143 | + type: "IncidentWebhookHelper", |
| 144 | +}; |
| 145 | +``` |
| 146 | + |
| 147 | +6. Submit and save. |
| 148 | +7. Return to the Script Includes list and continue creating. |
| 149 | +8. Click `New` to create a new Script Include. |
| 150 | +9. Enter **IncidentWebhookHelperAjax** in `Name`, select **All application scopes** in `Accessible from`. |
| 151 | +10. Keep `Client callable` and `Active` checked. |
| 152 | +11. Enter the following content in `Script`: |
| 153 | + |
| 154 | + ```js |
| 155 | + var IncidentWebhookHelperAjax = Class.create(); |
| 156 | + IncidentWebhookHelperAjax.prototype = Object.extendsObject( |
| 157 | + global.AbstractAjaxProcessor, |
| 158 | + { |
| 159 | + sendWebhook: function () { |
| 160 | + var sysId = this.getParameter("sysparm_sys_id"); |
| 161 | + var gr = new GlideRecord("incident"); |
| 162 | + if (gr.get(sysId)) { |
| 163 | + var helper = new IncidentWebhookHelper(); |
| 164 | + helper.sendIncidentWebhook(gr); |
| 165 | + return "Success"; |
| 166 | + } |
| 167 | + return "Request failed"; |
| 168 | + }, |
| 169 | + } |
| 170 | + ); |
| 171 | + ``` |
| 172 | + |
| 173 | +12. Submit and save. |
| 174 | + |
| 175 | +### Auto Sync |
| 176 | + |
| 177 | +This method relies on ServiceNow's Business Rules configuration. Using this method, you can automatically sync Incidents to Flashduty when there are new or update events. |
| 178 | +
|
| 179 | +#### Configure Business Rules |
| 180 | +
|
| 181 | +1. Log in to the ServiceNow instance console, select `ALL`, enter `Business Rules` and select `System Definition`-`Business Rules`. |
| 182 | +2. Click `New` to create a new Business Rule. |
| 183 | +3. Enter **Send To Flashduty** in `Name`, select **Incident** in `Table`. |
| 184 | +4. Keep `Advanced` and `Active` checked. |
| 185 | +5. In the `When to run` section, select **async** in `When`, keep `Insert` and `Upsert` checked, configure others as needed. |
| 186 | +6. In the `Advanced` section, enter the following content in `Script`, where you need to add the integration push URL in **endpoint**: |
| 187 | +
|
| 188 | +<div class="hide"> |
| 189 | +
|
| 190 | +Note: The body configures default receiving fields. If you have custom fields that need to be synced to Flashduty, you need to manually add content to the body. For example, if you want to add a field named: test_001 (this field name can be obtained when adding custom fields in the integration configuration, do not use the field name displayed in the ServiceNow Incident form), then you need to add to the body: test_001: current.getDisplayValue("test_001"). |
| 191 | +
|
| 192 | +</div> |
| 193 | +
|
| 194 | +```js |
| 195 | +(function executeRule(current, previous) { |
| 196 | + function getLastComment(recordSysId) { |
| 197 | + var journalGR = new GlideRecord("sys_journal_field"); |
| 198 | + journalGR.addQuery("element_id", recordSysId); |
| 199 | + journalGR.addQuery("element", "comments"); |
| 200 | + journalGR.orderByDesc("sys_created_on"); |
| 201 | + journalGR.setLimit(1); |
| 202 | + journalGR.query(); |
| 203 | + if (journalGR.next()) { |
| 204 | + var comment = journalGR.getValue("value"); |
| 205 | + return comment; |
| 206 | + } |
| 207 | +
|
| 208 | + return ""; |
| 209 | + } |
| 210 | +
|
| 211 | + var operation = current.operation() || "unknown"; |
| 212 | + var isPreviousNull = previous === null; |
| 213 | + var createdOn = current.getValue("sys_created_on"); |
| 214 | + var updatedOn = current.getValue("sys_updated_on"); |
| 215 | + var isNewRecord = createdOn === updatedOn; |
| 216 | +
|
| 217 | + var action = "update"; |
| 218 | + if (isPreviousNull && isNewRecord) { |
| 219 | + action = "insert"; |
| 220 | + } |
| 221 | +
|
| 222 | + var body = { |
| 223 | + action_type: action, |
| 224 | + number: current.getValue("number"), |
| 225 | + sys_id: current.getUniqueValue(), |
| 226 | + short_description: current.getValue("short_description"), |
| 227 | + description: current.getValue("description"), |
| 228 | + state: current.getDisplayValue("state"), |
| 229 | + impact: current.getDisplayValue("impact"), |
| 230 | + urgency: current.getDisplayValue("urgency"), |
| 231 | + comments: getLastComment(current.getUniqueValue()), |
| 232 | +<label name='field_mapping' tab='4'>{original.key}: current.getDisplayValue("{original.key}")</label> |
| 233 | +
|
| 234 | + }; |
| 235 | +
|
| 236 | + try { |
| 237 | + var endpoint = ""; |
| 238 | + var request = new sn_ws.RESTMessageV2(); |
| 239 | + request.setHttpMethod("POST"); |
| 240 | + request.setEndpoint(endpoint); |
| 241 | + request.setRequestHeader("Content-Type", "application/json"); |
| 242 | + request.setRequestBody(JSON.stringify(body)); |
| 243 | + request.executeAsync(); |
| 244 | + } catch (ex) { |
| 245 | + gs.error("Error sending webhook: " + ex.message); |
| 246 | + } |
| 247 | +})(current, previous); |
| 248 | +``` |
| 249 | +
|
| 250 | +7. Submit and save. |
| 251 | +
|
| 252 | +## Sync Information |
| 253 | +
|
| 254 | +### Form Fields |
| 255 | +
|
| 256 | +| ServiceNow | Flashduty | Notes | |
| 257 | +| ------------------- | ------------- | ----- | |
| 258 | +| Short_description | Title | | |
| 259 | +| Description | Description | | |
| 260 | +| Additional comments | Comments | | |
| 261 | +| State | Progress | | |
| 262 | +| Urgency | Severity | | |
| 263 | +| Others | Custom Fields | | |
| 264 | +
|
| 265 | +### Status Mapping |
| 266 | +
|
| 267 | +| ServiceNow | Flashduty | Notes | |
| 268 | +| ----------- | ---------- | ---------------------- | |
| 269 | +| New | Trigger | | |
| 270 | +| In Progress | Processing | | |
| 271 | +| On Hold | Snoozed | Default snooze 2 hours | |
| 272 | +| Resolved | CLosed | | |
| 273 | +| Closed | CLosed | | |
| 274 | +| Canceled | CLosed | | |
| 275 | +
|
| 276 | +### Priority Mapping |
| 277 | +
|
| 278 | +Only when ServiceNow's Urgency value changes will it affect Flashduty's Severity |
| 279 | +
|
| 280 | +| ServiceNow | Flashduty | Notes | |
| 281 | +| ---------- | --------- | ----- | |
| 282 | +| Low | Info | | |
| 283 | +| Medium | Warning | | |
| 284 | +| High | Critical | | |
0 commit comments