> 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/operations/find-audit-evidence.md).

# Find audit evidence

Find the audit event for one specific change — where events live, how to search them, how far the trail goes back, and what it will never tell you.

## What this is, and when you need it

Two things are commonly assumed about OpenIAM's audit log and both are wrong. It is not in the database, and nothing is deleting it — events accumulate indefinitely until you configure a limit. This page is how you find the event for one change, and what the trail can and cannot prove.

The action catalogue — **537** constants, grouped by the object each one targets — and the retention properties are in [Audit actions and retention](/administration-guide/reference/audit-actions-and-retention.md). Streaming events to an external system is [Audit export to a SIEM](/administration-guide/operations/audit-and-export.md).

## At a glance

|                          |                                                                                                                                                                                                                           |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **You will end up with** | The event for that one change open at `/webconsole/auditLog?id=<AUDIT_LOG_ID>`, with its `PREV_VALUE` / `NEW_VALUE` or its `FAILURE_REASON` attribute, and its children. Nothing is changed; searching audit is read-only |
| **Where**                | **Administration → Log Viewer**, `/webconsole/viewAuditLog`; one event opens at `/webconsole/auditLog?id=<AUDIT_LOG_ID>`                                                                                                  |
| **You need first**       | The action constant you are looking for, the object it acted on, the day you believe it happened, and a Webconsole account that can open `/webconsole/viewAuditLog`                                                       |
| **Takes**                | 5–15 minutes once you know the action constant; longer if you have to widen the window                                                                                                                                    |
| **Reversible**           | Yes — nothing is written, so there is nothing to undo. The only cost you cannot take back is the load a wide search puts on live traffic                                                                                  |

## Before you begin

Before anything else: **a `Save` confirmation is not evidence.** The Webconsole reports success as soon as the business operation returns, and audit writing happens afterwards, on a path that catches and swallows its own failures. A screen that said the change was saved tells you the change was saved; it tells you nothing about whether an audit event exists for it. The only evidence that an event exists is the event — found by search, or in the `auditlog` index, or arriving at your SIEM. Everything below is how to find it.

## Find the evidence for one change

{% stepper %}
{% step %}

### Name the action before you search

Look the constant up in [Audit actions and retention](/administration-guide/reference/audit-actions-and-retention.md), grouped by the kind of object each action targets — which mirrors the target filters on this screen. `MODIFY_USER`, not "user was edited"; `MODIFY_MANAGED_SYSTEM`, not "the connector changed". A search that leaves **Select an Action** empty on an installation that has never pruned reads the whole index.

Eighty-nine of the actions declare no target object — they record changes to OpenIAM's own configuration rather than to a user or a role — and no target filter reaches them. For those, [search by action name and date alone](/administration-guide/reference/audit-actions-and-retention.md#actions-that-declare-no-target-object).
{% endstep %}

{% step %}

### Search on the target, not the requester

If you know *what* changed but not *who* changed it, use **Add More Search Criteria** to reveal the field for the object — **Target User**, **Role**, **Group**, **Organization**, **Select a Managed System** — and leave **Requestor** empty. Searching by requester when you do not know the requester is how people conclude there is no audit trail.
{% endstep %}

{% step %}

### Narrow the window honestly, then widen once

**From Date** and **To Date** are the only filters that reduce the index scan as much as narrowing by action does. Start with the day you believe it happened; widen once if you are wrong, rather than searching a year.

{% hint style="warning" %}
On an installation that has never been pruned the `auditlog` index holds every event ever written, so a wide search competes with live traffic. Narrow by action and by day before you widen anything.
{% endhint %}
{% endstep %}

{% step %}

### Follow the parent audit log id

This is the step people miss. An OpenIAM operation is a *tree* of audit events, linked by a parent id that is carried from service to service, and the search returns parents only unless you say otherwise — which is what you want for "find the operation". Once you have the parent event, tick **Include child records in search** to see the layers the parent only summarises: the provisioning stream, the connector response, the workflow step. That is the whole operation.
{% endstep %}

{% step %}

### Read the attributes, not the summary line

For eleven user-changing actions the detail screen renders `PREV_VALUE` and `NEW_VALUE` as a before/after view: `CREATE_USER`, `MODIFY_USER`, `USER_REMOVE`, `USER_DISABLE`, `USER_ENABLE`, `USER_DELETE`, `USER_ACTIVE`, `USER_ACTIVATE`, `CHANGE_PASSWORD`, `USER_RESET_SECURITY_QUESTIONS` and `USER_RESET_ACCOUNT`. Those eleven are the actions where "what exactly changed" is answerable from the audit log alone. For everything else the answer is in the `attributes` list — `FAILURE_REASON`, `EXCEPTION`, `PROVISION_STREAM`, `TOTAL_COUNT`/`FAIL_COUNT`/`SKIPPED_COUNT` for a sync — and in the `requestBody` where a controller recorded one.
{% endstep %}
{% endstepper %}

## Verification

| Evidence                                                            | Meaning                                                                                     |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| The event at `/webconsole/auditLog?id=<id>`                         | The event was written to the `auditlog` index                                               |
| Its children, found by parent id                                    | The operation's own steps — the provisioning stream, the connector reply, the workflow step |
| A `START_USER_VERIFICATION` event carrying `NUM_DELETED_AUDIT_LOGS` | The prune task ran and deleted that many documents                                          |

## When it goes wrong

| Symptom                                                 | Inspect                                                                   | Fix                                                                                                                                  |
| ------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| Audit search returns nothing for a period               | Whether the `auditlog` index is healthy, then whether pruning was enabled | Unlike other indexes, `auditlog` cannot be reindexed from the database. Check `org.openiam.audit.log.prune.days*` and the batch task |
| An operation succeeded but no audit event exists        | `audit-log-manager` log for *"Can't save audit log"*                      | Audit writes never fail the operation they record. Fix OpenSearch; the event is gone                                                 |
| Direct OpenSearch query for root events returns nothing | The `parentId` field                                                      | Absence of a parent is the literal string `"null"`. Query `term parentId "null"`, not `must_not exists`                              |
| A long audit attribute is cut off                       | The stored value's length against the source value's                      | 32,766 characters is the ceiling, and over-length values are silently truncated. Not recoverable                                     |

## What the search screen can and cannot filter on

The screen is `/webconsole/viewAuditLog` and one event opens at `/webconsole/auditLog?id=<AUDIT_LOG_ID>`. The filters are **From Date**, **To Date**, **Select an Action**, **Result**, **Requestor**, **Requestor ID**, **Target User**, **Role**, **Group**, **Select a Managed System**, **Organization**, **Client IP** and **Request ID**, plus the **Include child records in search** checkbox — several of them appear only once you pick them from **Add More Search Criteria**, so a filter you cannot see is usually there rather than absent. Results always come back newest first, and there is no control that changes the sort.

Three things about that set are worth knowing before you widen a search.

**Narrowing by action is what makes a search fast.** It and the date window are the only two filters that reduce how much of the index is read. Everything else narrows the result, not the scan.

**Child events are excluded until you ask for them.** **Include child records in search** ships unticked, so a default search is a search over *operations*. Tick it and you are searching *steps* — which is where a failure usually is, one level below a parent that reports success.

**Two useful filters have no field on the screen.** A single synchronization run cannot be isolated from this screen — the run's own report on the synchronization screen is where that lives — and there is no filter for the authentication provider on an OAuth or federation event. Both exist in the product and neither is offered here; if you need them, say so to OpenIAM support rather than assuming you have missed a control.

## Where audit events live

Audit events are OpenSearch documents, not rows.

| OpenSearch index    | Document type an export transformer receives | Holds                          |
| ------------------- | -------------------------------------------- | ------------------------------ |
| `auditlog`          | `IdmAuditLogDoc`                             | The events themselves          |
| `auditlogattribute` | `AuditLogAttributeTupleDoc`                  | The key/value attribute tuples |
| `auditlogtargets`   | — none in this release                       | Declared, and unused           |

The observable check is the index list itself: query your OpenSearch cluster's `_cat/indices` and you will find `auditlog` there with a growing document count, and no audit table anywhere in the OpenIAM schemas. The tables that once held audit events were dropped in 4.2.0.0, and storing audit records in the database is not supported on 4.x at all. Confirm with a break-glass `SHOW TABLES LIKE '%LOG%'` against the `openiam` schema if you want it in writing; the result is empty.

{% hint style="danger" %}
**The `auditlog` database schema does not contain the audit log.** It holds exactly four tables — `AUDIT_EXPORT_CONNECTOR`, `AUDIT_EXPORT_CONNECTOR_ACTION`, `AUDIT_EXPORT_FAILED_DISPATCH` and `OPENIAM_VERSION` — the first three added in 2026.3.1.0 and 2026.3.3.0. The schema's name refers to the feature area, not to the events. `SHOW TABLES` against that schema is the check, and it takes a second.

Two consequences. A database backup that covers all seven schemas still does not preserve one audit event — see [What a backup and restore must cover](/administration-guide/platform/backup-and-restore-scope.md). And a reindex cannot repair a lost `auditlog` index, because there is no source to reindex from: unlike `users` or `role`, the audit indexes are the system of record. If audit retention is an obligation, snapshot OpenSearch or export continuously.
{% endhint %}

## The data model

These are the fields on an audit event, all of them queryable — both from the Webconsole's own search and from a direct OpenSearch query, and all of them visible to an export transformer:

| Field                                                                                                                              | ES type                 | Notes                                                                                                                                                                                                                                                                                                      |
| ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                                                                                                                               | keyword                 | The audit log id you will pass to the detail screen                                                                                                                                                                                                                                                        |
| `parentId`                                                                                                                         | keyword                 | **Defaults to the literal string `"null"`**, not to a null value — see below                                                                                                                                                                                                                               |
| `userId`, `principal`                                                                                                              | keyword                 | The *requester*                                                                                                                                                                                                                                                                                            |
| `timestamp`                                                                                                                        | date (`date_time`)      | Set by `audit-log-manager` on save if the caller left it unset                                                                                                                                                                                                                                             |
| `action`, `result`                                                                                                                 | keyword                 | The action constant and `SUCCESS` / `FAILURE`                                                                                                                                                                                                                                                              |
| `source`                                                                                                                           | keyword                 | An `AuditSource` value — `WEBCONSOLE`, `SELFSERVICE`, `SELFSERVICE_EXT`, `WORKFLOW`, `SOURCE_ADAPTER`, `RECONCILIATION`, `SYNCHRONIZATION`, `IDP`, `SP`, `ESB`, `PROXY`, `EXTERNAL_APPLICATION`, `IMPORT_FROM_EXT_LOG`, `EMAIL_MANAGER`, `CONNECTOR`                                                       |
| `clientIP`, `nodeIP`, `sessionID`, `correlationId`                                                                                 | keyword                 | `nodeIP` is the `DOCKER_HOST_IP` environment variable if set, otherwise the canonical host name                                                                                                                                                                                                            |
| `authProviderId`, `contentProviderId`, `uriPatternId`, `userVerificationProviderId`, `languageId`                                  | keyword                 | The authentication context                                                                                                                                                                                                                                                                                 |
| `attributes`                                                                                                                       | nested                  | Key/value tuples. Names come from `AuditAttributeName` — 90-odd constants including `PREV_VALUE`, `NEW_VALUE`, `FAILURE_REASON`, `SUCCESS_REASON`, `EXCEPTION`, `PROVISION_STREAM`, `DEPROVISION_STREAM`, `OTP_TYPE`, `REQUEST_ID`, `NUM_DELETED_AUDIT_LOGS`, `TOTAL_COUNT`, `FAIL_COUNT`, `SKIPPED_COUNT` |
| `targetUsers`, `targetRoles`, `targetGroups`, `targetResources`, `targetOrganizations`, `targetManagedSystems`, `targetAttributes` | nested                  | The objects acted **on**, each as key/value tuples                                                                                                                                                                                                                                                         |
| `uri`, `url`, `method`, `urlParameters`, `pathVariables`                                                                           | text / keyword / nested | The HTTP call, where there was one                                                                                                                                                                                                                                                                         |
| `requestBody`, `responseBody`, `responseCode`                                                                                      | text / keyword          | Populated only for the calls OpenIAM has annotated for auditing. Redaction of secrets is per-call, not blanket — do not assume a field is scrubbed because another one is                                                                                                                                  |
| `deviceInfo`                                                                                                                       | text                    |                                                                                                                                                                                                                                                                                                            |

Two quirks in that list are worth internalising.

**`parentId` defaults to the string `"null"`.** An event with no parent does not have an absent `parentId`; it has a `parentId` whose value is the four characters `null`. The reason is historical — the query layer could not express an "exists" test when the index was designed — but the consequence is live: if you query OpenSearch directly, a `must_not exists` on `parentId` matches **nothing**. Test `parentId` against the literal string instead. Run both queries against your own `auditlog` index and compare the hit counts; that is the check.

**Attribute values are truncated at 32,766 characters.** The field is an OpenSearch `keyword`, and its type cannot be widened without invalidating data already indexed, so an over-length value is silently trimmed rather than rejected. A very large SAML assertion or provisioning stream stored in an audit attribute is cut. Nothing on screen tells you it was cut — compare the stored value's length against the source if the exact bytes matter.

**A failed audit write never fails the operation it was recording.** OpenIAM stamps `timestamp` and `nodeIP`, writes to OpenSearch, then hands the same documents to the export service — and it catches and logs failures at both steps rather than propagating them, with `Can't save audit log` for the first and `Could not dispatch to audit export connectors` for the second. Both lines appear in the `audit-log-manager` log. That is the right trade-off for availability and the wrong one for assurance: if the absence of an event would matter to you, alert on those two log lines, because no screen in the product will report them.

## The parent/child tree

An OpenIAM operation is a *tree* of audit events, not one event. The parent id travels from service to service with the work, and the child events carry the layers the parent only summarises — the provisioning stream, the connector response, the workflow step, the mail send.

That means two things for searching. A search that shows only parents is a search over *operations*; a search that includes children is a search over *steps*. And a failure often lives one level below a parent that reports success, because the parent completed its own work before the asynchronous child failed.

## Retention: what the shipped state means for your search

Nothing is ever deleted unless somebody set a positive number of days **and** enabled the `PRUNE_AUDIT_LOGS` batch task, both of which ship off. So the trail is complete back to installation, the index grows without bound, and a wide search is expensive: narrow by action and by day, every time.

**Capping retention is not something you can do from the System Configuration tab on this build** — the keys are typed so that the tab refuses a number of days. What the properties are, what that refusal looks like, and who can make the change instead are all on [Audit actions and retention](/administration-guide/reference/audit-actions-and-retention.md#capping-retention-is-not-something-this-build-lets-you-do-from-a-screen). One consequence matters for searching rather than for configuring.

{% hint style="warning" %}
**The prune run is audited under the wrong action.** It records itself as `START_USER_VERIFICATION` — not `PRUNE_AUDIT_LOGS`, which is the action the catalogue and the batch task name imply. The count deleted is recorded correctly, in the `NUM_DELETED_AUDIT_LOGS` attribute, with source `WORKFLOW`. So after enabling pruning, look for `START_USER_VERIFICATION` events carrying `NUM_DELETED_AUDIT_LOGS`, and do not conclude from an absence of `PRUNE_AUDIT_LOGS` events that nothing was deleted. Verify against your build before writing a monitoring rule either way.
{% endhint %}

### Two more retention-related things in the `SystemAuditLog` category

The category holds **502** keys, of which 497 are the retention properties. The other five are residue and one live control.

`openiam.audit.log.purge.allowed` ships **`9`** — the super-administrator role id — with `PROPERTY_TYPE` `Role`. OpenIAM resolves the comma-separated roles on that key and decides, per requester, whether purge is permitted; the audit viewer receives the answer. **Nothing in this release acts on it.** There is no purge control on the audit log screen, at any privilege level, and scheduled pruning is the only deletion path. The check is the screen: open `/webconsole/viewAuditLog` as a member of role `9` and there is no purge button to find.

The four surviving `org.openiam.syslog.*` keys — `enable` (`false`), `export.child.rows` (`false`), `logopt` (`0`) and `priority` (`INFORMATIONAL`) — are the remains of the pre-connector syslog export. The six that carried its destination configuration (`actions`, `ident`, `facility`, `hostName`, `port`, `message.order`) were deleted by a later migration and are listed under [Properties removed by a later migration](/administration-guide/reference/system-properties.md#properties-removed-by-a-later-migration). Nothing in the product reads any of the ten. If a runbook tells you to enable syslog export by setting `org.openiam.syslog.enable`, it predates the audit export connector; use the connector instead.

And the obvious warning: deleted audit history is not recoverable, and pruning is the one maintenance operation on this page with no rollback. Confirm the retention obligation before you enable it, not after.

## Related

* [Audit actions and retention](/administration-guide/reference/audit-actions-and-retention.md) — the 537 action constants, and the 497 retention keys and what can actually be done with them
* [Audit export to a SIEM](/administration-guide/operations/audit-and-export.md) — streaming these events to an external system
* [Diagnose a failed operation](/administration-guide/operations/diagnose-a-failed-operation.md) — the audit trail as one of ten evidence layers
* [Which log answers which question](/administration-guide/operations/which-log-to-read.md) — when the failure was logged rather than audited
* [What a backup and restore must cover](/administration-guide/platform/backup-and-restore-scope.md) — why a database backup does not cover audit

*Applies to OpenIAM 2026.8.1. Last reviewed 7 August 2026.*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.openiam.com/administration-guide/operations/find-audit-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.
