Skip to main content

Building an Audit Stream

Target audience: Developers & Modelers


This how-to introduces you to the audit trail of a particular work item like a case or a process and how to add entries to this trail with the use of the 'Audit' activity, or the Java builder API as well as how you can make use of the audit engine capabilities to build any arbitrary audit stream of your own.

The Audit Instance

The audit stream is created using audit instances (you might also call them audit entries) being added for whatever reason. Those audit entries are later queried for using some filtering aspects like the scope or type or even user depending on the needs and angles to look into auditing.

In order to understand auditing better, we need to be aware of some concepts within the audit instance and the retrieval of them first.

Scoping and Permissions

An audit instance should always be scoped to a context it belongs to. Although the builder API does not explicitly enforce it to be set, it is a good practice to always include a scope to an audit entry. It might even be something like scope type global and any scope ID you like but scoping is usually the key for permissions and filtering, so a very important concept.

There are a couple of predefined scope types you can create audit entries for like cmmn or bpmn for the scope of a case or process, respectively. More scope types include task, conversation, user or user-account.

It does not stop there, you can add your own scope types as well as client or claim or whatever you need to build a stream for later on. Another example might be to save audit entries whenever some important settings are changed like setting some thresholds or escalation settings, changing an SLA and the like. You might want to use scope type settings there, and the scope ID would point to the exact setting which was changed.

The scope type and scope ID are always used together. The scope type defines the type of object or context the audit instance is bound to, and the scope ID references the exact object. As an example, you might use scope type cmmn and the scope ID contains the case id to bind an audit entry to a particular case instance.

The scope is typically used for permission handling as well when retrieving audit entries. The permission to see the audit entry is derived from its scope by default. If you created an audit instance scoped to a case instance, you need to have access to the case itself to also gain access to the audit entries scoped with it.

When you use the default audit REST endpoint to filter audit entries, there are only a few permission handlers yet available to make sure you only get the entries you are allowed to see, furthermore, the scope always needs to be present for a query, so it is meant to be used for scoped audit streams only using the built-in handlers like for cases, processes and tasks as an example.

When you build your own custom scopes or want to create an audit stream within another context than a single scope, you need to create your own REST endpoint for that by using the Java query API internally.


If you create your own REST endpoint to query for audit entries, always think about the permissions of the audit entries! Who is able to see what entry? What permissions do I need to check when querying for audit instances? Otherwise, you might leak classified information to be seen by users not entitled to see such information.

The sub scope ID is optional and further specifies the context of the audit instance. As an example you might use cmmn as the scope type and the case id as the scope ID and use a task id as the sub scope ID to bind the audit entry to the case as its main scope, but also point out that it was created in the context of a particular task within the case.

You can optionally also set the scope definition ID to the scoped object model for instance. This way you might later filter for all entries created within a specific type of case model as an example or a particular approval process model.

Type and Sub Type

Beyond the scope you can define the type and sub type for an audit entry. Most likely it is used for later filtering so be careful and think about the requirements for the audit stream you need to query and render to set appropriate type information.

Using an example for approvals, building just an approval audit stream in a dashboard where you want to see everything around approvals, no matter the case or user, just approvals. Then you could set the type to approval and use approved or declined as subtypes for instance.


Although the type and subType are custom fields for an audit entry, there are some predefined ones used in the audit view on a case or process like create or complete where if known, another icon is rendered for the entry according the type.


The payload is optional and might contain additional information for the audit entry. You can add an arbitrary number of additional data to the payload, there is no limitation, just keep the overall data you produce through those audit entries in mind.

There are some predefined keys for the payload in order to be rendered in the default audit trail UI of a case or process:

  • message: the string added to the payload using this key is rendered as the content of the audit entry. It might as well be set in Design as an expression, as an example, ${findUser(authenticatedUserId).displayName} has approved the request '${}' with comment: ${comment}. would evaluate that string before it is set as the message in the payload of an audit entry.
  • category: use system or user to set the category for the audit entry to be rendered accordingly in the default audit UI.

The Audit Service Task

See Audit Task for a description of the attributes associated with an audit task for BPMN process models.

We want to explore the options with the audit service task within a case and see its entry in the default audit trail UI of the case instance at runtime.
Simply create a new case model and add two audit tasks to it: audit-stream-case-model

Here are the sample properties for both audit tasks: audit-stream-audit-tasks

Let's look at them in more detail:

Creator idIf left empty (like we did), the currently authenticated user is taken by default. But you can also enter an expression resolving to a user id, if you want the entry being created in the context of someone else. That might make sense if you run stuff asynchronously and want to make sure the correct user is entered here as an example.
External idThis one is completely custom, so you can use it with whatever you want. It might point to an external important data object being changed, or any contextual reference or information you might want to use or filter the entries with. As we will see later, filtering is key when we talk about building an audit stream.
TypeThe type of the entry is custom too, so you can use the type and sub type as you want. When defining type / sub type ask yourself what you want to filter for later on. The main filtering properties are indeed type and sub type, if you create an audit stream outside of any particular scope like a case or process.
Sub typeSee comments on the type. The sub type just gives more details about the type. As an example, you might want to use approval as the type, then the sub type further specifies the entry like approved or declined or timed outor delegated just to mention some ideas.
Scope IDScoping is the most important aspect of an audit entry, see the previous chapters for a more detailed explanation about scoping. The scope ID contains the id of the context for the audit entry, in our case we use the case instance id for it.
Sub scope IDThe sub scope is optional and might further specify the scope / context for the entry. We could use a task id in here or whatever more specific scope than our main one.
Scope typeThe type needs to define the object we reference using the scope ID, so scope ID and scope type identify the context for the audit entry and usually defines the permissions too for the audit, if you have access to that object, you might also have access to its audit entries.
Scope definition IDThe optional definition id or model key of the scoped object which can be used for filtering later on as well. As an example, you might want to show all entries in the context of a particular case or process model.
PayloadThe payload is optional and might contain additional information. The message and category are predefined keywords used in the standard audit UI as described before.
Payload / categoryDefault values for the category are user and system but you can use any custom one of course as well.
Payload / messageThe message used in the default audit UI, might even contain an expression to be resolved when creating the audit entry.

If you deploy the sample app and run such a case, this is how the audit entries get rendered in the default audit trail UI: