> 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/developer-guide/how-scripting-works.md).

# How scripting works

The script types, where each one runs in a flow, what it receives, and the conventions every script follows.

OpenIAM's extension points are Groovy scripts. They run inside the platform's JVM, inside its Spring context, and are compiled when you save them.

**Read this before any tutorial.** The script types differ in what they extend, what they receive and when they run, and most mistakes come from applying one type's habits to another.

## The script types

| Type               | You write                                                                                                        | Runs when                                                    | Tutorial                                                                                   |
| ------------------ | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
| **Policy map**     | A class extending `AbstractIPolicyMapGroovy`                                                                     | Provisioning an account into a managed system, per attribute | [*Policy map scripts*](/developer-guide/policy-map-scripts.md)                             |
| **Transformation** | A class extending `AbstractUserTransformScript`, `AbstractGroupTransformScript` or `AbstractRoleTransformScript` | Synchronisation, once per inbound row                        | [*Transformation scripts*](/developer-guide/transformation-scripts.md)                     |
| **Validation**     | A class implementing `ValidationScript`                                                                          | Synchronisation, before transformation, per row              | [*Validation and attribute scripts*](/developer-guide/validation-and-attribute-scripts.md) |
| **Attributes**     | A class implementing `AttributesScript`                                                                          | Synchronisation, to declare which attributes are read        | [*Validation and attribute scripts*](/developer-guide/validation-and-attribute-scripts.md) |
| **Reconciliation** | A class extending a reconciliation command base class                                                            | Reconciliation, when a situation is matched                  | [*Reconciliation scripts*](/developer-guide/reconciliation-scripts.md)                     |
| **Batch**          | **A plain script, not a class**                                                                                  | A scheduled batch task                                       | [*Batch scripts*](/developer-guide/batch-scripts.md)                                       |

**Policy map is the type you will write most.** Across the scripts shipped under `conf/iamscripts/`, `AbstractIPolicyMapGroovy` accounts for more than five times the next most common base class. Transformation is the easiest to learn, which is why the tutorials start there, but policy map is where the volume is.

## Where each type runs

**Synchronisation** — reading identities from a source into OpenIAM:

```
source row
   -> AttributesScript      which attributes to read
   -> ValidationScript      is this row usable?
   -> TransformScript       map the row onto a ProvisionUser / Group / Role
   -> OpenIAM
```

**Provisioning** — pushing an account out to a managed system:

```
OpenIAM change
   -> a diff object describing what changed
   -> policy map script, per attribute
        isPerform(diff)        should this attribute be written at all?
        perform(attr, diff)    what value should it carry?
   -> connector -> managed system
```

**Reconciliation** — comparing OpenIAM against a managed system: a *situation* is matched, and the command script bound to that situation runs.

**Batch** — a scheduled task runs a script on its own, with nothing handed to it.

## Conventions every script follows

**The class name matches the file name.** `MemberOf_1.0.groovy` declares `class MemberOf` — the name before the version suffix.

**The version is in the file name.** `ScriptName_1.0.groovy`. When you change a script that is in use, **increment the version rather than editing in place**. It gives you something to go back to, and the Groovy Manager keeps both.

**No package declaration.** The large majority of shipped scripts have none, and those that do use no single convention. The platform identifies a script by name and folder.

**A logger is usually available.** Transformation base classes expose `log`. Policy map and reconciliation scripts in the shipped set typically declare their own with `LogFactory.getLog(...)`. Either works — do not declare one that shadows an inherited one.

**Spring beans are reachable, and how depends on the type.** Class-based scripts use `@Autowired` or an injected context field. Batch scripts receive the context through a binding variable. Each tutorial shows its own.

## What a service call returns

Scripts call the platform's services directly, and those services answer with `Response` — the internal service envelope. **It is not the envelope the REST API returns**, so a pattern you learned from an integration does not carry over.

| Field                             | Is                                                                |
| --------------------------------- | ----------------------------------------------------------------- |
| `status`                          | `SUCCESS` or `FAILURE`                                            |
| `errorCode`                       | A `ResponseCode` — 554 values, and specific                       |
| `errorText`                       | Human-readable detail                                             |
| `fieldMappings`                   | Which field failed validation, and why                            |
| `errorTokenList`                  | Structured detail where an operation produces more than one error |
| `rootCauseText`, `stacktraceText` | Diagnostics                                                       |

`isSuccess()` is the convenient check:

```groovy
def response = someService.doSomething(argument)
if (!response.isSuccess()) {
    log.error("failed: ${response.errorCode} ${response.errorText}")
    return
}
```

**Check every service call.** A script that ignores the response continues on the assumption that the call worked, and the failure surfaces later somewhere that does not point back at the script.

**Do not swallow `errorCode`.** Log it as it comes. A code you have never seen is more useful in a log than a message you substituted for it.

A caller reaching the platform over REST sees a different envelope — [*API fundamentals*](/developer-guide/fundamentals.md) sets out which endpoints return which.

## Where scripts live

**In the platform, not on disk in your project.** Two ways in, and they are not equivalent:

|                      | Groovy Manager                                   | Filesystem                                     |
| -------------------- | ------------------------------------------------ | ---------------------------------------------- |
| **Where**            | Webconsole → **Administration → Groovy Manager** | `conf/iamscripts/` on the server               |
| **Use for**          | Creating, editing, importing and exporting       | Bulk seeding, and reading the shipped examples |
| **Compiles on save** | Yes, with the error shown in the editor          | No                                             |

The folders under `conf/iamscripts/` are the taxonomy the Groovy Manager presents:

| Folder                                                                       | Holds                                                          |
| ---------------------------------------------------------------------------- | -------------------------------------------------------------- |
| `provision/`                                                                 | Policy map scripts, per managed system. The largest set by far |
| `sync/`                                                                      | Transformation, validation, matching and attribute scripts     |
| `recon/`                                                                     | Reconciliation scripts                                         |
| `batch/`                                                                     | Batch task scripts                                             |
| `bpm/`, `workflow/`                                                          | Workflow scripts                                               |
| `AM/`, `idp/`, `oauth/`, `uri/`                                              | Access management, identity provider and federation            |
| `selfservice/`, `webconsole/`, `ui-template/`                                | Interface behaviour                                            |
| `mail/`, `language/`, `geo/`, `reports/`                                     | Notification, localisation and reporting hooks                 |
| `prov-helper/`, `business-rules/`, `request-attributes/`, `user-conversion/` | Shared helpers                                                 |

**Read the shipped scripts.** They are production code, and they are the best reference available for every pattern in these tutorials.

## Testing

**There is no local test harness.** Scripts depend on the platform's Spring context and its injected services, so they are exercised by deploying them and observing them.

What you do locally is compile — [*Set up your environment*](/developer-guide/set-up-your-environment.md) covers a project that catches a wrong type or a missing method before you deploy. What you do on the platform is covered per type in each tutorial: how to trigger it, and where its output appears.

## Related

* [*Set up your environment*](/developer-guide/set-up-your-environment.md)
* [*Transformation scripts*](/developer-guide/transformation-scripts.md) — the easiest type to start with
* [*Policy map scripts*](/developer-guide/policy-map-scripts.md) — the type you will write most

*Applies to OpenIAM 2026.8.1.*


---

# 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/developer-guide/how-scripting-works.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.
