> 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/requests/approval-flows/approver-association-reference.md).

# Approver associations and the shipped flow shape

Every APPROVER\_ASSOC column, the fifteen approver and notifier entity types and where each is offered, how definitions reach the engine, and what the shipped entitlement flow does step by step.

## What this covers

These tables support [Approval flows](/administration-guide/requests/approval-flows.md). Read that page to change who approves; come here for every column on an approval step, every value the two entity-type fields accept and which dropdown each appears in, how a definition gets from a file to the engine, and what the shipped entitlement flow does on approve and on reject.

## How definitions reach the engine

You do not deploy workflows from the Webconsole. Four consequences of how OpenIAM loads them will reach you anyway:

1. **Definitions are loaded when the workflow service starts, and only then.** A changed definition file is not a deployed definition until the service restarts. So if you have asked someone to change a flow and nothing happened, "was the workflow service restarted?" is the first question — not the last.
2. **Every XML file in the workflow directory is deployed**, including one somebody left behind as a backup. A flow you have never heard of appearing in **Shipped approval workflows** but not in your inventory is usually this.
3. **Definitions are versioned, not replaced.** A redeploy creates a new version, and process instances already running continue on the version they started with. An edit does **not** retroactively fix in-flight requests — you have to find them and complete or cancel them deliberately.
4. **Task and variable history is retained.** That is what makes the diagnosis in [Debugging a stuck request](/administration-guide/requests/approval-flows.md#debugging-a-stuck-request) possible at all.

**49** definitions ship. The full inventory, with process IDs and which ones contain the multi-instance approval loop, is in [Shipped approval workflows](/administration-guide/reference/shipped-workflows.md).

{% hint style="warning" %}
Changing a workflow definition changes behaviour for every request of that type, tenant-wide, and there is no per-tenant override. Before you ask for one, read [Change configuration safely](/administration-guide/start/change-safely.md) and be sure an approver association would not do the job. The rollback for a definition change is a service restart with the previous file, which means downtime you should plan for.
{% endhint %}

## What the shipped entitlement flow does

`addUserToRole` is the flow behind "add this user to this role", and it is the template most entitlement flows follow. You cannot change its shape from the Webconsole, but you need to know four things it does, because each one produces a question you will be asked.

**Approval is serial, one approver at a time, in level order.** The resolved approvers are held in the process variable `ApproverCardindality` — the misspelling is the product's, so match it exactly when you search for it — as a list of `<approverUserId>:<approverLevel>` entries. Level `0` goes first.

**A rejection short-circuits every remaining approver.** The loop's exit condition is "the last decision was not an approval", so a reject at level 0 ends the request; levels 1 and above are never asked. There is no "reject with escalation to a higher approver" shape in the shipped flows.

**Task assignment depends on how many candidates resolved.** OpenIAM resolves the candidates for the current level and writes them to the task-local variable `candidateUsersIds`. **With exactly one candidate it sets the task assignee** and the `AssigneeUserId` variable, so the task appears directly in that person's **SelfService → Inbox**. **With more than one it adds each as a candidate**, so the task appears in several inboxes and has to be claimed before it can be decided. That single-versus-many branch is the answer to most "why can't my approver see the task" and "why do I have to claim it" questions. Task assignment is audited under `TASK_LISTENER`, carrying the `CANDIDATE_USER_IDS` attribute — that audit entry is how you prove who the task was offered to.

**The grant happens after the decision, not as part of it.** On approve, one step adds the role membership and a second sends notifications; on reject, only the rejection notification runs and nothing is granted. **So a request can read Approved while the user does not have the role.** When that happens the place to look is the workflow service log, where the membership step logs its own failure under `EntityMembershipDelegate` — not the audit log, which will happily show the approval.

**Confirm it end to end:** after an approval, open the user and check the membership exists. The request status is not evidence that the grant happened.

## The approver association model

One row of `APPROVER_ASSOC` is one approval step. Its columns:

| Column                    | Field                   | Type                    | What it does                                                         |
| ------------------------- | ----------------------- | ----------------------- | -------------------------------------------------------------------- |
| `APPROVER_ASSOC_ID`       | `id`                    | String(32)              | Primary key                                                          |
| `REQUEST_TYPE`            | `requestType`           | String(32)              | Narrows the row to one request type; `NULL` means all                |
| `ASSOCIATION_TYPE`        | `associationType`       | String(32)              | What kind of object *owns* this step — the values in the table below |
| `ASSOCIATION_ENTITY_ID`   | `associationEntityId`   | String(32)              | The owning object's ID                                               |
| `APPROVER_LEVEL`          | `approverLevel`         | Integer                 | Step order; `0` is the first step                                    |
| `APPROVER_ENTITY_TYPE`    | `approverEntityType`    | String(32)              | How the approver is identified — the same value set                  |
| `APPROVER_ENTITY_ID`      | `approverEntityId`      | String(32)              | The approver user/role/group/org ID                                  |
| `IS_MANDATORY`            | `mandatory`             | Y/N, **default `true`** | Required step                                                        |
| `APPLY_DELEGATION_FILTER` | `applyDelegationFilter` | Y/N                     | Apply the approver's delegation filter                               |
| `DAYS_BEFORE_REMINDER`    | `daysBeforeReminder`    | Integer                 | Days to wait before the first reminder                               |
| `NUMBER_OF_REMINDERS`     | `numberOfReminds`       | Integer                 | How many reminders to send                                           |
| `DAYS_TO_COMPLETE`        | `daysToComplete`        | Integer                 | Step SLA driving escalation                                          |
| `MAIL_TEMPLATE_ID`        | `emailTemplateId`       | String(32)              | Override the notification template                                   |

Three child collections hang off the row:

* `ESCALATION` — `ESCALATION_LEVEL`, `ESCALATION_ENTITY_ID`, `ESCALATION_ENTITY_TYPE`, plus its own `DAYS_BEFORE_REMINDER`, `NUMBER_OF_REMINDERS`, `DAYS_TO_COMPLETE` and `MAIL_TEMPLATE_ID`.
* `APP_ASSOCIATION_NOTIFIABLE` where `NOTIFIABLE_ON_APPROVED = 'Y'` → the *notify on approval* list.
* The same table where `NOTIFIABLE_ON_APPROVED = 'N'` → the *notify on rejection* list.

Both entity-type columns take the same set of values, and each value carries three flags that decide where it is offered. The **Approver object type** dropdown lists only the values marked as approvers; the *notify on approval* and *notify on rejection* pickers list only those marked notifiable. So if a value below is missing from a dropdown, this table is why — it is not a permissions problem.

| Value                   | Offered as approver | Offered as notifier | Part of the RBAC model |
| ----------------------- | ------------------- | ------------------- | ---------------------- |
| `USER`                  | yes                 | yes                 | no                     |
| `SUPERVISOR`            | yes                 | yes                 | no                     |
| `ROLE`                  | yes                 | yes                 | yes                    |
| `GROUP`                 | yes                 | yes                 | yes                    |
| `TARGET_USER`           | no                  | yes                 | no                     |
| `RESOURCE`              | no                  | no                  | yes                    |
| `ORGANIZATION`          | no                  | no                  | yes                    |
| `OWNER`                 | yes                 | yes                 | no                     |
| `ADMIN`                 | yes                 | yes                 | no                     |
| `APPLICATION_OWNER`     | no                  | no                  | no                     |
| `APPLICATION_ADMIN`     | no                  | no                  | no                     |
| `ENTITLEMENT_OWNER`     | no                  | no                  | no                     |
| `ENTITLEMENT_ADMIN`     | no                  | no                  | no                     |
| `SERVICE_ACCOUNT_OWNER` | no                  | no                  | no                     |
| `CERTIFICATION`         | no                  | no                  | yes                    |

The five `*_OWNER` / `*_ADMIN` values and `CERTIFICATION` are certification reviewer types and are deliberately excluded from the approver dropdown. `CERTIFICATION` associations are not read from `APPROVER_ASSOC` at all — they live in the certification service's own tables, so do not look for a campaign's reviewers here. See [Configure the review workflow](/administration-guide/governance/certification/configure-the-review-workflow.md).

## Related

* [Approval flows](/administration-guide/requests/approval-flows.md) — changing who approves, the routing properties, and diagnosing a stuck request
* [Shipped approval workflows](/administration-guide/reference/shipped-workflows.md) — all 49 definitions, their process IDs and which contain the multi-instance approval loop
* [Enumerated values](/administration-guide/reference/enums.md#workflow-variables) — the workflow variables named here
* [Configure the review workflow](/administration-guide/governance/certification/configure-the-review-workflow.md) — where certification reviewer types live instead

*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/requests/approval-flows/approver-association-reference.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.
