Skip to Content
Evaluate Get Started Plugins Glossary

Approval Plugin Overview

Key takeaways

Use ApprovalPlugin to build a per-process approval timeline, a My Tasks list, and a Completed by me list. Register it with WithPlugin before StartAsync and use actor-restricted command transitions; inbox population requires at least one Direct transition for an identity by default. Application code reads IApprovalProvider, while workflow actions copy the same data into process parameters. Web API exposes stored approval history and inbox records, but not the derived outbox.

ApprovalPlugin connects WorkflowRuntime to approval persistence and provides three approval views: Approval history shows the timeline of a process, Inbox shows work waiting for each user, and Outbox shows work a user has already completed. Use it when the application must show who can approve a document now, what is waiting for a user, how a document was approved, or which documents a user has already handled.

ApprovalPlugin supplies data and workflow integration. The host application renders its own screens, sends notifications when needed, and calls the WorkflowRuntime command APIs when a user selects a command.

See how approval views change

Consider a document scheme with three states: Request, Manager signed, and Boss signed. The first command is available to the manager or boss, and the second is available only to the boss. ApprovalPlugin changes its application views as the document advances.

Approval process from Request through Manager signed to Boss signed

Process stageApproval historyInboxOutbox
RequestTwo predicted stages; the first allows Manager and Boss, and the second allows BossManager and Boss can execute the first ApproveEmpty
Manager signedCompleted manager stage and pending boss stageBoss can execute ApproveManager has one process summary
Boss signedCompleted manager and boss stagesEmpty when no command remainsManager and boss each have a process summary

After a user executes Approve, Approval history records the completed stage, Outbox includes the process in that user's completed work, and Inbox is recalculated for the next stage.

Register ApprovalPlugin

Register ApprovalPlugin after the base runtime configuration and before StartAsync(). Set registration-sensitive members such as AutoApprovalHistory before calling WithPlugin, because OnPluginAdd decides whether to install the automatic build step.

ApprovalPluginRegistration.cs
using OptimaJet.Workflow.Core.Runtime;using OptimaJet.Workflow.Plugins;public static class ApprovalPluginRegistration{    public static WorkflowRuntime AddApprovalPlugin(this WorkflowRuntime runtime)    {        var plugin = new ApprovalPlugin        {            AutoApprovalHistory = true,            DisableApprovalHistory = false,            DisableInbox = false,            InboxCheckConditions = false,            UseLocalizedNamesInApprovalHistory = true        };        return runtime.WithPlugin(            plugin,            ["DocumentApproval"]        );    }}

The optional scheme list scopes ApprovalPlugin actions and Designer formats because WithPlugin passes the list to both providers. The plugin also uses the list to restrict its runtime handler and automatic history build step. Omit the list or pass an empty list to register those capabilities globally.

Before testing the result, verify the workflow scheme supplies the data ApprovalPlugin needs. When automatic history is enabled, its build step adds FillApprovalHistory to activities with a non-blank State in the schemes covered by the plugin. An inbox row requires an outgoing command transition with actor restrictions that resolve the identity, and at least one resolved transition for that identity must have the Direct classifier unless InboxActorsWithOnlyRejectCommands is enabled.

Known limitation - custom activity scope: WithPlugin adds generated custom activity types without a scheme filter, so they remain available in every scheme even when the registration call supplies a scheme list. Use the registration list, rather than activity visibility in the Designer, to determine which schemes the plugin handles.

Understand plugin capabilities

ApprovalPlugin is included in WorkflowEngine.NETCore-Core under the OptimaJet.Workflow.Plugins namespace. The plugin registers four asynchronous actions, Designer parameter formats, a runtime process handler, and an optional build step. Its base provider also creates Designer custom activity types for every action that is not excluded when WithPlugin runs. With the default AutoApprovalHistory value, these are GetApprovalHistory, GetInbox, and GetOutbox; setting the field to false before registration also adds FillApprovalHistory. The plugin registers no synchronous actions, conditions, rules, or custom condition forms.

ApprovalPlugin capabilities
CapabilityRole
FillApprovalHistory actionWrites a pending or completed item that the provider inserts or applies to a pending match
GetApprovalHistory actionReads history by process or identity and writes the list to a process parameter
GetInbox actionReads inbox entries by process or identity and writes the list to a process parameter
GetOutbox actionReads process summaries for one identity and writes the list to a process parameter
Custom activity typesProvide action wrappers on the Activities tab of the Designer Elements panel
Runtime process handlerDeletes stale pending history, runs pre-execution, and rebuilds inbox rows before the root process becomes idled or finalized
AutoApprovalHistoryBuilderStepAdds FillApprovalHistory to state activities when automatic history is enabled

ApprovalPlugin uses IApprovalProvider through WorkflowRuntime.PersistenceProvider. IPersistenceProvider extends IApprovalProvider, so every supported persistence provider supplies this contract.

Actor rules that represent roles or teams must resolve to workflow identity IDs. ApprovalPlugin persists inbox work for each resolved identity instead of storing one shared role or team task.

Configure ApprovalPlugin

ApprovalPlugin configuration members control history generation, inbox selection, names, and comments. Configure the plugin instance before registration because OnPluginAdd captures the automatic build-step decision; most other members are read when their handler or action runs.

ApprovalPlugin configuration members
MemberKind and typeDefaultWhen read and effect
AutoApprovalHistoryField, booltrueAt registration, excludes the FillApprovalHistory custom activity type and installs the build step when history is enabled; its current value also hides the action from ordinary Designer action lists
DisableApprovalHistoryProperty, boolfalseAt registration, prevents build-step installation; on each runtime-handler call, skips pending-row cleanup and history pre-execution; it does not block a FillApprovalHistory action already in a scheme
DisableInboxProperty, boolfalseOn each runtime-handler call, skips inbox deletion and rebuilding; existing inbox records remain
InboxCheckConditionsField, boolfalseDuring each inbox refresh, evaluates transition conditions before resolving actors and commands
InboxActorsWithOnlyRejectCommandsField, boolfalseDuring each inbox refresh, keeps an empty-command row for an identity that has no Direct transition
AllowedActorsFilterProperty, Func<string, bool>nullDuring each inbox refresh, filters Allow restrictions; Restrict restrictions are unaffected
GetUserNamesByIdsField, Func<List<string>, List<string>>nullDuring history pre-execution, maps distinct predicted identity IDs to values stored in AllowedTo
NameParameterForCommentField, string"Comment"During completed history execution, names the process parameter copied to Commentary
UseLocalizedNamesInApprovalHistoryProperty, booltrueWhen FillApprovalHistory writes a record, resolves localized state and command names with technical-name fallbacks

Set AutoApprovalHistory and DisableApprovalHistory before WithPlugin. Changing either member later does not remove a build step that was already registered or remove FillApprovalHistory from a scheme that was already built. A later AutoApprovalHistory change affects ordinary action discovery, but it does not regenerate the custom activity types already added to the runtime.

When GetUserNamesByIds is configured, ApprovalPlugin stores the list returned by that delegate. When it is null, ApprovalPlugin calls WorkflowRuntime.GetUserByIdentityAsync for each distinct identity if that runtime delegate is configured. If both delegates are null, the plugin stores the identity IDs unchanged in AllowedTo.

Apply automatic history changes

Use this deployment sequence when automatic history changes for schemes that already have process instances:

  1. Create new runtime instances with the required settings applied before WithPlugin, and deploy the same configuration to every runtime node. Changing a field on a runtime that already registered the build step does not unregister that step.
  2. After the new runtimes start, call SetSchemeIsObsoleteAsync(schemeCode, tenantId) for each affected scheme. The next scheme creation or update rebuilds the generated process scheme with the current build-step pipeline.
  3. Existing process instances remain bound to their old generated scheme until UpdateSchemeIfObsoleteAsync(processId) migrates them. The current activity must allow automatic scheme updates unless the overload with ignoreAutoSchemeUpdate: true is used.

Rebuilding changes future behavior only. It does not backfill approval stages that were never recorded or delete existing WorkflowApprovalHistory rows. A FillApprovalHistory action saved explicitly in the source scheme also remains after automatic injection is disabled.

What Disabled controls

ApprovalPlugin.Disabled affects action and Designer-format discovery. When it is true during registration, no generated custom activity types are added; setting it later hides existing types from Designer discovery but does not remove them from the runtime. Disabled does not prevent WithPlugin from registering the runtime process handler or automatic build step, so it is not a master switch for approval history and inbox maintenance.

Use ApprovalPlugin actions

ApprovalPlugin actions run inside workflow schemes. The three query actions read the persistence provider and save their result to the process parameter named by ParameterName; they do not return the list to application code.

ApprovalPlugin actions
ActionParametersStored result
FillApprovalHistoryNoneAdds or updates WorkflowApprovalHistory directly
GetApprovalHistoryQuery selector, output parameter, optional pagingList<ApprovalHistoryItem>
GetInboxQuery selector, output parameter, optional pagingList<InboxItem>
GetOutboxIdentity, output parameter, optional pagingList<OutboxItem>

GetApprovalHistory and GetInbox use the same parameter structure.

GetApprovalHistory and GetInbox parameters
ParameterTypeRequiredDefaultDescription
GetParameterTypeGetParameterTypeYes-Selects ProcessId or IdentityId
GetParameterValuestringYes-Supplies the process GUID or identity according to the selector
ParameterPurposeParameterPurposeYes in DesignerPersistenceControls how Workflow Engine stores the output parameter
ParameterNamestringYes-Names the process parameter that receives the result list
PageIndexintNo-1Uses a 1-based page index; values below 1 disable paging
PageSizeintNo-1Enables paging only when both page values are greater than zero

When an action omits PageIndex or PageSize, the handler checks inherited PluginSettings for a value with the same name before using -1.

GetOutbox accepts one identity instead of a ProcessId or IdentityId selector.

GetOutbox parameters
ParameterTypeRequiredDefaultDescription
IdentityId in DesignerstringYes-Selects the identity whose history is grouped into outbox summaries
ParameterPurposeParameterPurposeYes in DesignerPersistenceControls how Workflow Engine stores the output parameter
ParameterNamestringYes in Designer"" in the handlerNames the process parameter that receives the result list
PageIndexintNo-1Uses a 1-based page index; values below 1 disable paging
PageSizeintNo-1Enables paging only when both page values are greater than zero

Understand runtime integration

ApprovalPlugin combines a build-time action injection with runtime activity handling. Automatic history adds FillApprovalHistory to the pre-execution and execution implementations of eligible activities that have a non-blank state. The runtime handler then removes obsolete pending rows, starts pre-execution from the current activity, and rebuilds inbox entries each time root process execution is about to become idled or finalized.

The pre-execution pass used while approval history is enabled loads persisted parameters, resolves actor restrictions through the rule provider, evaluates transition conditions unless ResultOnPreExecution supplies a fixed result, and runs actions from PreExecutionImplementation. Keeping approval history enabled can therefore call custom rules, conditions, and pre-execution actions before a user executes the next command. Temporary values from the preceding request are not reloaded, so persist data required by those callbacks or handle its absence.

Custom callbacks must be safe to run during simulation and more than once. Check processInstance.IsPreExecution before sending notifications, writing to an external database, or calling an API that changes external state. For a condition with unsafe or expensive evaluation, set ResultOnPreExecution to the result that simulation should use. The simulated activity path is not committed, but external effects produced by custom code are not rolled back.

flowchart TB
    subgraph prediction[Build and predict]
        direction LR
        A[Build process scheme] --> B[Inject FillApprovalHistory]
        B --> C[Create pending history]
    end
    subgraph completion[Execute and refresh]
        direction LR
        D[Execute command, timer, or SetState with execution]
        D --> E[Update or insert completed history]
        E --> F[Refresh pending history and inbox]
    end
    prediction --> completion

SetStateAsync with SetStateParams.PreventExecution = true changes the current activity without running its implementation or invoking this runtime handler. Approval history and inbox rows are therefore not refreshed by that operation and can continue to describe the previous activity until later workflow execution invokes the handler.

The runtime handler skips its refresh cycle when both history and inbox are disabled. It also skips refreshes for a subprocess instance; actor resolution for a root process can still inspect that process and its descendants. FillApprovalHistory itself has no subprocess guard, so a build-injected or explicitly configured action can still write history while a subprocess executes.

Understand persistence requirements

ApprovalPlugin persists its primary records in two provider-managed tables or MongoDB collections. WorkflowInbox contains current per-identity work, and WorkflowApprovalHistory contains pending and completed stages. Outbox queries group completed history by identity and process, so no separate outbox store exists.

The plugin also persists WorkflowPluginApprovalHistoryOrder as an internal process parameter to assign the next Sort value. Query actions write their results to the output parameter requested by the scheme, but the inbox and history themselves are not serialized into process parameters.

Delete a process and approval data

The persistence provider removes ApprovalPlugin records during normal process deletion. Both DeleteProcessAsync(Guid) and DeleteProcessAsync(Guid[]) delete the matching WorkflowInbox and WorkflowApprovalHistory records as part of process deletion. Export approval history before deleting the process when the application must retain an audit record after the process is removed.

The approval history DELETE operations and inbox DELETE operations in the Workflow Engine HTTP API have a narrower scope. They delete selected projection records without deleting the process or running ApprovalPlugin. A later inbox refresh can recreate deleted inbox records, while deleted completed history is not backfilled automatically.

Use provider mutation methods

IApprovalProvider is the persistence contract through which ApprovalPlugin and application code read and modify Inbox and Approval history data and read derived Outbox summaries. It exposes mutation methods because ApprovalPlugin and administrative application code share this contract. ApprovalPlugin owns the normal inbox replacement and pending-history lifecycle; direct calls bypass that lifecycle and should be reserved for controlled repair, cleanup, or import operations.

MethodApprovalPlugin use
DropWorkflowInboxAsyncRemoves the current process inbox before the plugin inserts a refreshed snapshot
InsertInboxAsyncInserts the identities and available commands calculated during inbox refresh
FillApprovalHistoryAsyncInserts a history item or updates a matching pending item
DropEmptyApprovalHistoryAsyncRemoves pending predictions before ApprovalPlugin runs pre-execution again
DropApprovalHistoryByProcessIdAsyncRemoves all approval history for one process
DropApprovalHistoryByIdentityIdAsyncRemoves every history row whose IdentityId matches; ApprovalPlugin normally assigns this value only to completed rows

Calling an inbox delete without the matching insert leaves the worklist empty until a later refresh. Calling a history delete changes both the approval timeline and any derived outbox summaries. The provider methods do not execute a workflow transition or re-evaluate actor rules on their own.

See also

Frequently asked questions

How do I apply an AutoApprovalHistory change to existing processes?

Register a fresh ApprovalPlugin instance with the new setting, mark each affected scheme obsolete with SetSchemeIsObsoleteAsync, and migrate running instances with UpdateSchemeIfObsoleteAsync. Existing history is neither backfilled nor deleted by that migration.

When must I set AutoApprovalHistory?

Set AutoApprovalHistory before registering ApprovalPlugin. OnPluginAdd reads the field to decide whether to add AutoApprovalHistoryBuilderStep to the runtime builder.

Why can ApprovalPlugin call my rules or conditions before the next command?

ApprovalPlugin calls pre-execution before a root process becomes idled or finalized whenever approval history is enabled. Pre-execution resolves actor rules and may evaluate transition conditions; use ProcessInstance.IsPreExecution and ResultOnPreExecution to keep custom code safe during that simulation.

Can I enable ApprovalPlugin for selected schemes?

Yes. Pass a List<string> of scheme codes as the second argument to runtime.WithPlugin. The list scopes ApprovalPlugin actions, Designer formats, its runtime handler, and the automatic history build step.

Does ApprovalPlugin store inbox and history in process parameters?

No. The persistence provider stores inbox and history records. ApprovalPlugin uses one internal process parameter for history ordering, and its query actions can write result lists to parameters selected by the scheme.