---
title: 'ServiceNow'
description: 'Create and update ServiceNow incident records via inbound REST, keyed by correlation_id = Yorker incident ID.'
section: 'Integrations'
canonical_url: 'https://yorkermonitoring.com/docs/integrations/servicenow'
---

# ServiceNow

Yorker integrates with ServiceNow via inbound REST against the standard `incident` table. The first `opened` event creates a new incident record; subsequent lifecycle events update that record via `correlation_id` lookup — no `sys_id` round-trip required.

For the underlying model (lifecycle states, event types, scoped hypothesis), see [Incidents](/docs/concepts/incidents).

## Set up

Yorker needs an inbound REST credential with permission to create and update `incident` records.

1. In ServiceNow, create a service account (or re-use an existing integration user) with the **Incident** table permissions needed to `POST` and `PATCH`.
2. Note your instance URL (e.g. `https://acme.service-now.com`) and optionally the assignment group new incidents should route to.
3. Create the channel in Yorker:

```bash
curl -X POST https://yorkermonitoring.com/api/notification-channels \
  -H "Authorization: Bearer $YORKER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "snow-prod",
    "channel": {
      "type": "servicenow",
      "instanceUrl": "https://acme.service-now.com",
      "username": "yorker_integration",
      "password": "…",
      "assignmentGroup": "Network Operations"
    }
  }'
```

| Field              | Required | Description                                                           |
| ------------------ | -------- | --------------------------------------------------------------------- |
| `instanceUrl`      | yes      | Base URL of the ServiceNow instance                                   |
| `username`         | yes      | Integration user                                                      |
| `password`         | yes      | Password or OAuth token (stored at rest; treated as secret)           |
| `assignmentGroup`  | no       | Group name — maps to `assignment_group` on new incidents              |

Authentication is HTTP Basic over TLS.

## Event mapping

| Yorker event      | Action              | Notes                                                          |
| ----------------- | ------------------- | -------------------------------------------------------------- |
| `opened`          | `POST /incident`    | Creates record with `correlation_id = incident_id`             |
| `severity_changed`| `PATCH` record      | Updates `impact`, `urgency`, adds `work_notes`                 |
| `acknowledged`    | `PATCH` record      | Sets `state = 2` (Work in Progress), adds ack note             |
| `note_added`      | `PATCH` record      | Appends to `work_notes`                                         |
| `auto_resolved`   | `PATCH` record      | Sets `state = 6` (Resolved), fills `close_code` + `close_notes` |
| `closed`          | `PATCH` record      | Sets `state = 6` (Resolved), uses Yorker `closeReason`          |
| `reopened`        | *(skipped)*         | Yorker reopens create a new external ticket, not mutate the old|
| `alert_attached`  | *(skipped)*         | Internal-only                                                  |

Updates target `PATCH /api/now/table/incident?sysparm_query=correlation_id=<incident_id>` — no `sys_id` is stored.

### Field mapping (on `opened`)

| ServiceNow field       | Source                                                          |
| ---------------------- | --------------------------------------------------------------- |
| `short_description`    | `payload.hypothesis.summary` (stripped of CR/LF, capped at 160) |
| `description`          | Hypothesis summary + observations summary + triage URL          |
| `work_notes`           | `scope: external_symptoms_only` + ruled-out list                 |
| `impact`, `urgency`    | `critical → 1`, `warning → 2`, `info → 3`                       |
| `correlation_id`       | Yorker `incident_id`                                            |
| `correlation_display`  | `"Yorker Incident"`                                             |
| `assignment_group`     | Channel config (if set)                                         |

### Severity mapping

| Yorker severity | ServiceNow `impact` | ServiceNow `urgency` |
| --------------- | ------------------- | -------------------- |
| `critical`      | 1 (High)            | 1 (High)             |
| `warning`       | 2 (Medium)          | 2 (Medium)           |
| `info`          | 3 (Low)             | 3 (Low)              |

## Why reopened isn't routed

ServiceNow's "Reopen" action reverts a closed incident to an active state on the **same** record. Yorker's recurrence model treats a new burst of correlated alerts after auto-resolution as a **new incident** with a pointer back to the prior one (`recurrenceOf`). Those two semantics don't map cleanly, so Yorker creates a new ServiceNow record on recurrence (via the next `opened` event) rather than mutating the closed one. Use the **View in Yorker** link on each record to pivot between related incidents.

## Template overrides

Three fields are user-editable: `short_description`, `description`, and `work_notes`. The envelope (`state`, `impact`, `urgency`, `correlation_id`, `assignment_group`) stays under Yorker's control so the lookup pattern and severity mapping remain consistent.

> **UI editor note:** the in-app per-channel template editor (at **Settings > Notification Channels > Templates**) currently supports Slack, email, and webhook channels only. ServiceNow overrides are authored through the API below; a UI editor for ServiceNow is planned for a future release.

```bash
curl -X PUT https://yorkermonitoring.com/api/notification-channels/nch_abc \
  -H "Authorization: Bearer $YORKER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "incidentTemplate": {
      "channelType": "servicenow",
      "overrides": {
        "opened": {
          "shortDescription": "[{{upperCase incident.severity}}] {{incident.title}}",
          "description": "Yorker incident {{incident.incidentId}}\n\nHypothesis: {{payload.hypothesis.summary}}\n\nTriage: {{incident.triageUrl}}",
          "workNotes": "Scope: {{payload.hypothesis.scope}}\nRuled out:\n{{#each payload.hypothesis.ruledOut}}  - {{this}}\n{{/each}}"
        }
      }
    }
  }'
```

Template render failures fall back to the default string and log a warning. Dispatch never fails on a bad template.

Helpers and render context are the same as the [Slack integration](/docs/integrations/slack).

`short_description` is always stripped of CR/LF (header injection guard) and capped at 160 characters after render.

## Disabling

The ServiceNow channel type is **incident-pipeline-only** — it has no legacy per-alert dispatch path. `incidentSubscribed` is locked to `true` for ServiceNow channels; the API rejects create/update requests that set it to `false`. To stop routing incidents to a ServiceNow channel, delete it or remove it from the alert rule's channel list.
