> 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/campaign-history-and-evidence.md).

# Campaign history and evidence

Produce the certification evidence pack — the three report types, the 39 columns they all share, what they leave out, and the History tab that covers the gap.

## What this is, and when you need it

What a campaign is worth to an auditor is not the decisions but the ability to produce them: who was in scope, who was asked, what each reviewer decided, and when. Certification reporting is how that record leaves the product. Three CSVs cover it, and between them they answer the three questions someone reviewing your review will ask. Come here when the evidence has to exist outside the console.

These three are not BIRT reports and they are not built by the report designer — they are CSVs produced by `certification-manager` and mailed as attachments. If what you actually want is a scheduled, parameterised, formatted report, that is the BIRT catalogue instead: [Shipped reports](/administration-guide/reference/shipped-reports.md).

## At a glance

|                          |                                                                                                                                                                                        |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **You will end up with** | The scope and complete CSVs for a campaign on your disk, and a current-state CSV in your inbox for a campaign still running                                                            |
| **Where**                | The campaign's **Dashboard** tab → Reports section; the files live in `<confpath>/upload`                                                                                              |
| **You need first**       | The `CAMPAIGN_ID`; **Manager of Access Review** set on the definition, or the automatic reports are mailed to nobody; an email address on your own account for a current-state request |
| **Takes**                | Seconds for a download; minutes for a current-state report on a large campaign                                                                                                         |
| **Reversible**           | Yes — every action here is a read. Nothing about reporting changes campaign state                                                                                                      |

## Before you begin

Two limits are worth knowing before you promise an auditor a full picture:

* **Reports read `ACCESS_REVIEW_ITEM` only.** Excluded users and dropped access items live in `CAMPAIGN_PREVIEW_EXCLUSION` and are **not** rows in any of these CSVs. For exclusions use the **History** tab views on [Operate a live campaign](/administration-guide/governance/certification/operate-a-live-campaign.md).

  <div data-gb-custom-block data-tag="hint" data-style="warning" class="hint hint-warning"><p>The <strong>Complete report</strong> card describes itself as <em>"Full campaign result: decisions, reviewers, timestamps and exclusions."</em> The last word is wrong: the CSV is built from review items and carries no exclusion rows and no exclusion column. An auditor handed only this file is not seeing who was left out. Pair it with the <strong>History</strong> tab's <strong>Items excluded</strong> and <strong>Users excluded by scope</strong> views.</p></div>
* **`CURRENT_STATE_REPORT` goes to whoever asked for it, by email, and nowhere else.** Requested with no requester at all it is rejected with `INVALID_ARGUMENTS`; requested from the console there is always a requester, so what you actually have to check is that your own account has an email address.

## Produce the evidence pack

{% stepper %}
{% step %}

### Check what already exists

The **Reports** section on the campaign's **Dashboard** tab — *"Download available campaign reports or request a fresh snapshot."* — shows a card per report type, each marked **Available** or **Not available** with the date it was **Generated** and its size.

Availability is decided by looking for two exact file names under `<confpath>/upload`. OpenIAM does not list the directory and does not read the contents, so a report somebody renamed or moved by hand reads as never generated even though the evidence still exists on disk.
{% endstep %}

{% step %}

### Download the scope and complete reports

**Download** on the **Scope report** and **Complete report** cards. Both are files already on disk; the download hands you the existing file rather than building anything.

One wrinkle: the download is labelled as a spreadsheet even though the content is CSV, so your browser may offer it as `.xlsx`. Save it and open it as CSV — the file itself is unchanged.

Neither of these is something you trigger — both generate themselves, which is the next step.
{% endstep %}

{% step %}

### Understand which moment each snapshot was taken at

The report selection on a certification offers exactly three types:

| Value                  | Screen name          | Produced                                                      | File in `<confpath>/upload/`                                   | Delivered to           | Audit action                                  |
| ---------------------- | -------------------- | ------------------------------------------------------------- | -------------------------------------------------------------- | ---------------------- | --------------------------------------------- |
| `SCOPE_REPORT`         | Scope report         | Automatically at launch, once the campaign is fully populated | `certification_scope_report_<CAMPAIGN_ID>.csv`                 | Every campaign manager | `GENERATE_SCOPE_CERTIFICATION_REPORT`         |
| `RESULT_REPORT`        | Complete report      | Automatically when the last item settles                      | `certification_result_report_<CAMPAIGN_ID>.csv`                | Every campaign manager | `GENERATE_RESULT_CERTIFICATION_REPORT`        |
| `CURRENT_STATE_REPORT` | Current state report | On demand only                                                | `certification_report_<CAMPAIGN_ID>_<MM_dd_yyyy_HH_mm_ss>.csv` | The requester only     | `GENERATE_CURRENT_STATE_CERTIFICATION_REPORT` |

Those file names are fixed; nothing configures them.

{% hint style="info" %}
**All three reports have identical columns and identical row content.** The report type selects the file name, the audit action and the recipient — nothing about the data. What differs is the *moment* the snapshot is taken:

* **Scope** is taken at launch, so its decision columns are empty. It is the answer to *"who was in scope and who was asked?"*.
* **Complete** is taken when the campaign settles, so its decision columns are full. It is the answer to *"what did they decide?"*.
* **Current state** is taken whenever you ask, so it is a partial complete report. It is the answer to *"where are we right now?"*.

If you were expecting three different schemas — for example a scope report without reviewer decision columns — you will get the same 39 columns three times, with different fields populated.
{% endhint %}

**Scope** generates once the final launch batch is processed and the campaign is fully populated. Exactly one node in a cluster does the work, even when the last batch and the finalize message arrive together, so you will never see two scope reports for one campaign. **Complete** generates once the campaign's completed-item count equals its total, after the campaign row has been saved — whichever status the campaign then took.

{% hint style="info" %}
**The sign-off gate applies only to a campaign no human ever reviewed.** When every item was settled by the automation rules, nobody has attested to the result, so the campaign is held in `AWAITING_SIGN_OFF`. As soon as **one** item was decided by a human, that review *is* the attestation and the campaign closes straight to `COMPLETED`. Expiry takes precedence over both: any expired request gives `PARTIALLY_EXPIRED`, and all of them expiring gives `EXPIRED`.

So an ordinary campaign does not wait for sign-off, and the complete report is available either way. That matters because **nothing in the product completes a sign-off** — see [Operate certification services](/administration-guide/governance/certification/operate-certification-services.md).
{% endhint %}

Neither automatic report has a requester, which is why both route to the campaign managers instead. **A campaign with no&#x20;*****Manager of Access Review*****&#x20;configured generates both files on disk and mails them to nobody.** The audit record for the generation still appears, so a `GENERATE_SCOPE_CERTIFICATION_REPORT` event with no recipient complaint is not proof anyone received it — check the campaign's manager list.
{% endstep %}

{% step %}

### For a campaign still running, request a current-state report

**Request report** on the **Current state report** card, whose help text reads *"Generated on demand from the live campaign state. Delivered to your email when ready."* The report is built for whoever is signed in.

{% hint style="warning" %}
**The screen does not hand you this file.** The button returns as soon as the job is dispatched; the report is built in the background and arrives by email as an attachment on the requester's default email address. A user with no email address configured gets nothing, silently, and the timestamped current-state file is never offered for download the way the other two are. Confirm your account has an email address before you rely on this path.
{% endhint %}

The mail is not a `MAIL_TEMPLATE` render. It is a fixed HTML message with a subject of the form `<REPORT_TYPE> of campaign: <CAMPAIGN_NAME>` and the body *"Hello, this is automatically generated email. Please find report in the attachment. File was generated on "*. One shipped object looks like it should be involved and is not: the `MAIL_TEMPLATE` row `CERT_COMPLETE_REPORT` (name `CERTIFICATION_COMPLETE_REPORT`) exists, and nothing sends it.

**This is a limit, not a setting.** The report mail's wording cannot be changed from the Webconsole, and editing `CERT_COMPLETE_REPORT` has no effect — you can prove that by editing its subject line and requesting a report, which arrives with the wording above unchanged. If the wording has to change, it is a product change; raise it with support rather than looking for the template that governs it.
{% endstep %}

{% step %}

### Read the CSV, starting with `BULK_APPROVAL_RISK`

One row per `ACCESS_REVIEW_ITEM`, 39 columns, in this order:

| Group      | Columns                                                                                                                                                                 |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Request    | `REQUEST_NUMBER`                                                                                                                                                        |
| Subject    | `USERNAME`, `FIRST_NAME`, `LAST_NAME`, `SUPERVISOR_NAME`, `EMAIL`, `EMPLOYEE_ID`, `ORGANIZATION_STRUCTURE`, `TITLE`                                                     |
| Access     | `ACCESS_NAME`, `ACCESS_TYPE`, `APPLICATION_NAME`, `END_DATE_OF_ENTITLEMENT`, `ASSIGNMENT_DETAILS`, `ENTITLEMENT_NOTE`                                                   |
| Reviewer 1 | `FIRST_REVIEWER`, `FIRST_REVIEWER_EMAIL`, `DELEGATED_AT_FIRST_REVIEWER`, `ESCALATED_AT_FIRST_REVIEWER`, `DECISION_FIRST_REVIEWER`, `DATE_DECISION_FIRST_REVIEWER`       |
| Reviewer 2 | `SECOND_REVIEWER`, `SECOND_REVIEWER_EMAIL`, `DELEGATED_AT_SECOND_REVIEWER`, `ESCALATED_AT_SECOND_REVIEWER`, `DECISION_SECOND_REVIEWER`, `DATE_DECISION_SECOND_REVIEWER` |
| Reviewer 3 | `THIRD_REVIEWER`, `THIRD_REVIEWER_EMAIL`, `DELEGATED_AT_THIRD_REVIEWER`, `ESCALATED_AT_THIRD_REVIEWER`, `DECISION_THIRD_REVIEWER`, `DATE_DECISION_THIRD_REVIEWER`       |
| Campaign   | `CERTIFICATION_STATUS`, `CERTIFICATION_START_DATE`, `CERTIFICATION_COMPLETED_DATE`, `REQUEST_NOTE`                                                                      |
| Evidence   | `AUDIT_LOG`, `BULK_APPROVAL_RISK`                                                                                                                                       |

Four column behaviours matter when you read one:

**`BULK_APPROVAL_RISK`** is either `No` or the full sentence *"YES - reviewed via bulk approval (RISKY: items not reviewed individually)"*. This is the column an auditor should be shown first. It is derived from `ACCESS_REVIEW_ITEM_AUDIT.DECISION_SOURCE = 'BULK'`.

**Only three reviewer levels are represented.** The columns cover reviewer levels `0`, `1` and `2` and nothing above. A certification with a fourth reviewer step produces a CSV in which that step's reviewer, decision and dates appear nowhere — no error, no warning column, just absence. Count your reviewer steps against the three column groups before you hand the CSV over; keep certifications to three steps if the CSV is your evidence, and read `ACCESS_REVIEW_ITEM_AUDIT` for anything deeper.

**Decision values are literal strings**, not enum names: `Revoked`, `Approved`, `N/A`, or `N/A (access was already revoked)` where the entitlement had already gone from the target and reconciliation had reflected it.

**A row that could not be built is not silently dropped.** When one item cannot be rendered, OpenIAM writes an error-marker row for that item and carries on with the rest. A CSV with an odd-looking row is a data problem on that item, not a truncated report — the row count still matches the campaign's item count.
{% endstep %}
{% endstepper %}

## Verification

| Claim                        | Evidence                                                                                                                                                                                                                   |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A report was requested       | Audit `GENERATE_SCOPE_CERTIFICATION_REPORT`, `GENERATE_RESULT_CERTIFICATION_REPORT` or `GENERATE_CURRENT_STATE_CERTIFICATION_REPORT`, with info lines *"Type of report requested: …"*, *"Campaign: …"* and *"fileName: …"* |
| Chunks were dispatched       | Audit info *"Dispatched N report chunk(s) for job "*; service log *"Dispatching N chunk(s) for …"*                                                                                                                         |
| The file exists              | The **Scope report** or **Complete report** card reads **Available** with a generated date and a size, or the file is there as `<confpath>/upload/certification_{scope,result}_report_<CAMPAIGN_ID>.csv`                   |
| Content is complete          | Data row count equals `SELECT COUNT(*) FROM ACCESS_REVIEW_ITEM WHERE CAMPAIGN_ID = ?`                                                                                                                                      |
| Nothing was bulk-approved    | Every `BULK_APPROVAL_RISK` cell reads `No`                                                                                                                                                                                 |
| Exclusions are accounted for | `SELECT COUNT(*) FROM CAMPAIGN_PREVIEW_EXCLUSION WHERE CAMPAIGN_ID = ?` — those rows are **not** in the CSV                                                                                                                |

## When it goes wrong

| Symptom                                                           | Cause                                                                                                                                                         | Fix                                                                                                                                                                        |
| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Download** produced nothing                                     | The report has never been generated, or the file was renamed or moved on disk                                                                                 | Confirm the card reads **Available**; if it says **Not available** while a file exists, its name no longer matches `certification_{scope,result}_report_<CAMPAIGN_ID>.csv` |
| **Request report** reported success but no file appeared          | It is built in the background and delivered by email only; the timestamped file is never offered for download                                                 | Check the requester's default email address, then the mail service log                                                                                                     |
| Scope and complete reports exist on disk but nobody received them | Automatic reports route to the campaign's managers, and the campaign has none                                                                                 | Set **Manager of Access Review** on the **Configurations** tab, and download the existing files directly                                                                   |
| A current-state request fails with `INVALID_ARGUMENTS`            | The request carried no requester, so there is nobody to mail it to. **Request report** always supplies one, so this points at something other than the screen | Request it from the **Reports** section while signed in                                                                                                                    |
| A multi-node install produces a truncated or failed report        | `<confpath>/upload` is node-local, so the assembling node cannot see other nodes' `.parts` files                                                              | Ask the deployment owner to make `<confpath>/upload` shared storage — [Installation Guide](/installation/installation.md)                                                  |
| The report generated but is header-only                           | The campaign has zero `ACCESS_REVIEW_ITEM` rows — deliberate, so the card still reads **Available** rather than failing                                       | Check the preview's **Review items — In scope**; the scope excluded everything                                                                                             |
| A fourth reviewer step is missing from the CSV                    | The CSV has column groups for reviewer levels 0–2 only, and nothing above them is written                                                                     | Keep to three reviewer steps, or read `ACCESS_REVIEW_ITEM_AUDIT` directly                                                                                                  |
| One row is an error marker                                        | Row building is defensive per item so one bad item cannot abort the report                                                                                    | Find that item in the service log — *"Failed to build report row for access review item …"* — and inspect its data                                                         |
| `<confpath>/upload` fills the disk                                | `CLEANUP_CSV` ships disabled                                                                                                                                  | Enable it (cron `0 0 1 * * ?`)                                                                                                                                             |
| Editing `CERT_COMPLETE_REPORT` changed no email                   | Nothing sends that template; the report mail's wording is fixed                                                                                               | Accept the wording, or raise a product request — no configuration changes it                                                                                               |
| A report says an item is `N/A (access was already revoked)`       | The entitlement had already gone from the target and reconciliation reflected it before the decision                                                          | Informational — the decision was recorded, no second revocation was attempted                                                                                              |
| A report never appears at all, on any install                     | The chunk queue has no consumer                                                                                                                               | See [Diagnose a stalled campaign](/administration-guide/governance/certification/diagnose-a-stalled-campaign.md)                                                           |

## How the build works

Report generation is distributed, because a large campaign will not fit in one node's memory:

1. Count the campaign's review items and split them into pages of 5,000.
2. Create the `<confpath>/upload` directory if it is absent — a fresh deployment may not have it, and nothing else creates it.
3. Write job metadata, a countdown counter and a set of already-built page numbers into Redis, all with a 24-hour safety-net expiry.
4. Dispatch one message per page onto the report-chunk queue, so any cluster node can build a page into a headerless part file under `<confpath>/upload/.parts`.
5. A node that finds its page already in that set treats the message as a redelivery and skips it, so a requeued message cannot duplicate a page; a page that fails clears its marker and can be retried.
6. The node that takes the counter to zero assembles the part files into the final CSV and mails it.

Two operational consequences. First, `<confpath>/upload` must be the **same storage** on every node that builds report chunks, or the assembling node cannot find the parts and the report fails or truncates; whether it is depends on how the deployment mounts it, which is the deployment owner's question and is covered by the [Installation Guide](/installation/installation.md). The symptom on your side is a report that never appears, or appears short, on a multi-node install while working on a single node.

Second, the `CLEANUP_CSV` batch task (bean `cleanOldCSVInitializer.start`, cron `0 0 1 * * ?`) exists to age those files out, but **ships disabled** — so `<confpath>/upload` grows without bound until you enable it. Confirm which state yours is in on **Administration → Batch Tasks**: find `CLEANUP_CSV` and read the enabled column. On a default installation it is off, and no CSV has ever been deleted. See [Shipped batch tasks](/administration-guide/reference/shipped-batch-tasks.md).

## The BIRT report that is not this

`ACCESS_CERTIFICATION_REPORT` (report id `118`) is a `REPORT_INFO` row in the BIRT catalogue, and it is also the report type name behind **Request report**. They are not the same artifact: a current-state request renders no `.rptdesign` at all — it produces the CSV described above. So editing the `118` design changes nothing about what **Request report** delivers.

Two further certification-adjacent BIRT reports ship, `EMPLOYEE_CERT_REPORT` (`105`) and `RESOURCE_CERT_REPORT` (`106`); both are separate from campaign reporting. The full inventory is in [Shipped reports](/administration-guide/reference/shipped-reports.md).

## Related

* [Shipped reports](/administration-guide/reference/shipped-reports.md) — the BIRT catalogue, including `ACCESS_CERTIFICATION_REPORT` (`118`)
* [Operate a live campaign](/administration-guide/governance/certification/operate-a-live-campaign.md) — the campaign-history views that do include exclusions
* [Configure the review workflow](/administration-guide/governance/certification/configure-the-review-workflow.md) — why `BULK_APPROVAL_RISK` may be populated
* [Shipped batch tasks](/administration-guide/reference/shipped-batch-tasks.md) — `CLEANUP_CSV` and the rest

*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/campaign-history-and-evidence.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.
