> 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/governance/certification/membership-tags.md).

# Membership tags

Understand the five shipped membership-origin tags, verify each assignment path, create a custom tag, and know what certification and risk scoring do with them.

## What this is, and when you need it

Two users can hold the same role for very different reasons: an approved request, a birthright rule, a direct administrator action, or an ownership relationship. A **membership tag** records that assignment context on the membership row itself — not on the role, not on the user. Certification can exclude selected tags from a campaign's scope, and [risk scoring](/administration-guide/risk/factors.md#factor-membership-origin)'s `builtin.membership.origin` factor uses the tag to explain and score the grant path.

A tag does not grant access, does not replace an access right, and does not prove the originating control was effective. It is metadata about *how* the grant happened.

This page is the model: what the five shipped tags mean, which path stamps each one, and what certification and risk scoring do with them. It ends with the two short procedures that use the model — proving each assignment path on a disposable user, and adding a tag of your own. The tasks that consume tags are elsewhere: [Risk scoring](/administration-guide/risk/factors.md#factor-membership-origin) scores them, and [Plan and scope a campaign](/administration-guide/governance/certification/plan-and-scope-a-campaign.md) is where a campaign excludes them.

## Where they live

Tags are `METADATA_TYPE` rows in the **`MEMBERSHIP_TAG`** grouping, managed on **Administration → Metadata Types** (`/webconsole/metaDataTypeSearch`, edit at `/webconsole/metaDataTypeEdit?id=<TYPE_ID>`), where `MEMBERSHIP_TAG` is one of the values in the **Grouping** filter. Filtering on it is also the fastest way to see exactly which tags your deployment has, shipped and custom, in one list.

The link tables are one per membership kind, both keyed on the membership and the tag:

| Table                      | Membership                | Columns                                                       |
| -------------------------- | ------------------------- | ------------------------------------------------------------- |
| `USER_ROLE_MEMBERSHIP_TAG` | `USER_ROLE.MEMBERSHIP_ID` | `(MEMBERSHIP_ID, TAG_ID)`, `TAG_ID` → `METADATA_TYPE.TYPE_ID` |
| `USER_GRP_MEMBERSHIP_TAG`  | `USER_GRP.MEMBERSHIP_ID`  | `(MEMBERSHIP_ID, TAG_ID)`, `TAG_ID` → `METADATA_TYPE.TYPE_ID` |

Only role and group memberships are tagged. A campaign's exclusion list is a third table, `CERTIFICATION_TAG (CERTIFICATION_ID, TAG_ID)`. It was called `ACCESS_CERTIFICATION_TAG` in the `openiam` schema before certification moved to its own database, so a query or runbook using the old name is out of date and will now fail rather than return nothing.

Tags travel with the entitlement rather than with the membership row, which is what makes them useful: a screen that shows a user's access as a flat list still shows *how* each item arrived. The **Tags** column on a campaign preview's **Scope** view is the clearest place to see them, and one user's tags also show up as the membership-origin contribution on `/webconsole/user-risk?id=<USER_ID>`.

## The five shipped tags

Five ship, all seeded in the `MEMBERSHIP_TAG` grouping with `IS_SENSITIVE = 'Y'` and `CREATEABLE = 'Y'`. Confirm the set on **Administration → Metadata Types** with **Grouping** filtered to `MEMBERSHIP_TAG`: the five below are there on a default installation, and each shows as sensitive.

| `TYPE_ID`                   | Seeded `NAME`                         | Stamped when                                                                               | Governance meaning                                 |
| --------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------ | -------------------------------------------------- |
| `ADMIN_ASSIGNMENT`          | Admin assignment                      | A Webconsole or administrative-API grant                                                   | Administrative path that bypasses catalog approval |
| `SYSTEM_ASSIGNMENT`         | Admin or Owner system access          | The user is made owner or administrator of an object, or gains another system relationship | System/ownership access, not a requested grant     |
| `ACCESS_REQUEST_ASSIGNMENT` | Requested access                      | An access-request workflow completes                                                       | Request and approval path                          |
| `BUSINESS_RULE_ASSIGNMENT`  | Birthright assignment                 | A business rule grants birthright access                                                   | Policy-driven assignment                           |
| `PENDING_MANUAL_ASSIGNMENT` | Access awaiting administration action | A manual fulfilment task is opened                                                         | Transient lifecycle state, **not** an origin       |

Each is stamped by exactly one path, and knowing which is what makes a missing tag diagnosable: an entitlement granted in the Webconsole or over the provisioning API gets `ADMIN_ASSIGNMENT`; being made an owner or administrator of an object gets `SYSTEM_ASSIGNMENT`; a completed access-request workflow gets `ACCESS_REQUEST_ASSIGNMENT`; a business rule grant gets `BUSINESS_RULE_ASSIGNMENT`; and opening a manual fulfilment task adds `PENDING_MANUAL_ASSIGNMENT`, which is removed again when the task is completed. **Verify each assignment path** below walks all five on a disposable user, which is the only way to confirm your deployment behaves this way, because none of it is visible on a configuration screen.

All five are marked sensitive precisely because other subsystems match on the id. Do not rename, delete or repurpose them: renaming is safe because nothing matches on `NAME`, but changing or deleting a `TYPE_ID` silently breaks every risk parameter and campaign exclusion that names it.

`PENDING_MANUAL_ASSIGNMENT` is the odd one out and worth stating plainly: it says *fulfilment is incomplete*, not *how access was granted*. Risk scoring leaves it out of the origin factor entirely, and certification drops any membership carrying it from scope — both in the preview and at launch, so the preview count you approve is the count you get. It clears when the manual task completes; the origin tag stays. In a campaign preview those memberships appear as exclusions with the reason `ENTITLEMENT_PENDING_MANUAL_ASSIGNMENT`, which is where you see how many you have.

## Precedence when a membership carries several origins

When a membership carries more than one origin tag, one wins, and the order is fixed:

1. `ADMIN_ASSIGNMENT`
2. `SYSTEM_ASSIGNMENT`
3. `ACCESS_REQUEST_ASSIGNMENT`
4. `BUSINESS_RULE_ASSIGNMENT`
5. anything else, ordered lexicographically by type id

`PENDING_MANUAL_ASSIGNMENT` never takes part in that comparison. The ordering is not exposed anywhere in the Webconsole; the observable form of it is the origin contribution shown against the membership on `/webconsole/user-risk`, which names the tag that won.

The list is deliberately ordered **least-governed first**, so when a membership carries both `ADMIN_ASSIGNMENT` and `BUSINESS_RULE_ASSIGNMENT` the risk engine sees the administrative path — the one with the weakest controls behind it. That is the safe default, not a bug, but multiple origins on one membership is still a data problem: it usually means two assignment paths both stamped the same row. Fix the membership through its authoritative path rather than relying on the ordering.

## How certification uses tags

A current role or group membership is reviewable unless it is outside the campaign's entitlement or application scope, future-dated, expired, carries a configured exclusion tag, or is still pending manual assignment. The legacy `IS_CERTIFIED` access right plays no part in this any more — a membership does not need it to be reviewable, so removing it from a role changes nothing about campaign scope.

**Membership tags to exclude** removes every membership carrying the selected tag from the campaign preview, recorded as `CERTIFICATION_TAG` rows. It is a scope exclusion, not a display filter: excluding `BUSINESS_RULE_ASSIGNMENT` means reviewers never decide birthright memberships in that campaign, and nobody is told those memberships existed.

Use an exclusion only when a *named* control governs the omitted access, and record the tag, the rationale, the control owner and the expected exclusion count alongside the preview. If a campaign excludes `BUSINESS_RULE_ASSIGNMENT`, the rule-owner control has to independently prove the rule's criteria, population, grants, removals, failures and target state — otherwise the access is simply unreviewed. Keep `ADMIN_ASSIGNMENT` in scope unless a more frequent approved control owns it; it is the path with the least prior approval behind it.

Campaign scope and preview mechanics are on the campaign pages linked below.

## How risk scoring uses tags

`builtin.membership.origin` exposes one 0–100 parameter per `MEMBERSHIP_TAG` metadata type, keyed `score.<TYPE_ID>`, plus `unknownOriginScore` for a membership that was never stamped at all. Shipped bootstrap scores, and the full mechanism, are in [Risk factor reference](/administration-guide/risk/factors.md#factor-membership-origin-parameters): `ADMIN_ASSIGNMENT` 75, `SYSTEM_ASSIGNMENT` 60, `ACCESS_REQUEST_ASSIGNMENT` 25, `BUSINESS_RULE_ASSIGNMENT` 10, a custom tag 50, unknown origin 50.

Two consequences matter here. First, an unstamped membership is scored `unknownOriginScore`, not skipped — not knowing how access arrived is itself a governance signal, and 50 is deliberately not zero. Second, a tag with **no configured score contributes nothing**, so a custom tag created after `risk-manager` seeded its factor is worth 0 until you score it. The parameter appears on the screen immediately; the score does not.

## Verify each assignment path

Do this once, on a disposable role or group and a disposable user, before you rely on tags for either campaign scope or risk explanations. Each step ends by reading the tag back on the User Risk page (`/webconsole/user-risk?id=<TEST_USER>`) or from `USER_ROLE_MEMBERSHIP_TAG` directly.

{% stepper %}
{% step %}

### Direct administrative grant

Grant the role in Webconsole. Expect `ADMIN_ASSIGNMENT`. Remove it through the same path.
{% endstep %}

{% step %}

### Approved request

Grant it through a SelfService access request and let the approval complete. Expect `ACCESS_REQUEST_ASSIGNMENT`, stamped when the workflow finishes — **not** when the request is submitted, so a request still sitting with an approver has no tag and no membership to tag.
{% endstep %}

{% step %}

### Birthright

Trigger a business rule that grants the role. Expect `BUSINESS_RULE_ASSIGNMENT`.

Remember `PERFORM_BR_REPROVISION` ships **disabled**, so a rule change does not apply overnight whether or not you run a recalculation. Confirm the state on your system before you conclude the tag is not being stamped: **Administration → Batch Tasks**, find `PERFORM_BR_REPROVISION`, read the enabled column. Test on a rule you control. See [Shipped batch tasks](/administration-guide/reference/shipped-batch-tasks.md).
{% endstep %}

{% step %}

### Ownership

Make the user owner or administrator of a disposable entitlement. Expect `SYSTEM_ASSIGNMENT`.
{% endstep %}

{% step %}

### Pending manual fulfilment

Grant access requiring a manual fulfilment task on a manual managed system. Inspect the membership before completing the task — expect `PENDING_MANUAL_ASSIGNMENT` alongside the origin tag — and again after. The pending tag should be gone and the origin tag should remain.
{% endstep %}

{% step %}

### Campaign scope

Preview a campaign with no exclusion, then with one tag excluded, and reconcile the exact item and exclusion counts. A difference you cannot account for means the tag is being stamped somewhere you did not expect.
{% endstep %}
{% endstepper %}

## Create a custom tag

Only when the grant path is materially different *and* has an owner — an approved emergency-access workflow, for example. Not to make a campaign skip inconvenient access.

{% stepper %}
{% step %}

### Create the metadata type

**Administration → Metadata Types** (`/webconsole/metaDataTypeSearch`) → new type. Set **Grouping** to `MEMBERSHIP_TAG`, give it a stable uppercase `TYPE_ID` you will never change — the id, not the name, is what risk parameters and campaign exclusions reference — a readable display name, and **Active**.
{% endstep %}

{% step %}

### Stamp it from the assignment path

Have whatever performs the grant stamp the tag id on the membership — for an approved workflow of your own, that is a change to the script or integration behind it, not something the console can do for you. Test add, repeated add, removal, renewal, and migration from the previous path — a repeated add must not create a second row, and the composite primary key `(MEMBERSHIP_ID, TAG_ID)` is what stops it.
{% endstep %}

{% step %}

### Score it

Reopen **Administration → Risk factor configuration** (`/webconsole/risk-factors`), expand **Membership Origin**, and set `score.<YOUR_TAG_ID>`. Until you do, memberships on the new path contribute **0** for origin — which reads as safer than the unknown-origin default of 50, the opposite of what a new, lightly governed path deserves.
{% endstep %}

{% step %}

### Recompute and verify

Run the `RISK_NIGHTLY_SWEEP` task, then read a tagged user's membership on `/webconsole/user-risk?id=<TEST_USER>` and confirm the origin contribution names your tag and carries the score you set.
{% endstep %}

{% step %}

### Decide campaign treatment and ownership

Decide explicitly whether any campaign may exclude the new tag, and record who retires it when the path changes. An orphaned tag on live memberships is worse than no tag: it scores something nobody chose.
{% endstep %}
{% endstepper %}

## Verification

| What you did                    | Evidence                                                                                                                                                         |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Created or edited a tag type    | The `METADATA_TYPE` row with `GROUPING = 'MEMBERSHIP_TAG'`; `CREATE_METADATA_TYPE` or `MODIFY_METADATA_TYPE` in the audit log, target `METADATA_TYPE`            |
| Stamped a tag on a grant        | A row in `USER_ROLE_MEMBERSHIP_TAG` or `USER_GRP_MEMBERSHIP_TAG` for that `MEMBERSHIP_ID`; the tag in the **Tags** column of a campaign preview's **Scope** view |
| Confirmed risk sees the origin  | The membership's `builtin.membership.origin` contribution on `/webconsole/user-risk`, with the score you configured — not `unknownOriginScore`                   |
| Excluded a tag from a campaign  | A `CERTIFICATION_TAG` row for the campaign, and a preview item count lower than the unfiltered run by exactly the excluded population                            |
| A manual task completed cleanly | `PENDING_MANUAL_ASSIGNMENT` gone from the membership, origin tag still present                                                                                   |

## When it goes wrong

| Symptom                               | Cause                                                                                                                                                      | Fix                                                                                                                          |
| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| Expected tag absent                   | The grant arrived by a different path than you assumed, or the script behind your own path never stamped the tag                                           | Work out which of the five paths above actually granted it, then inspect `USER_ROLE_MEMBERSHIP_TAG` for that `MEMBERSHIP_ID` |
| Campaign silently omits access        | A `CERTIFICATION_TAG` exclusion, or `PENDING_MANUAL_ASSIGNMENT` on the membership                                                                          | Compare a preview with and without the exclusion; check the pending tag before assuming a scope bug                          |
| A custom tag scores zero              | A membership-tag type with no `score.<TYPE_ID>` value is treated as unscored, and contributes nothing rather than falling back to the unknown-origin score | Set the parameter on **Membership Origin** and recompute                                                                     |
| Every membership scores 50 for origin | `unknownOriginScore` applied — nothing is stamping origin tags on this population                                                                          | Verify the assignment paths; migrated memberships commonly have no tags at all                                               |
| The "wrong" origin wins               | The fixed precedence picks the least-governed tag when several are present                                                                                 | Remove the conflicting tag through the authoritative path; do not rely on ordering as a feature                              |

\| Tag disappears after a reprovision | The assignment path re-created the membership and did not re-stamp | Stamp inside the path that writes the membership, not afterwards | | Cannot delete a tag type | It is `IS_SENSITIVE = 'Y'`, and live memberships, risk parameters, campaign exclusions and audit records still reference the id | Do not delete. Deactivate the type and stop stamping it; a deleted `TYPE_ID` breaks every `score.<TYPE_ID>` parameter and every `CERTIFICATION_TAG` row that names it |

## Rollback

To back out a custom tag: restore the previous assignment implementation first, so nothing new is stamped. Then remove the tag from proven-affected memberships as a controlled migration, and recompute risk. Leave the metadata type itself in place and inactive — deleting it while memberships, risk factor parameters, campaign exclusions, scripts or audit evidence still reference the id turns a reversible change into a data-repair job.

## Related

* [Risk factor reference](/administration-guide/risk/factors.md#factor-membership-origin) — `builtin.membership.origin`, its per-tag parameters and shipped scores
* [The certification model](/administration-guide/governance/certification/certification-model.md) — how certification, risk and SoD relate
* [Plan and scope a campaign](/administration-guide/governance/certification/plan-and-scope-a-campaign.md) — where tag exclusions are set

*Applies to OpenIAM 2026.9.1. Last reviewed 14 September 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/governance/certification/membership-tags.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.
