# ServiceNow

Sync ServiceNow incidents to status dashboards via inbound webhooks.

Source: https://statusdashboard.com/docs/integrations/servicenow

> This is a plan-gated feature. If it isn't available in your account, visit the [Billing](/docs/org-mgmt/billing) page or contact support to review your options.

ServiceNow can push incident creates and updates to StatusDashboard through a **Business Rule** on the Incident table. When an incident is inserted or updated in ServiceNow, your rule builds a signed JSON payload and POSTs it to the [inbound webhook](/docs/integrations/webhooks/inbound) endpoint. StatusDashboard opens or advances the matching incident on your status dashboards and sends subscriber notifications when configured.

Configure a **Business Rule** on the Incident table in ServiceNow (and any supporting **Script Includes**) using the patterns below. **All mapping lives in ServiceNow**: which CMDB services map to which StatusDashboard components, how ServiceNow **State** maps to workflow phases, and how **Impact** maps to severity labels.

For payload fields, signing, and webhook logs, see [Inbound webhooks](/docs/integrations/webhooks/inbound). For component and workflow configuration, see [Components](/docs/components) and [Workflows](/docs/events/workflows).

***

## How it works
The integration is a one-way push from ServiceNow to StatusDashboard:

1. A ServiceNow **Business Rule** runs **after insert** or **after update** on **Incident** \[`incident`].
2. Your script reads the incident record (state, impact, descriptions, affected services).
3. Your script maps ServiceNow values to StatusDashboard **component IDs**, **severity labels**, and **workflow phase labels** using dictionaries you maintain in the script.
4. Your script signs the JSON body with your inbound webhook signing secret and POSTs to `https://api.statusdashboard.com/public/webhooks/inbound`.
5. StatusDashboard validates the signature, queues the payload, and creates or updates an incident event.

```mermaid
sequenceDiagram
  participant SN as ServiceNow Incident
  participant BR as Business Rule
  participant SD as StatusDashboard Inbound Webhook
  participant SP as Status Dashboard

  SN->>BR: Insert or update
  BR->>BR: Map CMDB services to componentIds
  BR->>BR: Map State and Impact
  BR->>SD: Signed POST (trigger or update)
  SD->>SP: Create or advance incident
```

> Inbound webhooks support 
>   **incidents only**
>   . Maintenance windows and informational notices must be created in the admin console or via the 
>   [REST API](/docs/api)
>   .

## Before you begin
Confirm the following on both sides before writing the Business Rule.

### StatusDashboard
| Requirement                             | Where to configure                                                                                                                          |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| Inbound webhook signing key             | [Inbound webhooks](/app/integrations/webhooks/inbound) — create a key; copy the **Key ID** and **Signing Secret** (secret is shown once)    |
| Components for each affected service    | [Components](/app/components) — note each component **UUID** (via [List components](/docs/api/status-dashboards/components/list) if needed) |
| Severity labels that match your mapping | [Severities](/app/settings/severities) — default labels include **Degraded Performance**, **Partial Outage**, and **Major Outage**          |
| Incident workflow phase labels          | [Workflows](/app/settings/workflows/incident) — default phases include **Investigating**, **Identified**, **Monitoring**, and **Resolved**  |
| Dashboards and component assignments    | [Dashboards](/app/dashboards) — components referenced in webhooks must exist and be valid for the event                                     |

### ServiceNow
| Requirement                         | Notes                                                                                                                                 |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| Incident table access               | Business Rule on **Incident** \[`incident`]                                                                                           |
| Outbound HTTPS                      | `RESTMessageV2` (or equivalent) to `api.statusdashboard.com` on port 443                                                              |
| Credential storage                  | Store Key ID and Signing Secret in a **System Property**, **Credential**, or encrypted script variable — not hard-coded in production |
| Custom incident field (recommended) | String field (for example `u_statusdashboard_event_id`) to store the StatusDashboard event UUID after the first successful trigger    |
| CMDB service identifiers            | `sys_id` values for **Business Service**, **Configuration Item**, and rows in **Impacted Services/CIs** (`task_cmdb_ci_service`)      |

> StatusDashboard does 
>   **not**
>    provide a built-in "External Service ID" mapping UI. Every ServiceNow-to-StatusDashboard translation is defined in your Business Rule (or a Script Include it calls). Plan to maintain those dictionaries when services or labels change.

## Step 1 — Create an inbound webhook signing key
1. Open [Inbound webhooks](/app/integrations/webhooks/inbound) in the admin console.
2. Click **Create key** and copy the **Key ID** (for example `wk_a1b2c3d4e5f6a7b8`) and **Signing Secret**.
3. Store both values in ServiceNow where your integration script can read them.

The signing secret cannot be retrieved after the creation dialog closes. If it is lost, revoke the old key and create a new one.

See [Authentication](/docs/integrations/webhooks/inbound#authentication) for the HMAC-SHA256 header format (`X-SD-Key-Id`, `X-SD-Signature`).

## Step 2 — Collect StatusDashboard component IDs
Each incident webhook must include at least one StatusDashboard **component UUID** in `componentIds`.

1. List your organization's components with `GET /app/components` (see [List components](/docs/api/status-dashboards/components/list)), or export IDs during initial setup.
2. Record the UUID next to each ServiceNow service you intend to map.

Example mapping table (maintain this in your ServiceNow script or a related list):

| ServiceNow source            | ServiceNow `sys_id` | StatusDashboard component | Component UUID                         |
| ---------------------------- | ------------------- | ------------------------- | -------------------------------------- |
| Business service on incident | `abc123…`           | API                       | `a1b2c3d4-e5f6-7890-abcd-ef1234567890` |
| Impacted CI (CMDB)           | `def456…`           | Payment Processing        | `b2c3d4e5-f6a7-8901-bcde-f12345678901` |

### Finding ServiceNow service `sys_id` values
| ServiceNow object                | How to locate the `sys_id`                                                                     |
| -------------------------------- | ---------------------------------------------------------------------------------------------- |
| Business service on the incident | Field **Business service** on the incident form                                                |
| Configuration items              | Filter navigator: `cmdb_ci.list` — open the CI, use **Copy sys\_id** from the form header menu |
| Impacted Services/CIs            | Related list **Impacted Services/CIs** on the incident — each row links to a CMDB service CI   |

Your script should collect **all** relevant `sys_id` values (primary business service plus impacted services), deduplicate them, map each to a StatusDashboard component UUID, and send the resulting UUID array as `componentIds` (1–20 IDs per request).

If no mapped component is found, skip the webhook or log an error. StatusDashboard rejects triggers with unknown component IDs.

## Step 3 — Define mapping dictionaries in ServiceNow
Add JavaScript objects at the top of your Business Rule (or in a Script Include) for the mappings below. Keys must match **display values** ServiceNow returns from `getDisplayValue()` unless you standardize on internal values.

### State → workflow phase (`statusLabel`)
Map ServiceNow **State** to the **exact** incident workflow phase **label** configured in StatusDashboard (case-sensitive).

Default StatusDashboard incident phases:

| ServiceNow State (example) | StatusDashboard `statusLabel` |
| -------------------------- | ----------------------------- |
| New                        | Investigating                 |
| In Progress                | Identified                    |
| On Hold                    | Identified                    |
| Resolved                   | Resolved                      |
| Closed                     | Resolved                      |
| Cancelled                  | Resolved                      |

Customize the dictionary to match your ServiceNow state list and your organization's workflow labels on [Workflows](/app/settings/workflows/incident).

### Impact → severity (`severity`)
Map ServiceNow **Impact** display values to StatusDashboard **severity labels** (also case-sensitive).

Default StatusDashboard severities:

| ServiceNow Impact (example) | StatusDashboard `severity` |
| --------------------------- | -------------------------- |
| 1 - High                    | Major Outage               |
| 2 - Medium                  | Partial Outage             |
| 3 - Low                     | Degraded Performance       |

If you renamed severities in Settings, update the dictionary to match. You may omit severity on updates; severity is required on **trigger** only.

### Description assembly
Decide how to build the StatusDashboard `description` from ServiceNow fields:

| Option                 | Behavior                                                                               |
| ---------------------- | -------------------------------------------------------------------------------------- |
| Short description only | Use **Short description**                                                              |
| Short + long           | Concatenate **Short description** and **Description** when the long field is populated |

Use **Short description** for the webhook `title` on trigger (max 250 characters).

## Step 4 — Track the StatusDashboard event ID
Your Business Rule tracks which StatusDashboard event corresponds to each ServiceNow incident. Store the StatusDashboard event UUID on the incident record and reference it on every update.

**Recommended pattern:**

1. Add a custom string field on Incident (for example `u_statusdashboard_event_id`) to store the StatusDashboard event UUID.
2. On **insert**, send `action: "trigger"` with `idempotencyKey` set to the incident `sys_id` (letters, numbers, dots, underscores, and hyphens only; max 128 characters).
3. After the first successful trigger, obtain the assigned **event ID** and write it to your custom field. Practical options:
   * **Webhook logs API:** Call `GET /app/integrations/webhooks/inbound/logs` (see [List inbound webhook logs](/docs/api/integrations/webhooks/inbound/logs/list)) and match the newest `processed` entry whose payload contains your `idempotencyKey`. Use the returned `eventId`.
   * **Manual (pilot):** Copy the **Event ID** from the matching row on [Inbound webhooks](/app/integrations/webhooks/inbound) webhook logs during testing.
4. On **update**, read `u_statusdashboard_event_id`. If populated, send `action: "update"`. If empty, treat the next operation as a trigger (with the same `idempotencyKey`) or resolve the ID from logs before updating.

> `idempotencyKey`
>    prevents duplicate incidents when ServiceNow retries the same trigger. It is remembered for 
>   **3 days**
>   . It does 
>   **not**
>    replace storing 
>   `eventId`
>    for update actions — updates require the UUID explicitly.

Optionally set a private webhook attribute on trigger so support staff can correlate records:

```json
"attributes": [
  { "key": "servicenow_incident_id", "value": "<incident_sys_id>", "isPublic": false }
]
```

## Step 5 — Create the Business Rule
Create a Business Rule in ServiceNow with these settings:

| Setting                          | Value                                                                                                                        |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **Name**                         | StatusDashboard Inbound Webhook (or your standard)                                                                           |
| **Table**                        | Incident \[`incident`]                                                                                                       |
| **Advanced**                     | Checked                                                                                                                      |
| **When**                         | `after`                                                                                                                      |
| **Insert**                       | Checked                                                                                                                      |
| **Update**                       | Checked                                                                                                                      |
| **Filter conditions** (optional) | Limit to incidents that affect published services, specific assignment groups, or categories you expose on status dashboards |

### Script structure (outline)
Implement the Advanced script using standard ServiceNow APIs. A typical flow:

1. **Load configuration** — endpoint URL, Key ID, Signing Secret, mapping dictionaries, notification flags.
2. **Build `componentIds`** — query **Business service**, **Impacted Services/CIs** (`task_cmdb_ci_service`), map each `sys_id` through your component dictionary; exit if the array is empty.
3. **Choose `action`:**
   * **`insert`** → `trigger`
   * **`update`** with stored `eventId` → `update`
   * **`update`** without stored `eventId` → `trigger` with `idempotencyKey` (or resolve ID first)
4. **Build JSON payload** — see [Payload reference](#payload-reference) below.
5. **Sign the body** — compute HMAC-SHA256 over `"<unix_seconds>.<raw_json_body>"` with the Signing Secret; set headers `Content-Type`, `X-SD-Key-Id`, and `X-SD-Signature: t=<seconds>,v1=<hex>`.
6. **Send** — `RESTMessageV2` POST to `https://api.statusdashboard.com/public/webhooks/inbound`.
7. **Persist `eventId`** — on first successful processing, update `u_statusdashboard_event_id` (via logs API poll or your chosen method).
8. **Log** — use `gs.info` / `gs.error` behind a debug flag for troubleshooting.

Consider moving signing and HTTP into a **Script Include** (for example `StatusDashboardWebhookClient`) so the Business Rule stays readable and you can unit-test mappings separately.

## Payload reference
All requests POST JSON to the inbound webhook endpoint. See [Payload reference](/docs/integrations/webhooks/inbound#payload-reference) for full field definitions.

### Trigger — new incident (`action: "trigger"`)
Send on ServiceNow **insert**, or on **update** when no StatusDashboard `eventId` is stored yet.

```json
{
  "action": "trigger",
  "title": "Elevated API error rate",
  "description": "Customers may see timeouts on login. Engineering is engaged.",
  "severity": "Major Outage",
  "componentIds": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"],
  "statusLabel": "Investigating",
  "initialMessage": "Incident opened from ServiceNow.",
  "idempotencyKey": "a1b2c3d4e5f6789012345678901234ab",
  "notifications": true,
  "attributes": [
    { "key": "servicenow_incident_id", "value": "a1b2c3d4e5f6789012345678901234ab", "isPublic": false }
  ]
}
```

| Field            | ServiceNow source (typical)                                                                           |
| ---------------- | ----------------------------------------------------------------------------------------------------- |
| `title`          | **Short description**                                                                                 |
| `description`    | **Short description** and optionally **Description**                                                  |
| `severity`       | **Impact** via your severity dictionary                                                               |
| `componentIds`   | Mapped UUIDs from affected CMDB services                                                              |
| `statusLabel`    | **State** via your state dictionary                                                                   |
| `idempotencyKey` | Incident `sys_id`                                                                                     |
| `notifications`  | Set `false` when you need a silent create (see [Notification suppression](#notification-suppression)) |

### Update — advance an existing incident (`action: "update"`)
Send on ServiceNow **update** when `u_statusdashboard_event_id` is populated.

```json
{
  "action": "update",
  "eventId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
  "statusLabel": "Identified",
  "message": "Root cause identified. Fix rolling out to all regions.",
  "notifications": true
}
```

| Scenario                                | Suggested `statusLabel`                                | Suggested `message`                                                                   |
| --------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------- |
| **State** changed (no customer comment) | Mapped new state                                       | Short description change, or a fixed phrase such as "Incident updated in ServiceNow." |
| **Customer visible comment** posted     | Current workflow phase (mapped from current **State**) | Text of the customer-visible comment (strip journal headers)                          |
| **Resolved / Closed**                   | Resolved (or your final phase label)                   | Resolution notes or **Close notes** when appropriate                                  |

> Inbound webhook 
>   **update**
>    actions advance the workflow timeline and phase. They do 
>   **not**
>    change the event 
>   **title**
>    or 
>   **description**
>    after creation. To edit those fields later, use 
>   `PATCH /app/events/{id}`
>    in the 
>   [REST API](/docs/api/event-management/events/update)
>    or update the event in the admin console.

> Inbound webhooks 
>   **cannot reopen**
>    a resolved incident to an earlier workflow phase. Once the event reaches a final phase, further updates must stay on that phase or be handled manually in StatusDashboard.

## Customer-visible comments
ServiceNow **Customer visible** comments map to StatusDashboard **timeline** entries.

When `current.comments.changes()` on an update:

1. Read the latest customer-visible journal entry (`comments.getJournalEntry(1)`).
2. Strip the journal header (text before the first newline) and trailing blank lines.
3. Send `action: "update"` with `message` set to the cleaned comment text and `statusLabel` set to the phase mapped from the incident's current **State**.

If the user edits **State**, **Impact**, or descriptions **and** posts a customer-visible comment in one save, prefer **one** webhook that includes the comment as the timeline message and the updated `statusLabel`. Avoid posting the comment first and saving field changes separately, which can produce two StatusDashboard timeline entries and duplicate notifications.

## Notification suppression
To suppress subscriber notifications for a specific webhook while still creating or updating the incident:

| Method                                                 | Behavior                                                                                                                                                                                          |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `"notifications": false` in the JSON payload           | Disables notifications for that request (and can disable future notifications on update when set on an update action)                                                                             |
| Magic string in **Short description** (custom pattern) | Remove a configured substring (for example `{-}`) from the description before sending, and set `"notifications": false` when the substring was present — useful for draft incidents in ServiceNow |

Inbound webhook notification defaults and segment targeting are documented on [Inbound webhooks](/docs/integrations/webhooks/inbound#payload-reference).

## What triggers the Business Rule
Actions in ServiceNow that should send a webhook:

| ServiceNow action                                  | StatusDashboard action                                                                         |
| -------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| New incident inserted                              | `trigger`                                                                                      |
| **State**, **Impact**, or affected services change | `update` (when `eventId` is known)                                                             |
| **Short description** or **Description** change    | No description sync via webhook — use timeline `message` to communicate, or PATCH via REST API |
| Customer-visible comment posted                    | `update` with comment as `message`                                                             |

Skip or filter incidents that should never appear on public status dashboards (internal-only categories, test assignment groups, and so on) using Business Rule conditions.

## Testing and troubleshooting
1. Enable debug logging in your script and watch **System Logs** in ServiceNow.
2. Send a test incident with one mapped component and a known **State** / **Impact**.
3. Open [Inbound webhooks](/app/integrations/webhooks/inbound) and review **Webhook logs**:
   * `processed` — incident created or updated; note the **Event ID**
   * `processing_failed` — check **Reason** (invalid severity label, unknown component ID, invalid workflow phase, plan limits)
   * `validation_failed` — malformed JSON or missing required fields
4. Remember that authentication failures return `200 { "ok": true }` by design. If nothing appears in logs, verify **Key ID**, **Signing Secret**, and the signature timestamp (must be within 5 minutes).

| Symptom                                                | Likely cause                                                                                  |
| ------------------------------------------------------ | --------------------------------------------------------------------------------------------- |
| Log shows `processing_failed` — invalid workflow phase | `statusLabel` does not exactly match a label on [Workflows](/app/settings/workflows/incident) |
| Log shows `processing_failed` — severity               | `severity` does not match a label on [Severities](/app/settings/severities)                   |
| Log shows `processing_failed` — component              | UUID wrong or component deleted                                                               |
| Duplicate incidents                                    | Missing or inconsistent `idempotencyKey` on trigger                                           |
| Updates ignored                                        | `eventId` not stored on the ServiceNow incident                                               |
| Two notifications for one edit                         | Comment posted and fields saved as separate ServiceNow transactions                           |
