# ServiceNow

> Connect ServiceNow for Table API reads, file and close one incident ticket per AIOps incident with the ticket preset, and send ServiceNow tickets in as signals.

_How-to · Updated 2026-10-26 · Describes release 0.1.164_

## Overview {#overview}

ServiceNow plays three parts. The read side: the **Incident** and **Change** agents read incidents, change requests and CMDB records through the Table API during last-change checks and correlation; the estate x-ray and the insights chat use the same connection. The ticket side: a ticket route with the `servicenow` preset files one incident in ServiceNow when an AIOps incident opens, writes the analysis back and resolves it when the incident resolves. The inbound side: a business rule can post ServiceNow tickets to the workspace webhook, where a ticket about a service the platform is already alerting on joins that incident.

Keep the three on the right credentials: the agents read with a read-only user, the ticket route writes with an `itil` user on a separate write-role connection the agents never see.

## Setup {#setup}

### Before you start {#before-you-start}

- The `datasources.manage` permission; the built-in owner and operator roles carry it.
- A ServiceNow web-service user for the agents' reads and, for tickets, a second user with the itil role ([Installation](#installation)).

### Installation {#installation}

For the read connection, have the ServiceNow administrator create a **Web service access only** user with read ACLs on the tables the agents need (`incident`, `change_request`, `cmdb_ci`). For the ticket connection, create a second user with the **itil** role and **Web service access only** ticked: itil is enough to file, update and resolve incidents through basic auth to the Table API; only an instance that enforces REST roles also needs `snc_basic_auth_api_access`. Where itil may not query `sys_choice`, **Check** reads the resolution codes from the incident form instead.

A personal developer instance hibernates when idle and answers with a web page until it is woken in the developer portal.

### Configuration {#configuration}

1. Open **Settings · Data in → Data sources** and select **Connect datasource**.
2. Under **Type**, choose **ServiceNow**. Enter a **Connection name**; the connected list shows it, and per-connection agent tools are named after it.
3. Fill the fields; the TLS-skip field sits under **Advanced connection settings**:

| Field (portal label) | API key | Value |
|---|---|---|
| Instance URL (e.g. https://org.service-now.com) | `url` | `https://<instance>.service-now.com`; a pasted browser URL is cut back to the site |
| Integration user (itil role) | `username` | The read-only user for the agents, or the itil user for tickets |
| Password | `password` | Sent as HTTP Basic; stored write-only |
| Skip TLS verify for a self-signed cert (optional — type: true) | `insecure` | Leave blank |

4. Optionally, open **Optional · describe this environment** and say in **What should AIOps know about it?** what this system is and what matters.
5. For the agents' connection, choose **Investigate only** under **Use this connection for** and select **Connect and verify**. For the ticket connection choose **Approved changes only (separate credential)** under **Use this connection for**, leave **Allow use for approved plans** off and save with **Save remediation credential**: a ticket route files and closes tickets and never runs an approved change, so it needs no execution consent. When the agents should also read ServiceNow, that makes two connections.

The read connection through the API (`POST /api/aiops/{tenant_id}/datasources`, requires `datasources.manage`; the password goes in `secret_values`):

```json
{
  "type_key": "servicenow",
  "name": "servicenow-read",
  "description": "ITSM of record; change requests name the service in cmdb_ci.",
  "config": {
    "url": "https://acme.service-now.com",
    "username": "aiops.reader"
  },
  "secret_values": {
    "password": "<password>"
  },
  "role": "read",
  "enabled": true
}
```

The ticket connection is the same body with the itil user, `"role": "write"` and `"name": "servicenow-tickets"`.

### Validation {#validation}

**Connect and verify**, and **Test** on the row later, sends `GET /api/now/table/incident?sysparm_limit=1&sysparm_fields=sys_id,number` with `Accept: application/json`. The verdicts, read from the status and the parsed body only:

| Verdict | Meaning |
|---|---|
| `ok · incident table readable` | The user authenticated and can read incidents. |
| `ok · authenticated; this user cannot read incidents — needed only for tickets (the itil role)` | A read-only user: fine for the agents, not for a ticket route. |
| `failed · HTTP 401: ServiceNow rejected the user name or password.` | Wrong credential. |
| `failed · the instance answered with a web page, not the API: a sleeping developer instance (wake it in the developer portal) or an SSO page` | The URL serves HTML. |
| `failed · HTTP <code>: ServiceNow did not answer the Table API` | Any other status. |

**You should see** a dialog titled `<connection name> is connected` with **Connected — verified from the control plane** and, while no ticket route uses the connection, a **Use for tickets** link; the row's **Last test** later reads `ok · incident table readable`, or the read-only-user verdict above.

On a ticket-capable connection, a green **Test** also runs the saved ticket route's own **Check** and shows its result, or points at setting one up.

### If something fails {#if-something-fails}

The dialog shows **Needs a fix**, what to fix and the check's own detail; the row is saved either way. Select **Edit**, correct the field the detail names and save again, or select **Retry** once a cause outside the form (network, vendor-side permission) is fixed. Every verdict this connector produces, and what to do about it, is in [Troubleshooting](#troubleshooting) below.

## Data collected {#data-collected}

### Tools the agent gets {#tools}

- `<name>_request` — the HTTP tool against the instance URL with HTTP Basic: `method`, `path`, `query`, `body`, `content_type`. Reads are Table API calls such as `GET /api/now/table/change_request?sysparm_query=cmdb_ci.name=checkout&sysparm_limit=20` and `GET /api/now/table/cmdb_ci`.

There is no native ServiceNow tool. Responses are capped at 20,000 characters per call; bound reads with `sysparm_limit` and `sysparm_fields`.

### Read surface {#read-surface}

`GET` and `HEAD` run freely. Every `POST`, `PUT`, `PATCH` and `DELETE` through the request tool is refused in an investigation, never offered for approval: `Investigation connections are read-only. This operation was not executed; prepare an incident change using a write connection and obtain approval.` Ticket writes do not go through the agent at all: the ticket route makes them from the write-role connection. The full rule table is under **The read/write gate in detail** in [Connections and remediation access](/help/read-and-write-roles.md#read-role-vs-write-role).

### Tickets {#tickets}

On the ticket connection's row select **Tickets**, press **Check**, then **Save ticket route** and **Send test ticket**, which files a real incident and resolves it again within seconds. The `servicenow` preset needs no parameter beyond `preset`; **Check** adds `close_code` and, when it finds them, `caller_sys_id`, `assignment_group_sys_id` and `duplicate_close_code`. Priority maps from the incident's P1–P4 to impact and urgency `1/1`, `1/2`, `2/2` and `2/3`. The route files tickets for the **Incident** agent's incidents only. The whole flow, including the API calls, is under [Ticket systems](/help/ticket-systems.md#ticket-systems) in Connections and remediation access.

### Tickets in {#tickets-in}

ServiceNow has no fixed webhook schema; a business rule posts this template to `https://<your-host>/webhook/servicenow/<workspace-slug>` with the token in `X-Webhook-Token`:

```json
{"sys_id": "<sys_id>", "number": "INC0012345",
 "short_description": "…", "description": "…",
 "priority": "3", "state": "2", "severity": "3",
 "cmdb_ci": "payments-api", "url": "https://<instance>.service-now.com/…",
 "assigned_to": "…", "assignment_group": "…", "watch_list": "…",
 "correlation_id": "…", "correlation_display": "…"}
```

A record nested under `record`, with reference fields as `{display_value, value}`, is read the same way. `cmdb_ci` (else `business_service`) becomes the `service` label, so the ticket joins the open incident whose alerts carry that exact value; any other value opens an incident of its own. A ticket with neither field names no service, and one without a severity, a priority, an assignee (`assigned_to` or `assignment_group`) or a `watch_list` lacks a required field: it waits under **Needs you → Triage**, unanalysed, AIOps asks for what is missing in its work notes once, and the business rule's post on **Update** that names everything moves it on ([required fields](/help/ticket-systems.md#required-fields)). `priority` `1` is `critical`, `2` `error`, `3` `warning`, `4` and `5` `info`; `state` `6` or `7` (resolved, closed) resolves the signal. Keep `correlation_id` in the template: the platform marks the tickets it files there (`aiops:<incident id>`), which is how its own tickets are recognised and dropped instead of opening a second incident. Field by field, with the response counters and error strings: [webhook reference](/help/webhook-reference.md#servicenow).

Change requests (`sys_class_name` `change_request…` or a `CHG…` number) are different: one this webhook delivers lands in the change ledger, and under unauthorized-change detection (`aiops_unauthorized_change_incidents`, off by default) it accounts for a captured write only when it arrives with the workspace's ITSM webhook token instead of the shared webhook token. Sent with the shared token, it is recorded and never covers a write, so a workspace with detection on sees the writes its tickets covered flagged until the sender uses the ITSM token ([The ITSM webhook token](/help/changes-reference.md#unauthorized)).

## Troubleshooting {#troubleshooting}

| What you see | Cause | What to do |
|---|---|---|
| `failed · HTTP 401: ServiceNow rejected the user name or password.` | Wrong credential, or a locked user. | Fix it in ServiceNow and rotate the password on **Edit**. |
| `failed · the instance answered with a web page, not the API: …` | A hibernating developer instance, or SSO in front of the API. | Wake the instance, or give the integration user basic auth. |
| `the connection URL must not carry a query or a fragment` / `… must not carry a user name or password` | A pasted URL with extras. | Enter the instance root. |
| `ok · authenticated; this user cannot read incidents — needed only for tickets (the itil role)` on the ticket connection | The ticket user lacks itil. | Grant itil and tick **Web service access only**. |
| **Check** warns it will use *Solution provided* | Neither `sys_choice` nor the incident form metadata could be read. | Accept the default close code, or widen the user's read. |
| The incident page says **Ticket filing failed: …** or **Writing back … failed: …** | A refused request; ServiceNow's own `Operation Failed` text is quoted. | Read the reason, fix the ACL or data policy, press **Retry**. |
| A `429` stalls a filing | ServiceNow rate limit; the platform waits per `Retry-After` up to 30 s. | Nothing; a `5xx` or timeout on a create is not retried in the same turn, and the next attempt searches for the ticket first so nothing is filed twice. |
| A ticket sent in opens its own incident instead of joining | `cmdb_ci` does not equal the alerts' `service` value character for character. | Align the CI name, or map it with `label_map`. |
| A `POST /api/now/table/incident` through the agent answers `Investigation connections are read-only.` | The write gate. | Expected; tickets are the ticket route's job. |

The generic verdict strings are explained in [Test a connection and read the failure](/help/manage-connections.md#troubleshooting). Every connection status word is in the [status and error reference](/help/status-and-error-reference.md#connection-statuses).

## Next steps {#next-steps}

- Select **Discover this connection** in the success dialog to find the services and dependencies behind it: [Launch a scan](/help/estate-harvest-and-xray.md#launch-a-scan).
- File one ServiceNow incident per AIOps incident and close it on resolve: [Tickets](#tickets).
- Send ServiceNow incidents in as signals: [Tickets in](#tickets-in).
- See what else each agent needs in [Choose your connection](/help/choose-your-connection.md#agent-needs).
