> For the complete documentation index, see [llms.txt](https://docs.openiam.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.openiam.com/administration-guide/operations/enterprise-integrations.md).

# Integration patterns for external systems

Choose between provisioning, a manual task, an outbound ITSM script and audit export, and learn the contract an outbound script owes the workflow that calls it.

## What this is, and when you need it

IGA workflows rarely end inside OpenIAM. A request may need to create a ServiceNow incident, or a task for a person, and audit events may need to reach a SIEM. These integrations fail when ownership, correlation, retries and terminal state are not designed together, and they fail *silently* more often than not, because the shipped extension point records success as soon as your script returns.

This page is a decision and a set of contracts, not a procedure: pick the pattern, learn what your script owes the workflow that calls it, then go to the page that does the work. Connector authentication, schemas and permissions on the far side belong to the **Connector Configuration Guide** and compiled payload classes to the **Developer Guide**; choosing a script version, attaching it and rolling it back are administrator work and are here.

## Choose the correct pattern

| Outcome                                                                                                                             | OpenIAM pattern                                                                                                                                                                                                                                                                                    | Do not confuse it with                                  |
| ----------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| Automatically create or change an account in another system                                                                         | Managed system, connector, policy map, provisioning                                                                                                                                                                                                                                                | An ITSM ticket asking a person to do it                 |
| A human performs the change                                                                                                         | A managed system whose resource type is `MAN_MANAGED_SYS`, plus a workflow task                                                                                                                                                                                                                    | Proof that the change was actually made                 |
| Open, update and close a Jira or ServiceNow ticket when a request, a user, a provisioning operation or a non-human identity changes | A managed system on the Ticket Connector with ticket rules — [Ticket integration with Jira and ServiceNow](/administration-guide/ticketing.md). It replaces the Groovy script hook below, which is deprecated since 2026.10.2                                                                      | Provisioning: a ticket asks a person to make the change |
| Create an ITSM incident after approval with your own script                                                                         | A Groovy script on the request's target resource, invoked when an approved request completes and audited as `TICKET_SYSTEM_INTEGRATION`. Deprecated since 2026.10.2 — [Raise a ServiceNow incident from an approval](/administration-guide/requests/approval-flows/raise-a-servicenow-incident.md) | A bidirectional ServiceNow connector                    |
| Export audit events to a SIEM                                                                                                       | An audit export connector, `SYSLOG` or `GENERIC_WEBHOOK`                                                                                                                                                                                                                                           | Connector provisioning, or child audit events           |
| Notify Slack or Teams                                                                                                               | A webhook consumed by your own automation, or mail                                                                                                                                                                                                                                                 | A built-in interactive approval channel                 |

Each of the first four has a page that does the work, and none of them is on this one:

* [Manual fulfilment](/administration-guide/requests/fulfilment.md) — the **Manual** flag, the resource type it writes, who the task goes to, and what completion records.
* [Raise a ServiceNow incident from an approval](/administration-guide/requests/approval-flows/raise-a-servicenow-incident.md) — the shipped examples, the incident field mapping, and a safe customer version. It sits under [Approval flows](/administration-guide/requests/approval-flows.md), because an approval completing is what invokes it.
* [Audit export to a SIEM](/administration-guide/operations/audit-and-export.md) — the connector columns, the transformer, the syslog frame and the retry chain. Two of its facts change integration designs before anything else does: **only parent events are exported**, so your SIEM sees a `PROVISIONING` event and not the `CONNECTOR_RESPONSE` child that says what the far side did; and export is **real-time only** on this release, so nothing can be designed around a batch window.
* [Configure a managed system](/administration-guide/lifecycle/configure-managed-system.md) — the connector path, when a connector exists.

## Define the correlation contract first

Every outbound interaction should carry enough non-secret context to reconcile both systems: the request, workflow or run id; the target user or object id; the managed system, resource or entitlement id; the operation and its effective date; the external ticket or event id once created; the attempt number, a timestamp with zone and the terminal status; and a sanitised failure reason.

Do not use a display name or an email address as the only correlation key. Decide **where the external id is stored** and **how duplicate creation is prevented** before you enable retries. The two shipped ServiceNow examples answer that question differently, and one answers it badly, which is why [Raise a ServiceNow incident from an approval](/administration-guide/requests/approval-flows/raise-a-servicenow-incident.md) says which to copy.

## The ticket-system extension point

{% hint style="warning" %}
This hook is deprecated since 2026.10.2. It still runs, and every run now writes the info line `Deprecated: use the Ticket Connector` on its `TICKET_SYSTEM_INTEGRATION` event. For Jira and ServiceNow use [ticket integration](/administration-guide/ticketing.md) instead; the move is described in [Operate ticket integration](/administration-guide/ticketing/operate-ticketing.md#operate-migrate-from-the-groovy-hook).
{% endhint %}

This is the general "do something outbound after a request completes" hook. It is worth understanding independently of ServiceNow, because ServiceNow is only the example that ships on it.

### Where it hangs

On the **Groovy Script** field of the *target resource of the request*, stored as `RES.GROOVY_SCRIPT_ID` and set at `/webconsole/editResource`. Nothing else selects it, so the scope of an outbound integration is entirely a function of which resource you attach the script to. A resource with that field empty invokes nothing and records nothing.

### What invokes it

Four request completions, and two of them do not use the request's target resource at all:

| Request completes                 | Resource whose script runs                                                                           | Object handed to your script |
| --------------------------------- | ---------------------------------------------------------------------------------------------------- | ---------------------------- |
| An access request                 | The request's target resource                                                                        | The user being provisioned   |
| A revoke-access request           | The request's target resource                                                                        | The user losing access       |
| A new hire or a self-registration | The fixed resources `NEW_HIRE_WITH_APPROVAL_AR`, `NEW_HIRE_NO_APPROVAL_AR` or `SELF_REGISTRATION_AR` | The newly created user       |
| A group create or edit request    | The fixed resources `NEW_GROUP_AR` or `EDIT_GROUP_AR`                                                | The group                    |

All five fixed ids ship as `ACTIVITI_REQUEST` resources with an empty **Groovy Script** field, named **NEW HIRE WITH APPROVAL**, **NEW HIRE NO APPROVAL**, **SELF REGISTRATION**, **NEW GROUP** and **EDIT GROUP**. To raise a ticket on a new hire or a group change you attach the script to one of those, not to whatever resource the requester picked.

### The contract your script implements

Your class extends `ResourceGroovyScriptHelper` and implements one abstract method:

{% code overflow="wrap" %}

```java
public abstract void perform(final Object object,
                             final Object model,
                             final Map<String, Object> taskVariables,
                             final AuditLogBuilder auditLogBuilder);
```

{% endcode %}

`object` is the subject from the table above — cast it to `User` on the three user paths and to `Group` on the group path. `model` is the request model. `taskVariables` is the workflow execution's variable map, which is where the original `Request` and `UserNotes` are. `auditLogBuilder` is the child audit event described below, and it is the only way to report a failure without aborting the request.

The base class gives you, as `protected` fields you can use directly:

| Available to your script                                                                                                                                        | What it is for                                                               |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `userMQService`, `roleRabbitMQService`, `groupRabbitMQService`, `resourceRabbitMQService`, `managedSystemRabbitMQService`, `approverAssociationRabbitMQService` | Reading and saving OpenIAM objects                                           |
| `jacksonMapper`, `objectMapper`                                                                                                                                 | Serialising a payload                                                        |
| `propertyValueSweeper`                                                                                                                                          | Reading a system property value                                              |
| `context`                                                                                                                                                       | The Spring application context, set on your instance after it is constructed |
| `log`                                                                                                                                                           | The workflow service log                                                     |
| `getDecryptedMngSysPassword(ManagedSysDto)`                                                                                                                     | The plaintext password stored on a managed system                            |
| `parseResourceAttributes(Resource)`                                                                                                                             | A resource's attributes as a name-to-values map                              |

The base class also declares a `request` field of type `HttpServletRequest`. **Nothing populates it on this path** — these scripts run on a workflow thread, not inside an HTTP request — so treat it as null and do not build anything on it.

### How it is audited

The invocation creates a **child** audit event under the request's own event, with action `TICKET_SYSTEM_INTEGRATION` and the info line *"Calling perform operation from `<groovyScriptId>`"*. If the class cannot be loaded, that child event **fails** with *"Error while instantiating groovy file `<groovyScriptId>`"* — which is also what you see when the class does not extend `ResourceGroovyScriptHelper`.

{% hint style="danger" %}
**The child event succeeds if your script returns without throwing.** `perform` returns `void`, and OpenIAM marks the child event successful immediately after calling it, without inspecting anything. An outage, a `401` or a malformed response therefore produces a `TICKET_SYSTEM_INTEGRATION` event marked **succeeded** and no ticket, and the shipped ServiceNow create example does exactly that because it catches its own exceptions and returns.

Calling `auditLogBuilder.fail()` inside your script does not help either: the unconditional success call runs afterwards and overwrites the result. The two real options — record a failure reason on the handed-in audit log builder and return, or throw and fail the whole request — are set out in [step 5 of the ServiceNow task](/administration-guide/requests/approval-flows/raise-a-servicenow-incident.md#build-and-attach-the-incident-script). Deciding between them is the first design decision, not the last.
{% endhint %}

## The request post-action hook, and how it differs

There is a second Groovy hook on the access-request path, and administrators confuse the two. It is selected by the system property `org.openiam.activiti.create.request.helper.script`, which ships `/bpm/CreateUserRequestHelper.groovy` — a class that extends `IntegrateCreateUserRequestHelper`, implements `perform(User, CreateAccessRequestModel, Map, AuditLogBuilder)`, and **ships with an empty method body**. It is audited as `CREATE_REQUEST_POST_ACTION` with the same *"Calling perform operation from …"* info line, and it succeeds unconditionally in the same way.

Three differences decide which one you want:

|             | Request post-action                                                                                                                                              | Ticket-system integration                                 |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| Selected by | One system property, for the whole installation                                                                                                                  | `RES.GROOVY_SCRIPT_ID`, per resource                      |
| Runs        | **Before** the entitlement is saved                                                                                                                              | **After** the entitlement is saved                        |
| Audit event | `CREATE_REQUEST_POST_ACTION`, attached to the *parent* of the request event rather than to the request event, so it does not appear among the request's children | `TICKET_SYSTEM_INTEGRATION`, a child of the request event |

Use the post-action hook when you need to change the request before it is applied, and the ticket-system hook when you need to tell somebody else that it was applied. When no script is configured, the workflow service logs *"There is no groovy defined for CREATE\_REQUEST\_POST\_ACTION. no actions will be performed"*.

{% hint style="warning" %}
**The entitlement is recorded before any outbound work happens.** On the access-request path OpenIAM saves the user, then calls the ticket script, then creates the manual task, and only then marks the request successful. OpenIAM's own record says the user holds the access from the moment the request completes; the ticket and the manual task are requests for somebody to make that true elsewhere. If a control requires proof of the change, the evidence has to come from the other system, and your reconciliation has to expect a window in which the two disagree.
{% endhint %}

## Update and closure are a separate contract

Nothing built on the Groovy hook updates or closes a ticket. [Ticket integration](/administration-guide/ticketing.md) does, for Jira and ServiceNow: its rules update and close the ticket they opened, and a ticket synchronization reads its status, resolution and assignee back every 15 minutes. Nothing consumes an inbound ITSM callback, though, and nothing changes OpenIAM because a ticket changed. If the requirement is bidirectional fulfilment, build it deliberately: decide which system owns `requested`, `assigned`, `fulfilled`, `failed`, `canceled` and `closed`; add an authenticated callback endpoint or a bounded polling batch task; validate the external ticket id **plus** its request or object relationship, because a ticket number alone is not authorization; make updates idempotent, reject backward transitions, and record both the external actor and the OpenIAM service actor; then test out-of-order, duplicate, missing and unauthorized callbacks.

Until that exists, describe the integration as **outbound incident creation**, not as ITSM fulfilment.

## Another ITSM platform

Jira and ServiceNow are covered by [ticket integration](/administration-guide/ticketing.md). No ManageEngine or Remedy implementation ships. Three honest patterns, in increasing effort: a manual managed system and an OpenIAM manual task; a customer-owned `ResourceGroovyScriptHelper` against the platform's own HTTPS API, with the same contract, correlation and idempotency requirements as above; or an external integration service that consumes an OpenIAM event — a generic-webhook audit export is the supported feed — and returns a secured status update. Do not rename the ServiceNow sample and claim connector parity.

## Collaboration channels and their real boundary

Two questions come up in every RFI, and both are better answered here than discovered in a project.

**There is no Slack or Teams messaging, notification or approval channel.** What exists for Teams is a plugin bootstrap, and nothing in it sends a message. The single administrator-facing control is the system property `org.openiam.ui.oauth.client.teams.plugin`, which names the OAuth authentication provider the plugin identifies itself as and **ships empty**; while it is blank the bootstrap reports a missing system configuration property, and when it names a provider that does not exist it reports that instead. There is no screen for either state. One half of the bootstrap is unauthenticated by design and writes no audit event at all; the other is deprecated and audited as `REGISTER_APPLICATION`. Neither is a channel.

For Slack the only shipped artefact is a **synchronization** configuration: `SLACK_GROUP_SYNC`, named **Slack Group Sync**, a full group synchronization that reads through the SCIM managed system `SCIM_MANAGED_SYS`. It ships **Active** with no cron expression, so it never runs until you schedule it. Slack is reachable as a SCIM provisioning and synchronization endpoint; it is not reachable as a place to send or approve anything.

What *is* supported is mail — see [Mail and notifications](/administration-guide/platform/mail-and-notifications.md) — and a generic-webhook audit export feeding an automation of your own that posts wherever you like. Before enabling that, decide the event filter, the recipient or channel owner, the minimum payload and its redaction, link authentication and expiry, rate limiting or digesting, and who owns delivery failure.

{% hint style="danger" %}
Do not permit approve or reject actions from a collaboration channel unless the product provides a signed, replay-resistant, auditable action flow and you have tested it. OpenIAM's one shipped **inbound** decision channel is email, swept by the `email-manager` service on `org.openiam.email.inbox.sweep` = **900000** ms and audited as `MAKE_DECISION_FROM_EMAIL` with source `EMAIL_MANAGER`. It does nothing until **Read Inbox?** is set on a mailbox configuration; the constraints are on [Manual fulfilment](/administration-guide/requests/fulfilment.md#email-based-fulfilment-and-approval). A message containing a link can direct a reviewer to OpenIAM; it is not itself a decision.
{% endhint %}

## Related

* [Raise a ServiceNow incident from an approval](/administration-guide/requests/approval-flows/raise-a-servicenow-incident.md) — the task built on the extension point above, under **Requests and approvals**
* [Manual fulfilment](/administration-guide/requests/fulfilment.md) — the human-task pattern end to end
* [Audit export to a SIEM](/administration-guide/operations/audit-and-export.md) — the export connector in full
* [Approval flows](/administration-guide/requests/approval-flows.md) — the workflows whose completion invokes these hooks
* [Shipped Groovy scripts](/administration-guide/reference/shipped-groovy-scripts.md) — where the ServiceNow examples sit among the 745

*Applies to OpenIAM 2026.8.1. Last reviewed 7 August 2026.*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.openiam.com/administration-guide/operations/enterprise-integrations.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
