Integrating ServiceNow Incidents with Elastic AI Agents for Observability Analysis
Source: Dev.to
Introduction
This is an example of calling an Elastic AI Agent from a ServiceNow incident.
The ServiceNow version used is Zurich.
The workflow involves having the Elastic AI Agent investigate the incident details and recording the returned results back into the incident.
[ServiceNow Incident]
│
▼
[UI Action (Manual Trigger)]
│
▼
[ServiceNow Flow Designer (Subflow)]
│
▼
[ServiceNow Action (Integration Step)]
│
▼
[Elastic Workflow (Orchestration)]
│
▼
[Elastic AI Agent (Agent Builder)]
│
▼
[Elasticsearch Observability Data]
Elastic Workflow Definition
Save a workflow with the following definition. Make sure to copy the Workflow ID from its URL – you’ll need it later to call the workflow from ServiceNow.
name: test_servicenow
enabled: true
triggers:
- type: manual
inputs:
- name: incident_number
type: string
required: true
- name: incident_sys_id
type: string
required: true
- name: short_description
type: string
required: true
- name: priority
type: string
required: true
steps:
- name: console
type: console
with:
message: "{{inputs.incident_number}} {{inputs.incident_sys_id}} {{inputs.priority}} {{inputs.short_description}}"
- name: check_with_ai_agent
type: ai.agent
with:
message: "Output in plain text format and **NO** markdown. Check index: kibana_sample_data_flights and get information of: {{ inputs.short_description }}"
- name: post_to_snow
type: http
with:
url: "https:///api/now/table/incident/{{ inputs.incident_sys_id }}"
method: PATCH
headers:
Authorization: "Basic "
Content-Type: "application/json"
body: '{"work_notes": "{{ steps.check_with_ai_agent.output.message | replace: "\n", "\\n"}}"}'
Basic authentication – set the Base64‑encoded string of username:password.
For testing you can use the admin credentials, e.g.:
echo -n "admin:yourPassword" | base64
Script Include: ElasticKibanaHelper
// Script Include: ElasticKibanaHelper
// Scope: Global
// Client callable: false
var ElasticKibanaHelper = Class.create();
ElasticKibanaHelper.prototype = {
initialize: function() {
this.baseUrl = 'https://.elastic.cloud';
this.apiKey = gs.getProperty('elastic.kibana.api_key', '');
this.timeout = 30000; // 30 seconds
},
/**
* Trigger a Kibana Workflow by ID.
*
* @param {String} workflowId - The Kibana workflow ID to run
* @param {Object} bodyParams - Optional JSON payload to send with the request
* @returns {Object} { success: Boolean, status: Number, body: Object, error: String }
*/
runWorkflow: function(workflowId, bodyParams) {
if (!workflowId) {
return this._error('workflowId is required');
}
if (!this.apiKey) {
gs.error('ElasticKibanaHelper: elastic.kibana.api_key system property is not set');
return this._error('API key not configured. Set the elastic.kibana.api_key system property.');
}
var endpoint = this.baseUrl + '/api/workflows/workflow/' + workflowId + '/run';
var payload = JSON.stringify({ inputs: bodyParams || {} });
try {
var request = new sn_ws.RESTMessageV2();
request.setEndpoint(endpoint);
request.setHttpMethod('POST');
// ── Authentication ───────────────────────────────────────
request.setRequestHeader('Authorization', 'ApiKey ' + this.apiKey);
// ── Standard headers ───────────────────────────────────────
request.setRequestHeader('Content-Type', 'application/json');
request.setRequestHeader('kbn-xsrf', 'true'); // Required by Kibana REST API
// ── Body ───────────────────────────────────────────────────
request.setRequestBody(payload);
// Timeout
request.setEccParameter('connect_timeout', this.timeout);
request.setEccParameter('read_timeout', this.timeout);
var response = request.execute();
var statusCode = response.getStatusCode();
var bodyText = response.getBody();
gs.info(
'ElasticKibanaHelper.runWorkflow | workflowId=' + workflowId +
' | status=' + statusCode
);
var parsedBody = {};
try { parsedBody = JSON.parse(bodyText); } catch (e) { parsedBody = { raw: bodyText }; }
if (statusCode >= 200 && statusCode < 300) {
return { success: true, status: statusCode, body: parsedBody };
} else {
return this._error('Request failed', statusCode, parsedBody);
}
} catch (ex) {
return this._error('Exception: ' + ex.message);
}
},
_error: function(message, status, body) {
return { success: false, status: status || 0, body: body || null, error: message };
}
};
Note: The ID of the Elastic Workflow to be executed is hard‑coded in the script above.
Form Button Script
(function() {
try {
var inputs = {};
// Set "current" to the "Name" defined in the Subflow Input (e.g., incident_record)
inputs['incident_record'] = current;
// Test with "synchronous execution" that is guaranteed to work
// * Be sure to use the Internal Name copied from the Subflow Properties for 'global.xxx'
var result = sn_fd.FlowAPI.executeSubflow('global.from_button_trigger_elastic_investigation', inputs);
// For synchronous execution, you can directly retrieve information from the returned object
gs.addInfoMessage('Executed Subflow');
} catch (ex) {
// Force display of the error type and details
var errorDetail = (ex.message) ? ex.message : ex.toString();
gs.addErrorMessage('[Debug] Error details: ' + errorDetail);
// Also output detailed information to the system log (sys_log)
gs.error('Subflow Debug: ' + errorDetail);
}
// Keep the current screen
action.setRedirectURL(current);
})();
Validation Steps
- Create an Incident in ServiceNow.
- Verify that the “Execute data investigation in Elastic AI” button appears in the top‑right corner of the incident form.
- The Short description field will be used as the investigation request sent to the Elastic AI Agent.
- Save the incident, then reopen it and click the button.
Expected Outcomes
- A toast/message “Executed Subflow” is displayed.
- The Elastic Workflow runs successfully.
- Results from the Elastic AI Agent are returned and populated on the incident record.
Context
Before AI Agents, integrations returned only limited data.
Now, AI Agents can perform sophisticated analyses and return results in a human‑readable format, greatly enhancing incident management effectiveness.