> 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/preview-and-launch.md).

# Preview and launch a campaign

Run a preview, read the three numbers that decide whether the scope is right, then promote it into a live campaign — plus every preview status and every exclusion reason.

## What this is, and when you need it

Preview is free: it writes nothing to the certification database and never calls the workflow engine. Launch is not — it creates review items, starts one Activiti process per user, and mails every resolved reviewer. So the risk on a first run is not a bad preview, it is a **launch at production scale before anyone has looked at the preview numbers**.

This page is the second half of building a campaign. The definition, its scope and its reviewer steps are [Plan and scope a campaign](/administration-guide/governance/certification/plan-and-scope-a-campaign.md); come here once the **Preview** tab has appeared.

**Every route into a campaign now goes through the preview.** There is no direct launch — see [Campaigns opened without a preview](#campaigns-opened-without-a-preview) for what that means for schedules and for older campaigns you inherit.

## At a glance

|                          |                                                                                                                                                           |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **You will end up with** | One `CAMPAIGN` row with review items created, Activiti processes started, reviewers notified, and the exclusion slice captured as evidence                |
| **Where**                | The **Preview** tab of `/webconsole/access-certification-edit?id=<CERTIFICATION_ID>`                                                                      |
| **You need first**       | A saved, scoped definition with at least one reviewer step, and an **Email Template** set                                                                 |
| **Takes**                | Minutes on a large population for the preview itself                                                                                                      |
| **Reversible**           | **No.** Launch mails reviewers and starts workflows. A campaign can be expired or deleted, but deleting it destroys the evidence that the review happened |

## Preview, read, launch

{% stepper %}
{% step %}

### Run a preview

**Preview** tab → **Request preview**. Set **Preview validity (hours)** first if this preview needs to stay launchable for longer, or shorter, than the default.

A preview runs in the background — the tab updates itself and the launch continues if you navigate away — and there is **one active preview per certification**. Starting a second one cancels the first, so the most recent request is the one that runs.

Compute runs in two phases, and the progress card names the one you are in, so the bar refills rather than sticking at 100%:

1. `1/2 Scanning users…` — page through the in-scope users, classifying each as included or excluded.
2. `2/2 Calculating results…` — resolve reviewers for every included user, build the review items, and persist the snapshot.

When it finishes, the tab shows the numbers you are actually judging as tiles: **Users** and **Review items**, each broken into **In scope**, **Excluded** and **Total**, with how long ago the preview was computed and how long it has left before it expires.
{% endstep %}

{% step %}

### Read the preview before you launch

Three numbers decide whether the scope is right:

* **Review items — In scope** is how many decisions you are about to ask humans to make. Divide by reviewer count; if the answer is in the thousands per person, the campaign will be bulk-approved, which is worse than a narrower campaign.
* **User — no reviewer**, one of the **Review exclusions before launching** tiles, is how many users are being held out because a reviewer could not be computed. This is almost always a data problem — a missing supervisor, an application with no owner — and it is cheaper to fix now than to explain later.
* **Users — Excluded** against **Users — In scope**. If exclusions dominate, the scope filter is doing something you did not intend. Click the tile to see which rows and why.

Browse the results on the tab's three views:

| Where                                            | What it shows                                                                                                                                             |
| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Scope** → **By user**                          | One row per user who will be reviewed, with their resolved reviewers                                                                                      |
| **Scope** → **By access item**                   | One row per review item that will be created                                                                                                              |
| **Reviewers**                                    | Every resolved reviewer with their user and item counts. The counts cover **only the first step** of the review; later approval steps may route elsewhere |
| **Excluded** → **Users**                         | Every user held out, with the reason                                                                                                                      |
| **Excluded** → **Access items**                  | Every access item dropped, with the reason                                                                                                                |
| The **Review exclusions before launching** tiles | Exclusion counts grouped by reason. Clicking a tile opens the **Excluded** view narrowed to it                                                            |
| {% endstep %}                                    |                                                                                                                                                           |

{% step %}

### Launch from the preview

**Launch campaign**, audited as `EXECUTE_CERTIFICATION`. That audit record is your proof the launch was accepted; a launch that was refused leaves none.

Two guards run first: the preview's status must be exactly `COMPLETED`, and it must still be inside the freshness window. A third check is a prompt rather than a block — if the definition was saved after the preview was computed, **Configuration changed since this preview.** offers **Recompute** or **Launch anyway**, and launching anyway creates the campaign from the preview exactly as shown, not from the configuration now on screen. Promotion then reuses the same progress card — `LAUNCHING` → `LAUNCHED`, or `LAUNCH_FAILED`, under `Creating the campaign…` — so one bar covers both compute and launch.

Launch does three things in order. It creates the `CAMPAIGN` row, stamped with `PREVIEW_ID`, `LAUNCHED_BY` and `LAUNCHED_FROM_PREVIEW_AT`. It **captures the exclusion slice** into `CAMPAIGN_PREVIEW_EXCLUSION` — fail-closed, before any included batch is submitted, so an incomplete capture aborts the launch and leaves an empty campaign rather than one with a partial audit trail. Then it creates review items and Activiti processes batch by batch, generates the scope report, and notifies reviewers.

The campaign opens as `STARTED` and moves to `IN_PROGRESS` on the first reviewer decision. A per-run copy of the policy flags — `AUTO_CERTIFY_ENABLED`, `AUTO_CERTIFY_MAX_SCORE`, `AUTO_CERTIFY_VISIBILITY`, `ALLOW_BULK_APPROVAL`, `BETA_FEATURES_ENABLED`, `MIN_MEMBERSHIP_RISK` — is taken at this moment, which is why editing the definition afterwards does not change a running campaign.
{% endstep %}
{% endstepper %}

## Verification

| What you changed        | Evidence                                                                                                                                                                                                                   |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Started a preview       | The **Preview** tab reaches `COMPLETED` and shows its metric tiles; leaving the tab and returning re-attaches to the same preview rather than offering **No preview yet**                                                  |
| Launched                | Audit `EXECUTE_CERTIFICATION`; a `CAMPAIGN` row with `PREVIEW_ID`, `LAUNCHED_BY` and `LAUNCHED_FROM_PREVIEW_AT` set; `EXCLUDED_USER_COUNT` / `EXCLUDED_ACCESS_COUNT` / `REVIEWERS_NOT_RESOLVED_COUNT` matching the preview |
| Reviewers were notified | `ACCESS_REVIEW_ITEM` rows exist **and** the campaign's `MAIL_TEMPLATE_ID` was non-blank; the notification audit event is `SEND_CERTIFICATION_NOTIFICATIONS`                                                                |
| Scope report generated  | `<confpath>/upload/certification_scope_report_<CAMPAIGN_ID>.csv`, or the **Reports** section of the campaign's **Dashboard** tab offers it for download                                                                    |

## When it goes wrong

| Symptom                                                                                                              | Cause                                                                                                                                      | Fix                                                                                                                                          |
| -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
| **Request preview** fails with `Failed to start preview` and `Another preview is already active for certification …` | Two starts raced each other for the certification's single active-preview slot                                                             | Re-open the **Preview** tab, which re-attaches to the preview that is running, and either use it or **Cancel preview** and request a new one |
| Launch refused with *"only a COMPLETED preview can be launched"*                                                     | The preview is in `RUNNING`, `FAILED`, `CANCELLED` or already `LAUNCHED`                                                                   | Re-run the preview; a terminal preview's snapshot has been dropped                                                                           |
| Launch refused with *"older than the 24h freshness window"*                                                          | `org.openiam.certification.preview.freshness.hours` exceeded                                                                               | Re-run the preview, or raise the property if your approval cycle genuinely needs longer                                                      |
| Preview fails with *"Preview capacity is full"*                                                                      | The bounded preview executor rejected the task                                                                                             | Retry, or raise `org.openiam.certification.preview.executor.max.pool.size` / `...queue.capacity`                                             |
| Preview completes with `includedUserCount = 0` and every user carrying `NO_APPROVER_ASSOCIATION`                     | No reviewer step on the certification                                                                                                      | Add a `CERTIFICATION_REVIEWER` row via the **Reviewers** tab                                                                                 |
| Every user is excluded as `ALL_MEMBERSHIPS_BELOW_RISK_THRESHOLD`                                                     | The membership risk score on the **Risk** tab is higher than any grant in scope                                                            | Lower it, or clear it to review everything — see [Access certification and risk](/administration-guide/risk/certification.md)                |
| Campaign launched but no email arrived                                                                               | `CERTIFICATION.MAIL_TEMPLATE_ID` is blank                                                                                                  | Set **Email Template**; nothing else in the notification path runs without it                                                                |
| A campaign has no exclusion rows at all                                                                              | It was opened directly rather than launched from a preview — see [Campaigns opened without a preview](#campaigns-opened-without-a-preview) | Expected. Launch from the **Preview** tab for anything you must evidence                                                                     |
| A preview is fine but nothing progresses after launch                                                                | The subsystem, not the campaign                                                                                                            | [Diagnose a stalled campaign](/administration-guide/governance/certification/diagnose-a-stalled-campaign.md)                                 |

## Preview statuses

The **Preview** tab reports one of eight values:

| Value           | Meaning                                              | Launchable |
| --------------- | ---------------------------------------------------- | ---------- |
| `PENDING`       | Accepted, preview id issued, compute not started     | no         |
| `RUNNING`       | Compute active, progress counter updating            | no         |
| `COMPLETED`     | Finished; results browsable and launchable           | **yes**    |
| `CANCELLED`     | Cancelled by an administrator before completion      | no         |
| `FAILED`        | Compute aborted; the tab shows the cause             | no         |
| `LAUNCHING`     | Promotion materializing the snapshot into a campaign | no         |
| `LAUNCHED`      | Promotion finished; campaign is open                 | no         |
| `LAUNCH_FAILED` | Promotion aborted; the tab shows the cause           | no         |

`CANCELLED`, `FAILED`, `LAUNCHED` and `LAUNCH_FAILED` are terminal. On reaching any of them OpenIAM drops the preview's OpenSearch snapshot and releases the certification's active-preview slot, so a fresh preview can be requested immediately — and so the **Scope**, **Reviewers** and **Excluded** views for that preview stop showing anything. `COMPLETED` is deliberately *not* terminal: it is finished but live, and its snapshot stays browsable. If you need the numbers from a launched preview after the fact, read them off the `CAMPAIGN` row, which keeps `EXCLUDED_USER_COUNT`, `EXCLUDED_ACCESS_COUNT` and `REVIEWERS_NOT_RESOLVED_COUNT`.

Two properties bound a preview's usefulness, both defaulting to 24 hours:

| Property                                              | Default | Effect                                                                                                                              |
| ----------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `org.openiam.certification.preview.age.warning.hours` | `24`    | Past this age the tab flags the preview as old — a soft warning, launch still allowed                                               |
| `org.openiam.certification.preview.freshness.hours`   | `24`    | Past this age launch is **hard-blocked**: *"Preview … is older than the 24h freshness window; re-run the preview before launching"* |

Both carry their defaults in the certification service itself, so they work without a `SYS_PROP` row — which also means you will not find them under **Administration → System Configuration**, and you cannot read their live values from the Webconsole. The observable form of the freshness setting is the refusal message itself, which names the window it enforced; if it says something other than 24h, your deployment has overridden the property, and the deployment owner can tell you to what.

## Every exclusion reason

Every exclusion the preview records carries a reason, and every reason belongs to exactly one of two stages. **Stage 1 (`SCOPE_FILTER`)** mirrors what launch does when selecting the population and entitlements. **Stage 2 (`REVIEWER_RESOLUTION`)** means the user survived scoping but no reviewer could be computed for an approval step. The **Review exclusions before launching** tiles on the **Preview** tab give you the counts by reason for one preview, which is the fastest way to see which of these you have actually hit; the bare value list is in [Campaign preview exclusion reasons](/administration-guide/reference/enums.md#campaign-preview-exclusion-reasons).

### Stage 1: `SCOPE_FILTER`

| Reason                                  | What it means                                                                                                                                                                                           | Usual fix                                                                                                                                                             |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `NO_APPROVER_ASSOCIATION`               | The certification has no reviewer step configured at all                                                                                                                                                | Add a step on the **Reviewers** tab                                                                                                                                   |
| `NO_SCOPE_MATCH`                        | No users matched the configured scope                                                                                                                                                                   | Wrong selection mode, wrong ids, or an extended search that resolves to nothing                                                                                       |
| `MEMBERSHIP_RISK_BELOW_THRESHOLD`       | This grant's membership risk score is below **Membership risk score**, so this one access item is not reviewed. A per-item drop — the user may still be reviewed for their remaining higher-risk access | Expected when a risk filter is set. Lower the threshold to widen the review                                                                                           |
| `ALL_MEMBERSHIPS_BELOW_RISK_THRESHOLD`  | Every one of the user's access items was dropped by the membership-risk filter, so nothing remained to review                                                                                           | Distinct from `ZERO_REMAINING_ENTITLEMENTS` on purpose: it means *all this user's access is low-risk*, so lower the threshold rather than hunting for a bad exclusion |
| `ROLE_EXCLUDED`                         | The role is in the configured exclusion list                                                                                                                                                            | Expected; remove it from `CERTIFICATION_ROLE_EXCLUDE` if unintended                                                                                                   |
| `GROUP_EXCLUDED`                        | The group is in the configured exclusion list                                                                                                                                                           | As above, `CERTIFICATION_GROUP_EXCLUDE`                                                                                                                               |
| `ZERO_REMAINING_ENTITLEMENTS`           | The user *did* have access, and the scope filter removed all of it                                                                                                                                      | Your entitlement filters are narrower than you think                                                                                                                  |
| `NO_ENTITLEMENTS`                       | The user had no roles or groups at all, before any filtering                                                                                                                                            | Nothing to review; distinct from the row above and reported separately                                                                                                |
| `ENTITLEMENT_START_FUTURE`              | The membership's start date is in the future                                                                                                                                                            | Expected — not yet effective                                                                                                                                          |
| `ENTITLEMENT_END_PAST`                  | The membership's end date has passed                                                                                                                                                                    | Expected — already lapsed                                                                                                                                             |
| `ENTITLEMENT_EXCLUDED_TAG`              | The membership carries a tag listed in **Membership Tags to Exclude**                                                                                                                                   | See [Membership tags](/administration-guide/governance/certification/membership-tags.md)                                                                              |
| `ENTITLEMENT_PENDING_MANUAL_ASSIGNMENT` | The membership is tagged `PENDING_MANUAL_ASSIGNMENT`                                                                                                                                                    | Fulfilment has not completed; review it next cycle                                                                                                                    |

{% hint style="info" %}
Two further Stage 1 reasons, `NO_RISK_SUBJECTS_MATCHED` and `USER_RISK_BELOW_THRESHOLD`, are retired. They belonged to the identity-risk population filter removed in 2026.9.1 and are never produced any more — they are kept only so exclusion rows and preview documents written before the change still load. If you see either, you are reading evidence from an older release.
{% endhint %}

### Stage 2: `REVIEWER_RESOLUTION`

| Reason                               | What it means                                                 | Usual fix                                                                    |
| ------------------------------------ | ------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `SUPERVISOR_NOT_FOUND`               | A `SUPERVISOR` step had no supervisor and no fallback applied | Populate the supervisor, or add a later step with a concrete `USER` reviewer |
| `ENTITLEMENT_OWNER_NOT_FOUND`        | An `ENTITLEMENT_OWNER` step resolved to nothing               | Set an owner on the role or group                                            |
| `ENTITLEMENT_ADMIN_NOT_FOUND`        | An `ENTITLEMENT_ADMIN` step resolved to nothing               | Set an admin on the role or group                                            |
| `APPLICATION_OWNER_NOT_FOUND`        | An `APPLICATION_OWNER` step resolved to null                  | Set an owner on the application resource                                     |
| `APPLICATION_ADMIN_NOT_FOUND`        | An `APPLICATION_ADMIN` step resolved to null                  | Set an admin on the application resource                                     |
| `GROUP_REVIEWER_EMPTY`               | A `GROUP` reviewer step pointed at a group with no members    | Populate the group, or point the step elsewhere                              |
| `ORGANIZATION_CERTIFIER_NOT_FOUND`   | The organization had no certifier                             | Set the certifier on the organization                                        |
| `USER_NOT_FOUND`                     | A named `USER` reviewer no longer exists                      | Replace the reviewer on the step                                             |
| `OWNER_OF_SERVICE_ACCOUNT_NOT_FOUND` | A `SERVICE_ACCOUNT_OWNER` step could not resolve an owner     | Assign an owner to the service account                                       |

At launch each reason is persisted on a `CAMPAIGN_PREVIEW_EXCLUSION` row alongside the grain of the exclusion (`USER` when the whole user was held out, `ACCESS` when one access item was dropped and the user proceeded) and its category (`USER_LEVEL`, `ACCESS_DROP` or `REVIEWER`). Those three columns together are your exclusion report, and they survive the preview snapshot being dropped.

## Campaigns opened without a preview

Every route into a campaign now goes through the preview. What used to be an execute button on the edit screen is labelled *"Go to preview"* and says *"Campaigns are now launched from a preview. You will be taken to the Preview tab to preview the campaign first, then launch it from there."* Scheduled runs moved the same way: the batch task behind a schedule, `accessCertificationInitializer.initializeAccessCertification`, starts a preview (`START_PREVIEW`) rather than opening a campaign, so **a scheduled certification produces a preview that waits for a human to promote it**. If you expected schedules to launch campaigns unattended, they no longer do. Confirm it the morning after the first scheduled run: the **Preview** tab holds a `COMPLETED` preview, there is no new `CAMPAIGN` row, and no `EXECUTE_CERTIFICATION` audit record exists for that certification.

You will still meet campaigns that were opened the older way, in a deployment that has been running for a while. They are recognisable, and they are weaker evidence:

* **No exclusion audit trail.** `CAMPAIGN_PREVIEW_EXCLUSION` is only written when a campaign is launched from a preview. A campaign opened without one has `PREVIEW_ID = NULL` and no exclusion rows, so you can never afterwards show *who was left out and why*.
* **No numbers before commitment.** Nobody saw the scale until after the mail had gone.

Treat a `CAMPAIGN` row with `PREVIEW_ID = NULL` as unevidenced for audit purposes, and re-run the review from a preview when the evidence matters.

## Related

* [Plan and scope a campaign](/administration-guide/governance/certification/plan-and-scope-a-campaign.md) — everything decided before the preview
* [Operate a live campaign](/administration-guide/governance/certification/operate-a-live-campaign.md) — what to do once it is live
* [The certification model](/administration-guide/governance/certification/certification-model.md) — the tables behind every object named here
* [Diagnose a stalled campaign](/administration-guide/governance/certification/diagnose-a-stalled-campaign.md) — when the preview or the launch stops moving
* [Enumerated values](/administration-guide/reference/enums.md) — preview statuses and every exclusion reason in the full catalogue

*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/preview-and-launch.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.
