Connect a ServiceNow instance so Escape can create incidents from issues, attach request evidence, and keep each incident's state in sync with its Escape issue.

> [!IMPORTANT]
>
> **Workflow Required for Incident Creation**
>
> A workflow must include a ServiceNow export action before Escape can create incidents. After you connect the integration, [create a workflow](<https://docs.escape.tech/documentation/automate/workflows/index.md>) with that export action.

## Configuration

### Create the Escape Role

Escape reads and updates incidents with the permissions of a ServiceNow user. That user needs the permissions of a role named `escape_integration`. Create it in ServiceNow and add these roles to it:

| Role | Why Escape needs it |
| --- | --- |
| `itil` | Read, create, and update incidents |
| `personalize_choices` | List incident choices (states, categories, subcategories, close codes) for the action form and status sync |
| `sn_sec_core.read_dictionary` | List incident fields, including custom `u_*` fields, for field mappings |

Without `personalize_choices` and `sn_sec_core.read_dictionary`, the **Category**, **Subcategory**, and field dropdowns of the action form fail with HTTP 403.

#### Use ACLs Instead of Roles

`personalize_choices` also lets the user create, edit, and delete choice list entries. If you'd rather grant read-only access, or your instance doesn't have one of these roles, replace `personalize_choices` or `sn_sec_core.read_dictionary` with read ACLs. Keep `itil` in every case: Escape needs it to create and update incidents.

1. Elevate to `security_admin`.
2. Go to **System Security** \> **Access Control (ACL)** \> **New**.
3. Set type `record`, operation `read`, and required role `escape_integration`.
4. Create the ACLs that replace the missing role:

   | Instead of | Create |
   | --- | --- |
   | `personalize_choices` | One read ACL on `sys_choice`, field `*` |
   | `sn_sec_core.read_dictionary` | Two read ACLs on `sys_dictionary`: one with field `-- None --`, one with field `*` |

#### Allow Reopening Closed and Canceled Incidents (Optional)

Escape reopens an incident when its issue opens again, for example when a scan finds a resolved issue again or someone reopens it in Escape. That's often after the incident is closed: by default, ServiceNow closes Resolved incidents automatically after 7 days, and False positive or Ignored issues cancel their incident.

ServiceNow's built-in ACLs don't let `itil` users change a **Closed** or **Canceled** incident. Without the two ACLs below, the reopen fails with HTTP 403: the issue is open in Escape while its incident stays closed. Skip this step if you don't need incidents to reopen.

1. Elevate to `security_admin`.
2. Go to **System Security** \> **Access Control (ACL)** \> **New**.
3. Create a first ACL: type `record`, operation `write`, table `incident`, field `-- None --`, required role `escape_integration`.
4. Create a second ACL: type `record`, operation `write`, table `incident`, field `State`, required role `escape_integration`.

You need both. With the first one alone, ServiceNow accepts the update but keeps the old state.

### Choose an Authentication Method

Pick one of these methods:

| Method | What you set up in ServiceNow | Escape acts as |
| --- | --- | --- |
| Basic auth | A user with a password | That user |
| API key | A user and a REST API key | The user the key belongs to |
| OAuth | An OAuth application | The user who authorizes it |

In every case, that user needs the [`escape_integration` role](<#create-the-escape-role>).

> [!WARNING]
>
> **API Key, OAuth, and REST API Access Policies**
>
> ServiceNow uses **REST API Access Policies** to decide which authentication methods can call an API. A policy only accepts the methods it lists.
>
> For API key and OAuth, you add your method to the policies of the **Table API** and the **Attachment API**. Once those policies exist, they also apply to Basic auth: if you use Basic auth, or another tool does, keep its profile listed too. Any method missing from the list fails with HTTP 401.

#### Basic Auth

1. Create a user for Escape, for example `escape.integration`, and check **Internal Integration User**.
2. Grant it the `escape_integration` role.
3. Set a password.

#### API Key

The steps below follow ServiceNow's [Inbound REST API keys](<https://www.servicenow.com/community/developer-advocate-blog/inbound-rest-api-keys/ba-p/2854924>) guide. They need the **API Key and HMAC Authentication** plugin and the `admin` role.

1. Create a user with the `escape_integration` role, as in steps 1 and 2 of [Basic Auth](<#basic-auth>).
2. Create an authentication profile for API keys:

   1. Go to **All** \> **System Web Services** \> **API Access Policies** \> **Inbound Authentication Profile**, and click **New**.
   2. Click **Create API Key authentication profiles**.
   3. Set a **Name**, and set **Auth Parameter** to **Header for API Key** (the `x-sn-apikey` header).
   4. Click **Submit**.
3. Create the API key:

   1. Go to **All** \> **System Web Services** \> **API Access Policies** \> **REST API Key**, and click **New**.
   2. Set a **Name**, and set **User** to the user from step 1.
   3. Save the record. ServiceNow generates the key in the **Token** field.
   4. Click the lock icon next to **Token**, and copy the key.
4. Allow the profile on the Table API:

   1. Go to **All** \> **System Web Services** \> **API Access Policies** \> **REST API Access Policies**.
   2. Open the policy of the **Table API**, or click **New** and set **REST API** to **Table API** if there's none.
   3. In the **Inbound authentication profiles** list at the bottom of the form, click **Insert a new row...** and pick the profile from step 2. Keep the profiles already listed. For a new policy, also add the profiles your other tools use, for example **BasicAuth for none public processors** for Basic auth, or they get HTTP 401.
   4. Click **Update**, or **Submit** for a new policy.
5. Repeat step 4 for the **Attachment API**.

#### OAuth

Escape uses the OAuth 2.0 authorization code flow with the `useraccount` scope, so it acts as the user who authorizes it. That user needs the `escape_integration` role.

1. Go to **System OAuth** \> **Application Registry** \> **New**, and pick **Create an OAuth API endpoint for external clients**.
2. Set **Redirect URL** to:

   ```text
   https://app.escape.tech/integrations-next/servicenow/oauth/callback
   ```
3. Add the `useraccount` **auth scope**, and leave **Public Client** unchecked.
4. Save, then copy the **Client ID** and **Client Secret**.
5. In **All** \> **System Web Services** \> **API Access Policies** \> **REST API Access Policies**, add the **OAuth token** authentication profile to the policies of the **Table API** and the **Attachment API**, like step 4 of [API Key](<#api-key>).

### Create a ServiceNow Integration

1. Go to the [Integrations page](<https://app.escape.tech/integrations-next/>) from the left navigation menu.
2. Click the [ServiceNow](<https://app.escape.tech/integrations-next/servicenow>) card in the **Ticketing** section.
3. Click **\+ Create new integration**.
4. Fill in the form based on your authentication method:

   - **Name**: a name to identify this integration
   - **Instance URL**: your ServiceNow URL, for example `https://acme.service-now.com`

   **For Basic auth:**

   - **Username**: the user ID, for example `escape.integration`
   - **Password**: its password

   **For API key:**

   - **API key**: the key from the **Token** field

   **For OAuth:**

   - **Client ID** and **Client secret**: from the OAuth application
5. Under **Network Configuration**, pick the location Escape should use to reach ServiceNow. Use a private location when the instance isn't reachable from the public internet.
6. For Basic auth and API key: click **Test Integration**, then **Connect Integration**.
7. For OAuth:

   1. Click **Connect Integration** to save the integration.
   2. In its **Settings** tab, click **Authorize**, then sign in to ServiceNow as the user Escape should act as, and allow access.
   3. Click **Test Integration**.

   If the authorization expires or is revoked, click **Authorize** again.

**Test Integration** checks whether the Table API accepts an incident list request. It does not confirm that the user can see incident records or create and update them. Check the validation logs to see the results.

| When Validation Succeeds |  | When Validation Fails |
| --- | --- | --- |
|  |  |  |
| The logs show **All good!** |  | The logs show the error message. See the table below for what to check. |

| Error | What to check |
| --- | --- |
| Could not reach the ServiceNow instance | The instance URL, and the network location if the instance isn't public |
| Rejected the username or password (HTTP 401) | The username and password |
| Rejected the API key (HTTP 401) | The key is active, its user has the `escape_integration` role, and the Table API and Attachment API policies list its profile |
| Rejected the OAuth token (HTTP 401) | The Table API and Attachment API policies list the OAuth token profile, then **Authorize** again |
| Not allowed to ... (HTTP 403) | The roles in [Create the Escape Role](<#create-the-escape-role>) |
| Save the integration first, then click Authorize | For OAuth, save and authorize the integration before testing it |

> [!WARNING]
>
> **Never share your password, API key, or client secret**
>
> Create dedicated credentials for Escape, and don't reuse them in other tools.

### Edit a ServiceNow Integration

1. Go to the [Integrations page](<https://app.escape.tech/integrations-next/>).
2. Open the [ServiceNow](<https://app.escape.tech/integrations-next/servicenow>) card.
3. Click **Edit** next to the integration.
4. Update the URL, credentials, or network location.
5. For OAuth, click **Authorize** again if you need a new authorization.
6. Save, then **Test Integration**.

You can't change the authentication method of a saved integration. To use another method, create a new integration.

### Delete a ServiceNow Integration

1. Go to the [Integrations page](<https://app.escape.tech/integrations-next/>).
2. Open the [ServiceNow](<https://app.escape.tech/integrations-next/servicenow>) card.
3. Click **Delete** next to the integration.

> [!WARNING]
>
> **Consequences of Deleting an Integration**
>
> Deleting the integration stops workflows that export with it, and you can no longer create incidents from those workflows or the Ticketing sidepanel using that connection.
>
> Update or remove those workflows first, or create a replacement integration.

## Using ServiceNow in Workflows

Add an **Export** action and select **ServiceNow**, then pick the integration.

| Trigger | What the export does |
| --- | --- |
| **New Issue** | Creates and links an incident |
| **Manual** | Creates and links an incident (used from the Ticketing sidepanel) |
| **Issue Updated** | Moves the linked incident to the state mapped from the Escape issue status |

### Incident Fields

Each dropdown lists the values of your instance. Fields left empty keep your instance defaults.

- **Category** and **Subcategory**: changing the category clears the subcategory.
- **Assignment group**
- **Caller**: when empty, the user Escape acts as. If Escape can't read that user's `sys_user` record, incidents are created without a caller and the run logs a warning.

### Default Values

Escape writes these fields on every new incident. They're set once at creation and never synced afterwards.

| Incident field | Value |
| --- | --- |
| `short_description` | `[Escape] <issue name>`, cut at 160 characters |
| `description` | Issue context and remediation, then `View in Escape: <link>` |
| `impact`, `urgency` | Derived from the severity, see below |
| `correlation_id` | `escape:<issue id>` |
| `correlation_display` | `Escape` |

ServiceNow has no severity on incidents: it computes priority from impact and urgency. Escape maps severity like this:

| Escape severity | `impact` | `urgency` |
| --- | --- | --- |
| Critical | 1 | 1 |
| High | 1 | 2 |
| Medium | 2 | 2 |
| Low | 3 | 2 |
| Info | 3 | 3 |

With the default priority lookup table, that gives priorities 1 to 5. A field mapping to `impact` or `urgency` replaces these values.

Escape also attaches the issue's evidence to the incident: each HTTP request and response is a JSON file named `exchange-{id}.json`. If an attachment fails, the run logs a warning and the incident is kept.

### Field Mappings

Write Escape properties to any incident field from the instance dictionary, including custom `u_*` fields. These properties support option mapping (one Escape value to one ServiceNow choice): Severity, Category, Status, Asset Class, Asset Tags, Asset Type.

Available Escape properties:

**Issue:** Severity, Created At, Name, Category, Context, Status, Full Remediation, Targets, Link in All Issues, Link in Asset, Link in Scan, Link in Profile

**Profile:** Profile Name, Profile Scanner Kind

**Asset:** Asset Name, Asset URL, Asset Tags, Asset External URL, Asset Type, Asset Class

**Custom rule:** Custom Rule Name, Custom Rule Link

A value that doesn't fit its field is skipped with a warning in the run logs, and the incident is still created.

### Fixed Values

Use fixed values for fields that should have the same value on every incident, whatever the issue. For example, set `contact_type` so every incident shows where it came from, or set a custom `u_*` field your team filters on.

Pick the incident field, then its value. How you enter the value depends on the field:

| Field type | Value input |
| --- | --- |
| Choice field, for example `contact_type` | A dropdown of the field's choices |
| Reference to a user or a group | A search by name |
| Reference to another table | The record's `sys_id`: in ServiceNow, open that table's list, right-click the record, then **Copy sys\_id** |
| Any other field | Free text |

### Status Mapping

The status mapping decides which incident state Escape sets when an issue changes status. It's optional: without it, Escape uses these defaults. You can change any of them, and each dropdown lists the incident states of your instance.

| Escape status | Default incident state |
| --- | --- |
| **Open** | New |
| **Resolved** | Resolved |
| **False positive**, **Ignored** | Canceled |
| **Manual review** | Unchanged |

Escape finds the default states by their label (`New`, `Resolved`, `Canceled`), so if your instance renamed one, map that status yourself.

### Resolution

ServiceNow requires a resolution code and notes to resolve or close an incident. Escape fills them in when it moves an incident to a **Resolved** or **Closed** state, including a new incident created for an issue that's already resolved.

- **Resolution code**: when empty, `Solution provided`.
- **Resolution notes**: when empty, `Resolved in Escape.`

A **Canceled** incident always gets `No resolution provided`.

## Usage

### Creating Incidents From Workflows

When a **New Issue** workflow fires, Escape creates one incident per matching issue, then stores the incident number and link on the Escape ticket.

Creation skips **Ignored** and **False positive** issues. An issue that already has a ticket is skipped, including tickets from other workflows. **Manual** can recreate an incident only when its ticket belongs to that workflow and Escape confirms the incident no longer exists.

Before creating an incident, Escape looks for one with the issue's `correlation_id`. If it finds one, it links it instead of creating a duplicate, so a run that was interrupted can safely run again.

If the issue is already resolved, Escape resolves the new incident right away.

### Creating Incidents Manually

1. Open an issue sidepanel.
2. Open the **Ticketing** tab.
3. Select a workflow whose export action uses ServiceNow.
4. Click **Create Ticket**.

The workflow's fields, mappings, and fixed values are used. The sidepanel then shows the incident number, its live state, and a link to it.

## Bi-Directional Status Sync

### Escape to ServiceNow

Use a workflow with trigger **Issue Updated** and a ServiceNow export.

When the issue changes status, Escape moves the linked incident to the mapped state (see [Status Mapping](<#status-mapping>)), with a resolution code and notes when it resolves, closes, or cancels it (see [Resolution](<#resolution>)). Escape adds a work note with the new status, the date, and a link to the issue. When the incident reopens, it also adds a comment the caller can see.

Reopening a **Closed** or **Canceled** incident needs the [write ACLs](<#allow-reopening-closed-and-canceled-incidents-optional>).

### ServiceNow to Escape

No extra workflow is required.

Escape runs a built-in pull for each ServiceNow integration at least once per day (the scheduler also runs after you create or update the integration). It reads the state of every incident the integration created.

| Incident state | Escape issue |
| --- | --- |
| A state you mapped in **Status mapping** | The status mapped to it |
| **Resolved** or **Closed** | **Resolved** |
| **Canceled** | **Ignored** |
| Any other state | **Open** |

When an issue and its incident both changed since the last sync, the most recent change wins. Status changes pulled from ServiceNow don't trigger **Issue Updated** workflows.

> [!TIP]
>
> **Keep the Incident State Duration Metric Active**
>
> Escape uses ServiceNow's **Incident State Duration** metric to know when each incident changed state, so it can tell which side changed last. The metric is active by default. If you turn it off, Escape guesses from the incident's dates and can pick the wrong side.

## Related

- [Ticketing Integrations](<https://docs.escape.tech/documentation/automate/notifications-ticketing/index.md>)
- [Workflows](<https://docs.escape.tech/documentation/automate/workflows/index.md>)
- [Workflow Actions](<https://docs.escape.tech/documentation/automate/workflows/actions/index.md>)
- [Jira Integration](<https://docs.escape.tech/documentation/automate/notifications-ticketing/jira/index.md>)
- [Azure Boards Integration](<https://docs.escape.tech/documentation/automate/notifications-ticketing/azure-boards/index.md>)
