---
title: "Campaign preview"
metaTitle: "Campaign preview"
metaDescription: "Describes the dry-run preview of a certification campaign that reports who would be reviewed, by whom, and what needs attention — and is then promoted into the live campaign without recomputing."
---

When you launch a certification campaign, OpenIAM resolves the campaign **scope** (which users and which of their entitlements should be reviewed) and the **reviewers** (who must review each item). Historically you only discovered the result of those calculations *after* launching the campaign — at which point users or entitlements may have been silently dropped, and some reviewers may not have been resolvable.

**Campaign preview** runs exactly the scope and reviewer calculation that a launch would, but writes nothing permanent to the campaign: no campaign, no access review items, and no review workflow tasks are created while you are previewing. It lets you validate a certification configuration and fix problems *before* committing to a live campaign — and, once you are satisfied, the campaign is launched **directly from the preview snapshot**, so what you previewed is exactly what launches.

<div style="border:1px solid #169998; margin-top: 15px; margin-bottom: 15px; padding-top: 10px; padding-bottom: 10px; padding-left: 5px; padding-right: 5px;"><span style="color: #169998; font-weight: bold;">Note:</span> A preview never starts the review workflow. The Activiti review workflow is only ever invoked when you promote a preview to a live campaign (launch from preview). Both the preview and the launch use the <b>same</b> reviewer-resolution engine inside the <code>certification-manager</code> service, so the reviewers you see in the preview are the reviewers the launched campaign assigns.</div>

## Using campaign preview in the console

The certification console presents campaigns across tabs, including a **Preview** tab and a **History** tab.

### 1. Request a preview

On the **Preview** tab, set **Preview validity (hours)** — how long the preview's results may still be promoted to a live campaign before they are considered stale (default **24**) — and click **Request preview**. This starts the dry-run review described below: OpenIAM resolves the campaign scope and reviewers in the background and shows a live progress indicator while it runs. No campaign is created at this stage.

If you navigate away and return while a preview is still valid, the console re-attaches to that **active preview** rather than starting a new one, so you keep your in-flight (or completed) results.

![Requesting a preview from the Preview tab](img/UAR-campaign-preview-request.png)

### 2. Review the results

When the preview completes, the tab shows the aggregate metric cards and three result tabs — **Scope**, **Reviewers**, and **Excluded** (see [Reading the preview](#reading-the-preview)). Use the **Excluded** tab and its **Review exclusions** panel to find and fix configuration or data problems, then click **Request preview** again to re-run until the campaign looks right. The card beside the results shows how long ago the preview ran and how much of its validity window is left.

![Reviewing preview results — Scope, Reviewers and Excluded](img/UAR-campaign-preview-results.png)

### 3. Promote to a live campaign

When you are satisfied, click **Launch campaign** to **promote** the preview. OpenIAM materializes the live campaign directly from the previewed snapshot (no recompute), shows the launch progress, and starts the review workflow. Promotion is only allowed while the preview is still within its validity window (see [Launching from a preview](#launching-from-a-preview)).

![Promoting a preview to a live campaign](img/UAR-campaign-preview-promote.png)

### 4. Track it in History

Once promoted, the campaign moves to the **History** tab, where you can track the launched campaign alongside previously run campaigns.

![The launched campaign on the History tab](img/UAR-campaign-history-tab.png)

## How it works

A preview is **ephemeral**, **asynchronous**, and **snapshot-consistent**:

* **Ephemeral** — preview results are never written to the `certification` database. Progress is held in Redis and the computed results are stored in OpenSearch.
* **Asynchronous** — when you start a preview you get a `previewId` back immediately. The scope and reviewer calculation runs in the background while you watch a live progress counter (`processedUsers` / `totalUsers`). The compute runs as **two sequential phases**, each reporting its own progress so the bar refills instead of stalling at 100% partway through: **Scanning users** (page through the in-scope population, classifying each user as included or excluded) followed by **Building results** (resolve reviewers for every included user, build the review-item documents, and persist the snapshot). The Preview tab relabels the progress bar as it moves between the two phases.
* **Snapshot-consistent** — the entire preview reflects a single point-in-time read of identity data, so the metrics, the in-scope lists, and the excluded list all agree with one another. That same snapshot is what promotion materializes into the live campaign.

There is **one active preview per certification** — starting a new preview cancels any preview already running for that certification.

### The two simulation stages

A preview mirrors the two stages a real launch performs.

**Stage 1 — scope filtering.** Determines which users and entitlements survive into the campaign. A user or entitlement can be dropped for any of the following reasons:

| Reason | Meaning |
|---|---|
| No approver association | The certification has no reviewer rule configured. |
| No scope match | No users match the configured scope. |
| No risk events | A risk-event-driven certification found no qualifying risk events. |
| Role excluded | The role is explicitly excluded from the certification. |
| Group excluded | The group is explicitly excluded from the certification. |
| Zero remaining entitlements | After filtering, the user has no roles or groups left to review. |
| Entitlement start in future | The entitlement's start date is in the future. |
| Entitlement end in past | The entitlement's end date has already passed. |
| Entitlement has excluded tag | The entitlement carries a membership tag configured as excluded. |
| Entitlement pending manual assignment | The entitlement is tagged `PENDING_MANUAL_ASSIGNMENT`. |

**Stage 2 — reviewer resolution.** For each surviving user&times;entitlement item, the resolver resolves the configured reviewer rules to concrete reviewer users. A step can fail to resolve for any of these reasons:

| Reason | Meaning |
|---|---|
| Supervisor not found | A `SUPERVISOR` reviewer could not be resolved and no fallback applied. |
| Entitlement owner / admin not found | An `ENTITLEMENT_OWNER` / admin reviewer could not be resolved (the role/group has no owner/admin). |
| Application owner / admin not found | An `APPLICATION_OWNER` / admin reviewer could not be resolved (the backing resource has no owner/admin). |
| Group reviewer empty | A `GROUP` reviewer rule resolves to a group that has no members. |
| Organization certifier not found | An organization-certifier reviewer could not be resolved for the user's organization. |
| Owner of service account not found | The owner of a service account could not be resolved. |
| User not found | A directly named reviewer user could not be resolved. |

Every problem the preview finds names the exact offending entity (the role, group, resource, or user to fix) and the precise issue, so you can act on it directly.

## Reading the preview

### Aggregate metrics

The metric cards at the top of the preview screen summarize the run:

1. **Users in campaign** — distinct users that survived Stage 1 and will be reviewed.
2. **Review items** — total number of user&times;entitlement rows that will be reviewed, with the average number of items per included user.
3. **Reviewers not calculated** — number of Stage 2 reviewer-resolution failures.
4. **Excluded access items** — number of user&times;entitlement rows dropped during Stage 1.
5. **Users dropped — no entitlements** — number of users dropped because they had zero remaining roles or groups after filtering.

### Scope tab

The **Scope** tab shows everything that *will* be reviewed, with two switchable views:

* **By user** — one row per in-scope user, the number of access review items they carry, and the distinct reviewer(s) resolved across that access. Click the access-count chip to drill into that user's individual items.
* **By access item** — the reverse roll-up: one row per in-scope entitlement (role or group), the number of users it covers, and the distinct reviewer(s) resolved across it.

### Reviewers tab

The **Reviewers** tab rolls the run up by reviewer: one row per resolved reviewer at a given approval **step**, with the number of distinct in-scope users that reviewer covers at that step. Use it to confirm reviewer load is distributed as you expect before launching.

### Excluded tab

The **Excluded** tab is the heart of the **fix-and-re-run loop** — it lists everything the preview dropped so you can correct the underlying data and re-run. A **Review exclusions** panel summarizes the drops as clickable cards that narrow the list:

* **Needs attention (all)** — every **genuinely excluded** user: one that was dropped from the campaign entirely by Stage 1 scope filtering, or that is held back by an unresolved reviewer in Stage 2. A user who keeps reviewable access and merely lost a single entitlement is *not* counted here (see the note below).
* **Reviewer unresolved** — users who *have* surviving access but at least one approval step could not resolve a reviewer; such a user is held out of the clean in-scope set until you fix the cause (assign an owner/admin to the role/group/resource, or a supervisor to the user).
* **Access filtered out** — users whose access was all filtered out in Stage 1 (e.g. zero remaining entitlements).

The tab itself offers two views:

* **Users** — one row per **genuinely excluded** user, with the reason(s) and any per-entitlement detail. This view lists only users dropped from the campaign — either filtered out entirely in Stage 1, or held back by an unresolved reviewer in Stage 2. It does **not** list a user who stays in the campaign with reviewable access and merely had one of several entitlements dropped.
* **Access items** — the flat list of **every** dropped user&times;entitlement pairing, each with a display-ready explanation (which tag, what date) so you can act without drilling into the user. This includes entitlements dropped from users who otherwise remain in the campaign, so a single dropped entitlement always appears here even when its user is not on the **Users** view.

<div style="border:1px solid #169998; margin-top: 15px; margin-bottom: 15px; padding-top: 10px; padding-bottom: 10px; padding-left: 5px; padding-right: 5px;"><span style="color: #169998; font-weight: bold;">Users vs. access items:</span> A user that keeps reviewable access but loses one entitlement is still in the campaign — that user appears on the <b>Scope</b> tab (for the clean access) and the dropped entitlement appears on the Excluded <b>Access items</b> view, but the user is <b>not</b> on the Excluded <b>Users</b> view. The <b>Users</b> view is reserved for users actually excluded from the campaign. Fix the underlying data, re-run the preview, and confirm the user or access has moved into scope.</div>

## Launching from a preview

When you promote a completed preview, OpenIAM builds the live campaign **directly from the preview snapshot** — it does **not** recompute scope or reviewers. The reviewers, the items, and the per-user identity captured at preview time are exactly what the campaign is created with, so "what you preview is what launches".

* **No recompute, no double work.** Reviewer resolution happens once, during the preview; the launch reuses it and only performs the launch-time step assembly (review-step ordering, self-review reroute, escalation) before starting the Activiti review workflow.
* **Validity window.** Because a snapshot can drift from current identity data, a completed preview can only be launched while it is still within its validity window. Each preview carries the **Preview validity (hours)** you chose when you requested it (falling back to the server default when left unset — see [Configuration](#configuration)); a preview launched past that window is **rejected** and must be re-run. Only a `COMPLETED` preview can be launched. The Preview tab shows the time remaining and flags an age warning as the window runs down.
* **Asynchronous, with progress.** Promotion runs in the background and reports through the same status channel and progress bar as the preview compute: the status moves `COMPLETED` &rarr; `LAUNCHING` &rarr; `LAUNCHED` (or `LAUNCH_FAILED`), and the progress counter ticks as users are materialized into the campaign.

<div style="border:1px solid #169998; margin-top: 15px; margin-bottom: 15px; padding-top: 10px; padding-bottom: 10px; padding-left: 5px; padding-right: 5px;"><span style="color: #169998; font-weight: bold;">No automatic launch:</span> certifications are never launched automatically. A <b>scheduled</b> certification generates a preview (it does not open a campaign); an administrator reviews that preview and then promotes it. The only way to open a campaign is to launch from a preview.</div>

## Service operations

The preview feature is exposed through the `certification-manager` service over RabbitMQ:

| Operation | Purpose |
|---|---|
| `START_PREVIEW` | Begin an asynchronous preview for a certification configuration, with an optional per-preview validity window. Returns a `previewId` immediately. Also fired by a scheduled certification. |
| `GET_PREVIEW_STATUS` | Fetch the live status and aggregate metrics for a `previewId`. Serves live progress while a preview is `RUNNING` and while promotion is `LAUNCHING`; serves the final metrics when complete. |
| `GET_ACTIVE_PREVIEW` | Fetch the status of the certification's currently active preview, so a client that lost its `previewId` can re-attach instead of starting a new one. |
| `GET_PREVIEW_ITEMS` | Page through the individual in-scope user&times;entitlement items. |
| `GET_PREVIEW_SCOPE_BY_USER` | Page through the Scope "By user" roll-up. |
| `GET_PREVIEW_SCOPE_BY_ACCESS` | Page through the Scope "By access item" roll-up. |
| `GET_PREVIEW_REVIEWERS` | Page through the Reviewers roll-up. |
| `GET_PREVIEW_EXCLUDED_USERS` | Page through the excluded users. |
| `GET_PREVIEW_EXCLUDED_ACCESS` | Page through the flattened excluded access items. |
| `GET_PREVIEW_EXCLUSION_FACETS` | Fetch the pre-aggregated exclusion reason counts and filter options for the Review exclusions panel. |
| `GET_PREVIEW_FILTER_OPTIONS` | Fetch the distinct values for a result-grid column so the console can offer a typeahead/dropdown filter on it. |
| `CANCEL_PREVIEW` | Cancel a running preview and delete its ephemeral data. |
| `LAUNCH_FROM_PREVIEW` | Promote a fresh, completed preview to a live campaign, materializing it from the snapshot. **This is the only operation that starts the review workflow.** |

## Ephemeral storage and retention

* **Progress** is tracked in **Redis** so the live `processedUsers` / `totalUsers` counter and the lifecycle status (`PENDING`, `RUNNING`, `COMPLETED`, `CANCELLED`, `FAILED`, and the promotion states `LAUNCHING`, `LAUNCHED`, `LAUNCH_FAILED`) are available immediately.
* **Results** — the aggregate metadata document, the per-row items, and the per-user identity snapshot — are stored in **OpenSearch** in dedicated preview indices.
* **Retention** — preview data is short-lived. The moment a preview reaches a **terminal state** — `CANCELLED`, `FAILED`, `LAUNCHED`, or `LAUNCH_FAILED` — its OpenSearch snapshot is **dropped immediately** and the certification's active-preview slot is released, so you can request a fresh preview right away. Only the lightweight live status (and message) is kept in Redis so a polling admin still sees the outcome; it is swept after a **48-hour TTL**. A preview left untouched in a non-terminal state (e.g. a `COMPLETED` snapshot that is never launched) is likewise reaped by the 48-hour TTL.

## Configuration

| Property | Default | Purpose |
|---|---|---|
| `org.openiam.certification.preview.freshness.hours` | `24` | Default maximum age of a completed preview that may still be launched, used when a preview is requested **without** an explicit **Preview validity (hours)**. A per-preview validity always takes precedence over this default. |
| `org.openiam.certification.preview.age.warning.hours` | `24` | Age past which a completed preview's status carries an age-warning flag in the UI. |
| `org.openiam.certification.preview.executor.core.pool.size` | `2` | Steady-state worker threads kept alive for preview compute. |
| `org.openiam.certification.preview.executor.max.pool.size` | `4` | Hard ceiling on concurrent preview computes. |
| `org.openiam.certification.preview.executor.queue.capacity` | `50` | Bounded work-queue capacity before the caller-runs fallback applies. |

In short: choose a validity window, use the preview to validate scope and reviewers and fix configuration problems through the fix-and-re-run loop, then promote it — the launch reuses the previewed snapshot (no recompute) and only then starts the review workflow.
