---
title: Change Handlers
slug: change-handlers
docTags: 
createdAt: 2025-10-29T21:35:03.848Z
---

Change Handlers consume pending changes detected by processImport and decide what to do (submit jobs, create roles, update users, log tickets, etc.). You select a handler by setting the process/provider in the workflow.

# Shared Concepts

**Where they run**
&#x20;After processImport, each row in the **data** table with a pending action (Create/Update/Delete) is passed to the handler.

**Common options**

:::BlockQuote
\<process>
&#x20; \<provider>Innovation.Activate.Library\Innovation.Activate.Connectors.UserChangeHandler\</provider>
&#x20; \<options>
&#x20;   \<maxChanges>0\</maxChanges> \<!-- 0 = no limit -->
&#x20;   \<batchSize>500\</batchSize>
&#x20;   \<retry>
&#x20;     \<timeout>+2days\</timeout> \<!-- how long to keep retrying -->
&#x20;     \<clear>false\</clear>       \<!-- if true, clear error flag after retries -->
&#x20;   \</retry>
&#x20; \</options>
&#x20; \<!-- handler-specific config follows -->
\</process>
:::

**Common events** (inside an \<events> block)

- create, update, move, delete, enable, disable&#x20;
- Steps you can use: submit, execute, notify, end, retry, error, set, ticket, publish, unpublish&#x20;

# UserChangeHandler

Use when connector data represents **people** that should link to AD users and drive identity lifecycle automation.

**Minimum config**

:::BlockQuote
\<process>
&#x20; \<provider>Innovation.Activate.Library\Innovation.Activate.Connectors.UserChangeHandler\</provider>
&#x20; \<user>
&#x20;   \<link name="User Account">
&#x20;     \<scope>=//Roles/System Roles/Users\</scope>
&#x20;     \<match when="employeeNumber">(employeeNumber=%=//Current/employeeNumber%)\</match>
&#x20;     \<match when="domainName">(samAccountName=%=//Current/domainName%)\</match>
&#x20;     \<match>(givenName=%=//Current/firstName%)(sn=%=//Current/lastName%)\</match>
&#x20;   \</link>
&#x20;   \<!-- Optional: raise enable/disable based on an expression -->
&#x20;   \<enabled>=/isActive\</enabled>
&#x20; \</user>
&#x20; \<links>
&#x20;   \<manager>
&#x20;     \<scope>=//Roles/System Roles/Users\</scope>
&#x20;     \<match when="managerDomainName">(samAccountName=%=//Current/managerDomainName%)\</match>
&#x20;   \</manager>
&#x20; \</links>
&#x20; \<events>
&#x20;   \<create>
&#x20;     \<submit>
&#x20;       \<task>=//Tasks/User/Create User\</task>
&#x20;       \<arguments>
&#x20;         \<user>
&#x20;           \<firstName>%=//Current/firstName%\</firstName>
&#x20;           \<lastName>%=//Current/lastName%\</lastName>
&#x20;           \<manager>=//Links/manager\</manager>
&#x20;         \</user>
&#x20;       \</arguments>
&#x20;     \</submit>
&#x20;   \</create>
&#x20; \</events>
\</process>
:::

**Globals provided**

- User — matched AD user (if found)&#x20;
- Links — collection of additional linked users (e.g., manager)&#x20;
- RoleLinks — roles resolved by role link handlers&#x20;

# RoleChangeHandler

Use when connector data represents a **hierarchy** you want as Activate **Roles** (e.g., departments, locations).

**Minimum config**

:::BlockQuote
\<process>
&#x20; \<provider>Innovation.Activate.Library\Innovation.Activate.Connectors.RoleChangeHandler\</provider>
&#x20; \<role>
&#x20;   \<root>=//Roles/Departments\</root>
&#x20;   \<key>DepartmentCode\</key>
&#x20;   \<field>departmentCode\</field>
&#x20;   \<name>=//Current/department\</name>
&#x20;   \<!-- Choose one parent strategy: field reference or explicit link -->
&#x20;   \<parent>parentCode\</parent> \<!-- field reference model -->
&#x20;   \<parameters>
&#x20;     \<departmentCode name="DepartmentCode" type="String">=/departmentCode\</departmentCode>
&#x20;   \</parameters>
&#x20;   \<events>
&#x20;     \<missing>
&#x20;       \<createRole>
&#x20;         \<description>Created by import %=//System/Now%\</description>
&#x20;       \</createRole>
&#x20;     \</missing>
&#x20;   \</events>
&#x20; \</role>
\</process>
:::

**Events raised by handler**: create, update, delete (plus createRole step for new roles)

# GenericChangeHandler

Use when you need event-driven actions (submit jobs, run scripts) without built-in user/role semantics.

**Minimal skeleton**

:::BlockQuote
\<process>
&#x20; \<provider>Innovation.Activate.Library\Innovation.Activate.Connectors.GenericChangeHandler\</provider>
&#x20; \<links>
&#x20;   \<!-- optional AD lookups you want available during events -->
&#x20; \</links>
&#x20; \<events>
&#x20;   \<create>
&#x20;     \<submit>
&#x20;       \<task>=//Tasks/Custom/Create Thing\</task>
&#x20;       \<arguments>
&#x20;         \<data>
&#x20;           \<externalId>=/id\</externalId>
&#x20;         \</data>
&#x20;       \</arguments>
&#x20;     \</submit>
&#x20;   \</create>
&#x20; \</events>
\</process>
:::

**Globals**: Links (if configured)

# ScriptChangeHandler

Use when change processing logic itself must be scripted (complex business rules, multi-step orchestration).

**Skeleton**

:::BlockQuote
\<process>
&#x20; \<provider>Innovation.Activate.Library\Innovation.Activate.Connectors.ScriptChangeHandler\</provider>
&#x20; \<script>=/ChangeProcessor\</script>
&#x20; \<options>
&#x20;   \<batchSize>200\</batchSize>
&#x20; \</options>
\</process>
:::

# ServiceDeskTicketChangeHandler

Use when connector data represents **tickets** you want to create/update inside Activate.

**Minimum config**

:::BlockQuote
\<process>
&#x20; \<provider>Innovation.Activate.Library\Innovation.Activate.Connectors.ServiceDeskTicketChangeHandler\</provider>
&#x20; \<user>
&#x20;   \<!-- optional user link if tickets should be assigned to people -->
&#x20; \</user>
&#x20; \<ticket>
&#x20;   \<!-- define how to match to existing tickets -->
&#x20;   \<match field="ticketNumber">=/ticketNumber\</match>
&#x20;   \<match field="JobID">=/jobId\</match>
&#x20; \</ticket>
&#x20; \<events>
&#x20;   \<create>
&#x20;     \<ticket>
&#x20;       \<ticketNumber>=/ticketNumber\</ticketNumber>
&#x20;       \<name>=/name\</name>
&#x20;       \<state>=if(=/state, "Closed", "Open")\</state>
&#x20;       \<status>=/status\</status>
&#x20;       \<user>=//User\</user>
&#x20;       \<description>=/details\</description>
&#x20;       \<notes>=/notes\</notes>
&#x20;     \</ticket>
&#x20;   \</create>
&#x20; \</events>
\</process>
:::

**Globals**: User (if user link configured), Ticket (when matched)

# Event Steps (Quick Reference)

- **submit** — Submit a job/task with arguments.&#x20;
- **execute** — Run a script (inline reference).&#x20;
- **notify** — Send an email/notification (supports when=errors).&#x20;
- **end** — Mark successful end of the current event.&#x20;
- **retry** — Keep change pending to retry later.&#x20;
- **error** — Mark row as error; respects retry policy.&#x20;
- **set** — Set parameter values without a script.&#x20;
- **ticket** — Create/update an Activate ticket.&#x20;
- **publish / unpublish** — Toggle publication state for provisioning objects.&#x20;

# Troubleshooting & Hardening

- Use options/retry/timeout to bound retry loops; alert on exceeded retries.&#x20;
- Emit **meaningful messages** in error so operators can triage quickly.&#x20;
- Add notify with when=errors and include a table of last error rows.&#x20;
- For bulk anomalies, prefer end + notify + manual intervention over automated retries.&#x20;
