﻿---
title: ServiceNow connector
description: Use the ServiceNow connector to search, read, and write records, incidents, security incidents, events, and attachments in ServiceNow, and to start workflows from incident, journal, and change-approval events.
url: https://docs-v3-preview.elastic.dev/elastic/kibana/pull/303145/reference/connectors-kibana/servicenow-search-action-type
products:
  - Kibana
applies_to:
  - Elastic Cloud Serverless: Preview
  - Elastic Stack: Preview since 9.4
---

# ServiceNow connector
The ServiceNow connector connects Kibana to your ServiceNow instance through the ServiceNow Table API. Agents and workflows can use it to search and read records such as incidents, knowledge articles, comments, and attachments.
You can use this connector in **Agent Builder** and **Workflows**.
Some capabilities of this connector are only available from a certain version:
- <applies-to>Elastic Cloud Serverless: Preview</applies-to> <applies-to>Elastic Stack: Planned</applies-to> Write actions to create, update, close, and delete records, add comments and work notes, and upload attachments.
- <applies-to>Elastic Cloud Serverless: Preview</applies-to> <applies-to>Elastic Stack: Planned</applies-to> Lookup actions to find choice values, user IDs, and the identity of the connector account before you write.

<note>
  For the ServiceNow ITSM, SecOps, and ITOM connectors used with alerting and cases, refer to [ServiceNow ITSM](https://docs-v3-preview.elastic.dev/elastic/kibana/pull/303145/reference/connectors-kibana/servicenow-action-type), [ServiceNow SecOps](https://docs-v3-preview.elastic.dev/elastic/kibana/pull/303145/reference/connectors-kibana/servicenow-sir-action-type), and [ServiceNow ITOM](https://docs-v3-preview.elastic.dev/elastic/kibana/pull/303145/reference/connectors-kibana/servicenow-itom-action-type).
</note>


## Prerequisites

In ServiceNow, you need:
- The `admin` role, to register an OAuth application in the Application Registry.
- A ServiceNow user with the roles for the actions you plan to use. With OAuth 2.0 Client Credentials, this is the user associated with the OAuth client. With OAuth 2.0 Authorization Code, this is the account that signs in to authorize the connector.
  - **Read operations** (Search, Get record, List records): `itil` for incidents, `knowledge` for knowledge articles.
- <applies-to>Elastic Cloud Serverless: Preview</applies-to> <applies-to>Elastic Stack: Planned</applies-to> **Write operations** (Create/Update/Close incident, Add comment, Add work note): `itil` with write permissions.
- <applies-to>Elastic Cloud Serverless: Preview</applies-to> <applies-to>Elastic Stack: Planned</applies-to> **Security incidents** (Create security incident): `sn_si_incident_write`.
- <applies-to>Elastic Cloud Serverless: Preview</applies-to> <applies-to>Elastic Stack: Planned</applies-to> **Delete record**: admin or equivalent role on the target table.
- <applies-to>Elastic Cloud Serverless: Preview</applies-to> <applies-to>Elastic Stack: Planned</applies-to> **ITOM events** (Create event): `evt_mgmt_integration`, or `evt_mgmt_operator` or `evt_mgmt_admin`.

In Kibana, you need:
- The **Actions and Connectors : All** [privilege](https://docs-v3-preview.elastic.dev/elastic/docs-content/tree/main/deploy-manage/users-roles/cluster-or-deployment-auth/kibana-privileges), to create and edit connectors.


## Create connectors in Kibana

To use the connector, set up OAuth in ServiceNow, create the connector in Kibana, and test it.

### Set up OAuth in ServiceNow

ServiceNow connectors authenticate with OAuth 2.0. Register an OAuth application in ServiceNow for the method you plan to use, then copy its client ID and client secret.

#### OAuth 2.0 Client Credentials

1. Select **System OAuth → Application Registry**.
2. Select **New**, then select **Create an OAuth API endpoint for external clients**.
3. Enter a name for your application.
4. Enter a **Client Secret** value, or let ServiceNow generate one.
5. Select **Submit**.
6. Copy the following values from the OAuth application:
   - **Client ID**: The auto-generated client ID.
- **Client Secret**: The secret you configured.
7. Verify that the OAuth client is associated with a user account that has the roles listed in [Prerequisites](#servicenow-search-prerequisites).


#### OAuth 2.0 Authorization Code (recommended for per-user access)

Use this method to let individual users sign in to ServiceNow through Kibana. Kibana stores refreshable tokens on the user's behalf.
1. Select **System OAuth** → **Application Registry**.
2. Select **New**, then select **Create an OAuth API endpoint for external clients**.
3. Configure the application as follows:
   - **Name**: Enter a name for the application (for example, "Elastic Kibana").
- **Redirect URL**: Enter Kibana's connector OAuth callback URL. Copy the following pattern and replace your public Kibana hostname:
  ```text
  https://<your-kibana-host>/api/actions/connector/_oauth_callback
  ```
- **Client Secret**: Enter a value, or let ServiceNow generate one.
4. Select **Submit**.
5. Copy the **Client ID** and **Client Secret** from the application.

<tip>
  The connector automatically configures the correct ServiceNow OAuth endpoints for your instance. You do not need to enter the authorization or token URLs manually.
</tip>


### Connector configuration

You can create connectors in **Stack Management → Connectors**.
ServiceNow connectors support **OAuth 2.0 Client Credentials** and **OAuth 2.0 Authorization Code** authentication. Select the authentication type when you create or edit the connector.
<definitions>
  <definition term="Instance URL">
    The URL of your ServiceNow instance (for example, `https://your-instance.service-now.com`).
  </definition>
</definitions>


#### OAuth 2.0 Client Credentials

<definitions>
  <definition term="Token URL">
    The OAuth 2.0 token endpoint URL for your ServiceNow instance (for example, `https://your-instance.service-now.com/oauth_token.do`).
  </definition>
  <definition term="Client ID">
    The OAuth client ID from your ServiceNow application registry.
  </definition>
  <definition term="Client Secret">
    The OAuth client secret for your ServiceNow application.
  </definition>
</definitions>


#### OAuth 2.0 Authorization Code

<definitions>
  <definition term="Client ID">
    The OAuth client ID from your ServiceNow application registry. Refer to [OAuth Authorization Code setup](#servicenow-search-oauth-auth-code).
  </definition>
  <definition term="Client Secret">
    The OAuth client secret for your ServiceNow application.
  </definition>
</definitions>

After you enter these values, authorize with your ServiceNow account.
The connector automatically uses the correct ServiceNow OAuth endpoints for your instance (`https://<your-instance>.service-now.com/oauth_auth.do` for authorization and `https://<your-instance>.service-now.com/oauth_token.do` for token exchange). The connector handles scopes automatically.

### Connector networking configuration

Use the [Action configuration settings](/elastic/kibana/pull/303145/reference/configuration-reference/alerting-settings#action-settings) to customize connector networking, such as proxies, certificates, or TLS settings. You can set configurations that apply to all your connectors or use `xpack.actions.customHostSettings` to set per-host configurations.

### Test the connector

You can test connectors when you create or edit the connector in Kibana.
The test verifies connectivity by querying the `sys_user` table, which any authenticated user can access.

## Connector actions

The ServiceNow connector has the following actions.

### Search and read actions

Use these actions to find and read ServiceNow records, tables, comments, and attachments.
<definitions>
  <definition term="Search">
    Search for records in a ServiceNow table using full-text search.
    - `table` (required): The table to search. Common values: `incident`, `kb_knowledge`, `sc_req_item`, `change_request`, `problem`, `sc_task`, `cmdb_ci`. Custom tables are also supported.
    - `query` (required): The full-text search query string.
    - `encodedQuery` (optional): ServiceNow encoded query to combine with the full-text search for additional filtering (for example, `active=true^priority=1`). Uses `^` to AND conditions and `^OR` for OR.
    - `fields` (optional): Comma-separated list of fields to return.
    - `limit` (optional): Maximum number of results (default: 20).
    - `offset` (optional): Offset for pagination.
  </definition>
  <definition term="Get record">
    Retrieve a specific record by its `sys_id`. To retrieve a knowledge article with full content, use `table=kb_knowledge` and request fields: `sys_id,number,short_description,text,topic,category,author,sys_created_on,sys_updated_on,workflow_state,kb_knowledge_base,kb_category`.
    - `table` (required): The table containing the record.
    - `sysId` (required): The `sys_id` of the record.
    - `fields` (optional): Comma-separated list of fields to return.
  </definition>
  <definition term="List records">
    List records from a table with optional filtering.
    - `table` (required): The table to query. Common values: `incident`, `kb_knowledge`, `sc_req_item`, `change_request`, `problem`, `sc_task`, `cmdb_ci`. Custom tables are also supported.
    - `encodedQuery` (optional): ServiceNow encoded query for filtering (for example, `active=true^priority=1`).
    - `fields` (optional): Comma-separated list of fields to return.
    - `limit` (optional): Maximum number of results (default: 20).
    - `offset` (optional): Offset for pagination.
    - `orderBy` (optional): Field to order by (prefix with `-` for descending).
  </definition>
  <definition term="List knowledge bases">
    List available knowledge bases with their titles and descriptions. Use this to discover what knowledge bases exist before searching for articles.
    - `limit` (optional): Maximum number of results (default: 20).
    - `offset` (optional): Offset for pagination.
  </definition>
  <definition term="Get comments">
    Retrieve comments and work notes for a specific record. Use this action to understand the history and context of an incident, change request, or other record.
    - `tableName` (required): The table the record belongs to (for example, `incident`, `change_request`).
    - `recordSysId` (required): The `sys_id` of the record.
    - `limit` (optional): Maximum number of journal entries to return (default: 20).
    - `offset` (optional): Offset for pagination.
  </definition>
  <definition term="List tables">
    List available ServiceNow tables with their names and labels. Use this to discover what tables exist in the instance, especially for custom or unfamiliar ServiceNow configurations.
    - `query` (optional): Filter to search table names or labels (for example, `incident`, `CMDB`).
    - `limit` (optional): Maximum number of results (default: 50).
    - `offset` (optional): Offset for pagination.
  </definition>
  <definition term="Describe table">
    Describe the structure of a ServiceNow table by listing its fields, including inherited fields, with their types, labels, and constraints. Use this to find out which fields to request or filter on before you query the table.
    - `table` (required): The name of the table to describe (for example, `incident`, `kb_knowledge`, `change_request`).
  </definition>
  <definition term="Get attachment">
    Download a ServiceNow attachment as base64-encoded binary content by its attachment `sys_id`. Returns `fileName`, `contentType`, and `base64` fields. To process document content (PDFs, Word files, and so on), pass the base64 value through the Elasticsearch attachment processor. To find attachment `sys_id` values, query the `sys_attachment` table using List records with `encodedQuery=table_name=<table>^table_sys_id=<record_sys_id>`.
    - `sysId` (required): The `sys_id` of the attachment (from the `sys_attachment` table).
  </definition>
</definitions>


### Lookup actions

<applies-to>
  - Elastic Cloud Serverless: Preview
  - Elastic Stack: Planned
</applies-to>

Use these actions to find the choice values, user IDs, and account identity you need before you write to ServiceNow.
<definitions>
  <definition term="Get choices">
    Look up valid choice values for a ServiceNow field. Call this before writing to discover valid values for `state`, `close_code`, `category`, `impact`, `urgency`, and other choice-list fields. Returns values with their display labels.
    - `tableName` (required): The ServiceNow table to get choices for (for example, `incident`, `change_request`, `sn_si_incident`).
    - `fieldName` (required): The field name to get choices for (for example, `state`, `close_code`, `category`, `impact`, `urgency`, `priority`).
    - `language` (optional): Language code for choice labels (default: `en`).
  </definition>
  <definition term="Query users">
    Search ServiceNow users by name, email, or username. Use this to look up the `sys_id` for `caller_id` or `assigned_to` fields before creating or updating an incident.
    - `query` (optional): Search text to filter users by name, email, or username. Omit to list recent users.
    - `limit` (optional): Maximum number of users to return (default: 20).
    - `offset` (optional): Offset for pagination.
  </definition>
  <definition term="Who am I">
    Return the identity of the currently authenticated ServiceNow user. Returns `sys_id`, `user_name`, `name`, `email`, `title`, `department`, and `active` status. Use this to verify connector credentials, find the `sys_id` of the connector account for use as `caller_id`, or confirm which account is performing write operations. Takes no parameters.
  </definition>
</definitions>


### Write actions

<applies-to>
  - Elastic Cloud Serverless: Preview
  - Elastic Stack: Planned
</applies-to>

These actions change data in ServiceNow. The connector user needs the write roles listed in [Prerequisites](#servicenow-search-prerequisites).
<definitions>
  <definition term="Create record">
    Insert a new record into any ServiceNow table. Returns the created record including its `sys_id` and record number. For ITSM incidents, use Create incident; for security incidents, use Create security incident; for ITOM events, use Create event. Use this action for all other tables.
    - `table` (required): The table to insert the record into.
    - `fields` (required): Key-value map of ServiceNow field names to values for the new record (for example, `{"short_description": "VPN issue", "impact": "2"}`). At least one field required; maximum 100 fields.
  </definition>
  <definition term="Update record">
    Update an existing record in any ServiceNow table by its `sys_id`. Provide only the fields that need to change — the connector leaves all other fields untouched. Returns the full updated record. For ITSM incidents, use Update incident.
    - `table` (required): The table containing the record.
    - `sysId` (required): The `sys_id` of the record to update.
    - `fields` (required): Key-value map of field names to their new values. At least one field required; maximum 100 fields.
  </definition>
  <definition term="Create incident">
    Create a new ITSM incident in ServiceNow. Returns the created incident including its `sys_id` and incident number (for example, `INC0012345`). Use Query users to resolve names to `sys_id` values for `caller_id` and `assigned_to`. Use Get choices to discover valid values for `category`, `impact`, and `urgency`.
    - `short_description` (required): Brief one-line summary of the incident.
    - `description` (optional): Detailed description.
    - `caller_id` (optional): `sys_id` or username of the reporting user.
    - `impact` (optional): Business impact — `1`=High, `2`=Medium, `3`=Low.
    - `urgency` (optional): Urgency level — `1`=High, `2`=Medium, `3`=Low.
    - `category` (optional): Incident category.
    - `subcategory` (optional): Incident subcategory.
    - `assignment_group` (optional): `sys_id` or name of the assignment group.
    - `assigned_to` (optional): `sys_id` or username of the assigned technician.
    - `comments` (optional): Initial customer-visible comment.
    - `work_notes` (optional): Initial internal work note (not visible to the caller).
  </definition>
  <definition term="Update incident">
    Update an existing ITSM incident by its `sys_id`. Provide only the fields to change. Returns the updated incident. To resolve or close an incident, use Close incident instead.
    - `sysId` (required): The `sys_id` of the incident to update.
    - `short_description` (optional): Updated brief summary.
    - `description` (optional): Updated detailed description.
    - `state` (optional): Incident state — `1`=New, `2`=In Progress, `3`=On Hold, `6`=Resolved, `7`=Closed.
    - `caller_id` (optional): `sys_id` or username of the caller.
    - `impact` (optional): Business impact — `1`=High, `2`=Medium, `3`=Low.
    - `urgency` (optional): Urgency level — `1`=High, `2`=Medium, `3`=Low.
    - `category` (optional): Incident category.
    - `subcategory` (optional): Incident subcategory.
    - `assignment_group` (optional): `sys_id` or name of the assignment group.
    - `assigned_to` (optional): `sys_id` or username of the assigned technician.
    - `comments` (optional): Customer-visible comment to append.
    - `work_notes` (optional): Internal work note to append (not visible to the caller).
    - `close_code` (optional): Resolution close code (use Get choices with `tableName=incident`, `fieldName=close_code`).
    - `close_notes` (optional): Detailed resolution notes (required when setting state to `6` or `7`).
  </definition>
  <definition term="Add comment">
    Add a customer-visible comment to a ServiceNow record. The comment appears in the record journal and is visible to the caller. Use Add work note for internal-only notes.
    - `table` (required): The table containing the record (for example, `incident`, `change_request`).
    - `sysId` (required): The `sys_id` of the record.
    - `comment` (required): The comment text to add.
  </definition>
  <definition term="Add work note">
    Add an internal work note to a ServiceNow record. Work notes are only visible to agents and never shown to the caller. Use Add comment for customer-facing journal entries.
    - `table` (required): The table containing the record (for example, `incident`, `change_request`).
    - `sysId` (required): The `sys_id` of the record.
    - `workNote` (required): The internal work note text to add.
  </definition>
  <definition term="Close incident">
    Resolve or close a ServiceNow incident by setting its state to Resolved (`6`) or Closed (`7`). A close code and close notes are required. Use Get choices with `tableName=incident`, `fieldName=close_code` to see valid close codes for the instance.
    - `sysId` (required): The `sys_id` of the incident to close.
    - `closeCode` (required): Resolution close code.
    - `closeNotes` (required): Detailed description of how the incident was resolved.
    - `state` (optional): Final state — `6`=Resolved, `7`=Closed (default: `6`).
  </definition>
  <definition term="Create security incident">
    Create a new Security Operations (SecOps/SIR) incident in the `sn_si_incident` table. Use this for cyber security incidents and threat investigations rather than ITSM incidents. Returns the created incident with its `sys_id`.
    <note>
      Requires the **ServiceNow Security Incident Response (SIR)** plugin to be installed on the ServiceNow instance. The connector user must have the `sn_si_incident_write` role. If the plugin is absent, the connector returns a 400 error stating that the table does not exist.
    </note>
    - `short_description` (required): Brief summary of the security incident.
    - `description` (optional): Detailed description.
    - `priority` (optional): Priority — `1`=Critical, `2`=High, `3`=Moderate, `4`=Low, `5`=Planning.
    - `category` (optional): Security incident category (use Get choices with `tableName=sn_si_incident`, `fieldName=category`).
    - `subcategory` (optional): Security incident subcategory.
    - `assignment_group` (optional): `sys_id` or name of the assignment group.
    - `assigned_to` (optional): `sys_id` or username of the assigned analyst.
    - `affected_user` (optional): `sys_id` or username of the affected user.
    - `comments` (optional): Initial customer-visible comment.
    - `work_notes` (optional): Initial internal work note.
    - `business_criticality` (optional): Business criticality — `1`=Critical, `2`=High, `3`=Medium, `4`=Low, `5`=Negligible.
  </definition>
  <definition term="Create event">
    Send an ITOM event to ServiceNow Event Management via `/api/global/em/jsonv2`. Creates or updates an alert in the Event Management console. Use `message_key` to deduplicate: events with the same `source`, `node`, `type`, and `message_key` update the existing alert instead of creating a new one.
    <note>
      Requires the **ServiceNow Event Management (ITOM)** plugin to be installed on the ServiceNow instance. The connector user must have the `evt_mgmt_integration` role (or `evt_mgmt_operator`/`evt_mgmt_admin`). If the plugin is absent or the role is missing, the connector returns a 400 error.
    </note>
    - `source` (required): Event source system (for example, `"Elastic"`, `"monitoring-agent"`).
    - `type` (required): Event type or category (for example, `"high_cpu"`, `"service_down"`).
    - `node` (optional): Hostname or IP address of the affected node.
    - `resource` (optional): Affected resource name (disk partition, service name, and so on).
    - `metric_name` (optional): Name of the metric that triggered the event.
    - `value` (optional): Current metric value at the time of the event (for example, `"95.2"`).
    - `severity` (optional): Severity — `0`=Clear, `1`=Critical, `2`=Major, `3`=Minor, `4`=Warning, `5`=Info.
    - `description` (optional): Detailed description of the event.
    - `message_key` (optional): Unique key for deduplication.
    - `additional_info` (optional): Extra key-value metadata to attach to the event (maximum 50 entries).
  </definition>
  <definition term="Upload attachment">
    Upload a file attachment to a ServiceNow record. The file must be provided as base64-encoded content. Returns the attachment metadata including the new attachment `sys_id`. Avoid files larger than 5 MB. To retrieve existing attachments use Get attachment.
    - `tableName` (required): The ServiceNow table to attach the file to (for example, `incident`, `change_request`).
    - `tableSysId` (required): The `sys_id` of the record to attach the file to.
    - `fileName` (required): Name of the file including extension (for example, `screenshot.png`, `report.pdf`).
    - `contentType` (required): MIME type of the file (for example, `application/pdf`, `image/png`, `text/plain`).
    - `base64Content` (required): Base64-encoded file content.
  </definition>
  <definition term="Delete record">
    Permanently delete a record from a ServiceNow table by its `sys_id`. This operation cannot be undone. Use only for automation-created records that need cleanup — prefer updating state to "Cancelled" or "Closed" over deleting business records.
    - `table` (required): The table containing the record to delete.
    - `sysId` (required): The `sys_id` of the record to permanently delete.
  </definition>
</definitions>


## Receive ServiceNow events

<applies-to>
  - Elastic Cloud Serverless: Preview
  - Elastic Stack: Planned
</applies-to>

The connector can start a workflow from an incident, a journal entry, or a change-approval update. A saved connector does not receive events until **Receive events** is turned on for that connector. After you save a new connector, Kibana shows the **Webhook URL** and the **Ingest token** once, so copy them then. For an existing connector, save it, then select **Rotate ingest token**. A new token stops the previous one from working. This toggle appears when `xpack.actions.inboundEvents.enabled` is `true`.
In ServiceNow, you need the `admin` role to create the outbound REST message and the business rules. Incident and journal events need Incident Management, which provides the `incident` table. Change-approval events need Change Management, which provides `change_request`. Without Change Management, you can still use incident and journal events. ServiceNow must be able to reach a Kibana URL. A developer instance cannot call `localhost`.
In Kibana, you need an Enterprise license and `server.publicBaseUrl` set to the host that ServiceNow uses to reach Kibana. `xpack.actions.inboundEvents.enabled` defaults to `true`. Set it to `false` to turn receive events off.
Kibana starts the workflows that are enabled and subscribed to the matching event. To send events from ServiceNow, refer to [Start a workflow from ServiceNow events](#servicenow-search-inbound-howto).
The connector supports six events. A workflow subscribes to an event by using its ID as the trigger `type`.

| Workflow event                                    | `occurrence`                    | Required fields                                         | Optional fields                                                   |
|---------------------------------------------------|---------------------------------|---------------------------------------------------------|-------------------------------------------------------------------|
| `servicenow_search.incident_created`              | `incident.created`              | `table`, `sys_id`, `number`                             | `summary`, `state`, `priority`, `assignment_group`, `assigned_to` |
| `servicenow_search.incident_updated`              | `incident.updated`              | `table`, `sys_id`                                       | `number`, `changed_fields`                                        |
| `servicenow_search.incident_resolved`             | `incident.resolved`             | `table`, `sys_id`, `state`                              | `number`, `close_code`, `close_notes`                             |
| `servicenow_search.comment_added`                 | `comment.added`                 | `table`, `sys_id`, `journal_entry_id`, `author`, `text` | `number`, `timestamp`                                             |
| `servicenow_search.work_note_added`               | `work_note.added`               | `table`, `sys_id`, `journal_entry_id`, `author`, `text` | `number`, `timestamp`                                             |
| `servicenow_search.change_approval_state_changed` | `change.approval_state_changed` | `change_request_id`, `approval_id`, `state`             | `approver`, `previous_state`                                      |

Reference fields are sys_ids (`getValue()`), not display values. `sys_updated_on` is used only to correlate retries. It is not a workflow field. A body that omits it still starts the workflow, with a new correlation key. `summary` is the incident short description. `changed_fields` is an array of up to 20 objects, `{ "field", "previous", "current" }`. `previous` and `current` are included when ServiceNow sent them. Identifiers are limited to 128 characters, field values and `summary` to 1,024, and `text` and `close_notes` to 40,000.
`incident.resolved` covers both resolved and closed. The resulting `state` is on the event, so a workflow condition can keep one of the two. A comment, a work note, and an approval are separate occurrences. A workflow subscribed to one of them does not run for the others.
An `occurrence` that is not in this table, or a body missing a required field, does not start these workflows.

### Webhook URL and authentication

ServiceNow sends each event to the webhook URL of the connector. Copy the **Webhook URL** from the connector. It has one of these shapes. The default space has no `/s/{space-id}` prefix.
```text
https://<kibana-host>/api/actions/events/.servicenow_search/<connector-id>
https://<kibana-host>/s/<space-id>/api/actions/events/.servicenow_search/<connector-id>
```

Send `Authorization: Bearer <ingest-token>` and `Content-Type: application/json`. If the REST message cannot set `Authorization`, add `?token=<ingest-token>` to the URL instead.

## Start a workflow from ServiceNow events

<applies-to>
  - Elastic Cloud Serverless: Preview
  - Elastic Stack: Planned
</applies-to>

To run a Kibana workflow when an incident, comment, work note, or change approval changes in ServiceNow, create an outbound REST message and business rules in ServiceNow, then subscribe a workflow to the event. Kibana does not create the ServiceNow rules for you.
Before you start, meet the requirements in [Receive ServiceNow events](#servicenow-search-inbound-events) and turn on **Receive events** on the connector. Create the REST message first, then the rules that use it. ServiceNow does not send a record that was saved before the REST message and the matching rule existed, so create a new record to test.
The rules run after the database write and send the call with `executeAsync()`, so the user transaction does not wait on Kibana. Flow Designer and IntegrationHub are not required. The user who changes a record must be able to read that record. The journal rule sends a work note only when that user can read `work_notes` on the incident.

### Create the outbound REST message

In the ServiceNow banner, select **All** and enter `REST Message`. Open **System Web Services → Outbound → REST Message**. The Admin workspace menu does not list this record.
Create a message whose **Name** is exactly `Elastic Workflows`, then save it before adding a method. Under **HTTP Methods**, select **New** and set:
- **Name**: `post`. The scripts look up this name. It is case sensitive. **POST**, `Default POST`, or any other name throws `com.glide.communications.ProcessingException` and writes nothing to the outbound HTTP log. That error is in **System Logs → Errors**.
- **HTTP method**: POST. This dropdown is the verb. It is separate from **Name**.
- **Endpoint**: the **Webhook URL** from the connector, on a host the ServiceNow instance can reach.
- HTTP header `Content-Type`: `application/json`
- HTTP header `Authorization`: `Bearer <ingest-token>`, where the value is the **Ingest token** from the connector

The three business rules call `new sn_ws.RESTMessageV2('Elastic Workflows', 'post')`. If you rotate the ingest token in Kibana, update this header, or the `token` query parameter, with the new token and save the message.

### Create the incident rule

Select **All**, enter `Business Rules`, and open **System Definition → Business Rules**. Create an **after** business rule on `incident` with **Advanced**, **Insert**, and **Update** selected. Put the script on the **Advanced** tab. Set **When** to **after**, not **async**. An **async** rule has no `previous` record, so `previous.nil()` is true for an update and the workflow receives `servicenow_search.incident_created`.
On insert, `current.operation()` is `insert` and the script sends `incident.created`.
When `state` changes to the resolved or closed value, it sends `incident.resolved` and does not also send `incident.updated`. Out-of-box values are `6` (Resolved) and `7` (Closed). If this instance uses different choices, change those two values in the script. Any other watched-field change sends `incident.updated` with `changed_fields`. A journal-only update changes none of those fields, so the script does not call Kibana. Comments and work notes are delivered by the journal rule.
```javascript
(function executeRule(current, previous) {
  var isInsert = current.operation() == 'insert';
  var state = current.getValue('state');
  var stateChanged = !isInsert && current.state.changes();
  var isResolvedOrClosed = stateChanged && (state == '6' || state == '7');
  var occurrence;

  if (isInsert) {
    occurrence = 'incident.created';
  } else if (isResolvedOrClosed) {
    occurrence = 'incident.resolved';
  } else {
    occurrence = 'incident.updated';
  }

  var watched = [
    'short_description', 'state', 'priority', 'assignment_group', 'assigned_to',
    'urgency', 'impact', 'category', 'caller_id', 'cmdb_ci', 'close_code', 'close_notes'
  ];
  var changedFields = [];

  if (occurrence == 'incident.updated') {
    for (var i = 0; i < watched.length; i++) {
      var fieldName = watched[i];
      if (current[fieldName].changes()) {
        changedFields.push({
          field: fieldName,
          previous: previous.getValue(fieldName),
          current: current.getValue(fieldName)
        });
      }
    }
    if (changedFields.length === 0) {
      return;
    }
  }

  var body = {
    occurrence: occurrence,
    table: current.getTableName(),
    sys_id: current.getUniqueValue(),
    number: current.getValue('number'),
    sys_updated_on: current.getValue('sys_updated_on')
  };

  if (occurrence == 'incident.created') {
    body.summary = current.getValue('short_description');
    body.state = state;
    body.priority = current.getValue('priority');
    body.assignment_group = current.getValue('assignment_group');
    body.assigned_to = current.getValue('assigned_to');
  } else if (occurrence == 'incident.updated') {
    body.changed_fields = changedFields.slice(0, 20);
  } else {
    body.state = state;
    body.close_code = current.getValue('close_code');
    body.close_notes = current.getValue('close_notes');
  }

  var request = new sn_ws.RESTMessageV2('Elastic Workflows', 'post');
  request.setRequestBody(JSON.stringify(body));
  request.executeAsync();
})(current, previous);
```


### Create the journal rule

Create an **after** business rule on `sys_journal_field` with **Insert** selected. Set the condition to `name=incident` and `element` in `comments`, `work_notes`. `comments` sends `comment.added`. `work_notes` sends `work_note.added` only when the user who saved the record can read `work_notes` on the parent incident. `table` and `sys_id` identify that incident (`name` and `element_id`). `author` is `sys_created_by` on the journal row.
```javascript
(function executeRule(current, previous) {
  var element = current.getValue('element');
  var occurrence;

  if (element == 'comments') {
    occurrence = 'comment.added';
  } else if (element == 'work_notes') {
    occurrence = 'work_note.added';
  } else {
    return;
  }

  var parent = new GlideRecord(current.getValue('name'));
  var parentFound = parent.get(current.getValue('element_id'));
  if (element == 'work_notes' && (!parentFound || !parent.work_notes.canRead())) {
    return;
  }

  var text = current.getValue('value');
  var author = current.getValue('sys_created_by');
  if (!text || !author) {
    return;
  }

  var body = {
    occurrence: occurrence,
    table: current.getValue('name'),
    sys_id: current.getValue('element_id'),
    journal_entry_id: current.getUniqueValue(),
    author: author,
    text: text,
    timestamp: current.getValue('sys_created_on')
  };
  if (parentFound) {
    body.number = parent.getValue('number');
  }

  var request = new sn_ws.RESTMessageV2('Elastic Workflows', 'post');
  request.setRequestBody(JSON.stringify(body));
  request.executeAsync();
})(current, previous);
```


### Create the change-approval rule

Create an **after** business rule on `sysapproval_approver` with **Update** selected and a condition that **State** changes. **after** is required so `previous` contains the approval state before the change. The script sends `change.approval_state_changed` only when `sysapproval` is the sys_id of a `change_request`. Approvals for other tables are ignored. `approver` is the approver's sys_id. `state` is the current approval choice, such as `approved` or `rejected`.
```javascript
(function executeRule(current, previous) {
  if (!current.state.changes()) {
    return;
  }

  var changeRequestId = current.getValue('sysapproval');
  var changeRequest = new GlideRecord('change_request');
  if (!changeRequest.get(changeRequestId)) {
    return;
  }

  var body = {
    occurrence: 'change.approval_state_changed',
    change_request_id: changeRequestId,
    approval_id: current.getUniqueValue(),
    state: current.getValue('state'),
    approver: current.getValue('approver'),
    previous_state: previous.getValue('state')
  };

  var request = new sn_ws.RESTMessageV2('Elastic Workflows', 'post');
  request.setRequestBody(JSON.stringify(body));
  request.executeAsync();
})(current, previous);
```


### Confirm that ServiceNow calls Kibana

Create a new incident after the REST message and the incident rule are saved. Then select **All**, enter `Outbound HTTP Log`, and open the list at `sys_outbound_http_log_list.do`. The form `sys_outbound_http_log.do` is a new record, not the list of calls.
Look for a row whose hostname is the Kibana host:
- **No Kibana row, and System Logs → Errors shows `ProcessingException`.** The REST message **Name** is not `Elastic Workflows`, or the HTTP method **Name** is not `post`.
- **No Kibana row and no error.** The incident script returned before the REST call. On an update, a watched field has to change. A journal-only save does not call Kibana.
- **An update starts `servicenow_search.incident_created`.** **When** is **async**. Set it to **after** and detect the insert with `current.operation() == 'insert'` only. An async rule has no `previous` object, so treating an empty `previous` as an insert labels every save as created.
- **A connection error.** The endpoint is not reachable from the ServiceNow instance. Use a public host, and set `server.publicBaseUrl` to that host.
- **HTTP 404.** The ingest token does not match the connector, or **Receive events** was not saved before the token was rotated.
- **HTTP 202.** Kibana accepted the event. The workflow must be enabled and subscribed to that event id, such as `servicenow_search.incident_created`.

Rows for `signaldc.service-now.com` are ServiceNow telemetry. A response status of `-1` on those rows is not a Kibana response.

### Subscribe a workflow to an event

This workflow runs for `servicenow_search.incident_created` when the priority is `1`. Replace `<connector-id>` with the connector instance id. For another space, use the webhook URL copied from that space's connector.
```yaml
name: Notify on a new priority 1 incident
enabled: true
triggers:
  - type: servicenow_search.incident_created
    connector-id: <connector-id>
    on:
      condition: "event.priority:1"
steps:
  - name: log_incident
    type: console
    with:
      message: "Incident {{ event.number }} was created: {{ event.summary }}"
```


### Test with a sample event

POST this body to the ingest URL. It starts `servicenow_search.incident_created` with `number` `INC0010001`, `summary` `VPN is down`, and `priority` `1`.
```bash
curl -X POST \
  'https://<kibana-host>/api/actions/events/.servicenow_search/<connector-id>' \
  -H 'Authorization: Bearer <ingest-token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "occurrence": "incident.created",
    "table": "incident",
    "sys_id": "46b66a40a9fe198101d44d884e7d3a1a",
    "number": "INC0010001",
    "summary": "VPN is down",
    "state": "1",
    "priority": "1",
    "assignment_group": "group-1",
    "assigned_to": "user-1",
    "sys_updated_on": "2026-01-01 12:00:00"
  }'
```