> ## Documentation Index
> Fetch the complete documentation index at: https://docs.neoagent.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Architecture

> How Neo Agent receives real-time ticket events from ServiceNow

ServiceNow does not have native webhook support the way ConnectWise, Autotask, or Halo do. Instead of asking you to wire one up by hand, Neo Agent provisions a **Business Rule** plus a **System Property** in your ServiceNow instance the first time you connect — these together emulate a webhook.

<Info>
  You don't need to configure anything in ServiceNow yourself. Both artifacts are created, kept up to date, and removed automatically when you connect or disconnect Neo Agent in the dashboard.
</Info>

## What Neo Provisions

When you connect ServiceNow, Neo creates two records in your instance:

| Artifact        | Table            | Name                       | Purpose                                                                                                                                                                                                                                                                                 |
| --------------- | ---------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| System Property | `sys_properties` | `x_neoagent.callback_url`  | Stores the Neo Agent callback URL. The Business Rule reads this at runtime so the URL can be updated without editing the script.                                                                                                                                                        |
| Business Rule   | `sys_script`     | `Neo Agent — Task Webhook` | Fires `after` insert and update on the parent `task` table. POSTs the changed record to the callback URL above. Because every task-family subclass (incident, sc\_request, sc\_req\_item, sc\_task, change\_request, problem) inherits from `task`, a single rule captures all of them. |

Both records are tagged in their description as **auto-managed; do not edit**. If you do edit them, Neo will overwrite your changes the next time it re-provisions.

<Info>
  If your instance was connected before mid-May 2026, you may also have a legacy `Neo Agent — Incident Webhook` rule on the `incident` table. Neo's re-provisioning step deletes it automatically on the next reconnect; if you've never reconnected, you can safely delete it yourself — the new task-table rule supersedes it.
</Info>

## The Webhook Payload

Each insert or update on any task-family record POSTs a JSON body shaped like this:

```json theme={null}
{
  "sys_id": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
  "number": "INC0010001",
  "sys_mod_count": "7",
  "table": "incident",
  "action": "insert",
  "current": {
    "short_description": "Printer is offline",
    "description": "User reports the 3rd-floor MFP is unresponsive.",
    "state": "1",
    "priority": "3",
    "urgency": "2",
    "impact": "3",
    "assignment_group": "<sys_id>",
    "assigned_to": "<sys_id>",
    "caller_id": "<sys_id>",
    "company": "<sys_id>",
    "category": "hardware",
    "sys_updated_on": "2026-04-29 14:30:00",
    "sys_created_on": "2026-04-29 14:30:00"
  },
  "previous": {
    "state": "1",
    "priority": "2",
    "assignment_group": "<sys_id>",
    "assigned_to": "<sys_id>"
  }
}
```

* `table` reflects the row's actual `sys_class_name` — one of `incident`, `sc_request`, `sc_req_item`, `sc_task`, `change_request`, or `problem`. Workflow triggers can filter on this via the **Ticket Type** rule in the rules-builder.
* `number` carries the corresponding human-friendly prefix: `INC*`, `REQ*`, `RITM*`, `TASK*`, `CHG*`, or `PRB*`.
* `sys_mod_count` is ServiceNow's monotonic per-record version counter. Neo uses it to tiebreak the rare case where two updates land on the same record inside the same wall-clock second — without it the two callbacks would collide and one diff would be lost. Older Business Rule revisions (pre-v2) don't emit this field; Neo accepts both shapes.
* `action` is `"insert"` for newly created rows and `"update"` for any field change.
* `previous` is only included on updates and reflects the values immediately before the change.
* All reference fields (`assignment_group`, `caller_id`, `company`, etc.) are sent as ServiceNow `sys_id`s — Neo resolves them on the receiving side.
* Subclass-specific fields (`urgency`, `impact`, `caller_id`, `category`) only exist on `incident`; on other subclasses they come back as empty strings — Neo treats those as `null` and ignores them in workflow matching.

## Locating the Artifacts in ServiceNow

If you ever need to inspect or temporarily disable the integration from the ServiceNow side:

<Steps>
  <Step title="Find the System Property">
    Navigate to **System Properties → All Properties** (or browse to `sys_properties.list`) and filter by `Name` `is` `x_neoagent.callback_url`. The **Value** column shows the exact URL Neo Agent will be POSTed to.
  </Step>

  <Step title="Find the Business Rule">
    Navigate to **System Definition → Business Rules** (or browse to `sys_script.list`) and filter by `Name` `is` `Neo Agent — Task Webhook`. The rule should be **Active**, run **after** insert and update on the `task` table.
  </Step>

  <Step title="Disable temporarily (optional)">
    To pause webhook delivery without disconnecting Neo Agent, untick **Active** on the Business Rule. Re-tick it to resume.

    <Warning>
      Disabling the Business Rule stops all event delivery to Neo Agent. Any automation that depends on ticket events — across every task-family subclass, not just incidents — will idle until it is re-enabled.
    </Warning>
  </Step>
</Steps>

## Delivery Semantics — Best-Effort

The Business Rule POSTs the webhook payload using ServiceNow's `RESTMessageV2.executeAsync()` so the user save in ServiceNow doesn't block on Neo's response. This is by design — without it, every save would wait for Neo's Azure Functions to reply, which would add visible latency to technician edits. The trade-off:

* **No automatic retries on Neo's side.** If Neo's endpoint returns 4xx/5xx, is restarting, or is temporarily unreachable, ServiceNow does not retry the delivery. The event is lost from the Business Rule's perspective.
* **No 4xx/5xx body capture in the Business Rule's logs.** `executeAsync` doesn't expose the response synchronously, so `[NeoAgent]` syslog entries cover errors raised during the script itself (missing System Property, exception inside the rule) but not Neo-side rejections.

For comparison: ConnectWise, Autotask, and Halo all retry failed deliveries on their side. ServiceNow does not, so the loss window is exactly the duration of any Neo-side outage.

In practice, callbacks landing during a Neo outage are rare and have a self-correcting fallback: the next legitimate update on the same record sends a fresh callback that carries the current state. If you suspect a missed event, edit the affected record (e.g. add a one-character work note, then revert) to force a new callback.

## Verifying Deliveries

ServiceNow logs every Business Rule execution. To confirm webhooks are firing:

1. Navigate to **System Logs → All** (or browse to `syslog.list`).
2. Filter by `Message` contains `[NeoAgent]` to see entries written by the rule. These cover rule-side errors only (missing callback URL, exceptions inside the script). Successful deliveries and Neo-side response codes are not logged here because `executeAsync` is fire-and-forget — see the **Delivery Semantics** section above.
3. Update any task-family record — change priority, state, or assignment on an incident, request item, change request, etc.
4. If nothing is logged and Neo doesn't see the event, check the **REST Outbound** logs at `sys_outbound_rest_message_log.list` for the most recent POST to the Neo callback URL. The response status is captured by SN's outbound logging even though the Business Rule itself doesn't inspect it.

## Troubleshooting

<AccordionGroup>
  <Accordion title="No events reach Neo Agent">
    * Confirm the **Active** flag on the Business Rule is set to `true`.
    * Confirm the System Property value is non-empty and points at a `https://...neoagent.io/callback/servicenow?...` URL.
    * Check `sys_outbound_rest_message_log` for a recent attempt — if the POST is failing with a `401` or `403`, the Azure Functions function key in `?code=` is stale. Disconnect and reconnect ServiceNow in the Neo dashboard to refresh it.
  </Accordion>

  <Accordion title="OAuth refresh failed">
    ServiceNow refresh tokens are long-lived (default 100 days) but can be revoked when the OAuth Application Registry is regenerated or the user who consented loses admin rights. If Neo reports an authentication failure in the inbox, the fix is to reconnect ServiceNow from the Neo dashboard — this re-runs the OAuth Authorization Code flow and captures a fresh refresh token.
  </Accordion>

  <Accordion title="Webhook fires too often">
    The Business Rule fires on every update, including system-driven updates from workflows or other Business Rules. If you only want to react to specific changes, raise a ticket with us — Neo can filter at the receive side, but we may also be able to tighten the rule's `condition` field for your instance.
  </Accordion>

  <Accordion title="Disconnect leaves orphaned artifacts">
    Disconnecting ServiceNow in the Neo dashboard runs an unprovision step that deletes both the System Property and the Business Rule. If you can still see them after disconnecting, the unprovision call failed mid-flight — open the artifacts and delete them manually, or reconnect and disconnect once more.
  </Accordion>
</AccordionGroup>

## Related Pages

* [ServiceNow Overview](/integrations/psa/servicenow/overview)
